【OpenCV】图像/视频相似度测量PSNR( Peak signal-to-noise ratio) and SSIM,视频/图片转换
目录
1 目标
2 原理
2.1 图像比较 - PSNR and SSIM¶
3 代码
3.1如何读取一个视频流(摄像头或者视频文件)?¶
3 运行效果
视频/图片转换:
- 如何用OpenCV创建一个视频文件
- 用OpenCV能创建什么样的视频文件
- 如何释放视频文件当中的某个颜色通道
为了使例子简单,我就仅仅释放原始视频RGB通道中的一个,并把它放入新视频文件中。你可以使用命令行参数来控制程序的一些行为:
- 第一个参数指向你需要操作的视频文件。
- 第二个参数可以是如下的几个字母之一:R G B。用来指定你需要释放哪一个通道。
- 最后一个参数是Y(Yes)或者N(No). 如果你选择N, 就直接使用视频输入格式来创建输出文件,否则就会弹出一个对话框来让你选择编码器。
1 目标
如何打开和读取视频流
两种检查相似度的方法:PSNR和SSIM
数据链接:https://pan.baidu.com/s/1zqcFKmWViSF1O8QK2pVhMg 提取码:g5p9
2 原理
2.1 图像比较 - PSNR and SSIM¶
我们想检查压缩视频带来的细微差异的时候,就需要构建一个能够逐帧比较差视频差异的系统。最常用的比较算法是PSNR( Peak signal-to-noise ratio)。这是个使用“局部均值误差”来判断差异的最简单的方法,假设有这两幅图像:I1和I2,它们的行列数分别是i,j,有c个通道。
PSNR公式如下:
每个像素的每个通道的值占用一个字节,值域[0,255]。这里每个像素会有 个有效的最大值 注意当两幅图像的相同的话,MSE的值会变成0。这样会导致PSNR的公式会除以0而变得没有意义。所以我们需要单独的处理这样的特殊情况。此外由于像素的动态范围很广,在处理时会使用对数变换来缩小范围。这些变换的C++代码如下:
double getPSNR(const Mat& I1, const Mat& I2)
{Mat s1;absdiff(I1, I2, s1); // |I1 - I2|s1.convertTo(s1, CV_32F); // 不能在8位矩阵上做平方运算s1 = s1.mul(s1); // |I1 - I2|^2
Scalar s = sum(s1); // 叠加每个通道的元素
double sse = s.val[0] + s.val[1] + s.val[2]; // 叠加所有通道
if( sse <= 1e-10) // 如果值太小就直接等于0return 0;else{double mse =sse /(double)(I1.channels() * I1.total());double psnr = 10.0*log10((255*255)/mse);return psnr;}
}
在考察压缩后的视频时,这个值大约在30到50之间,数字越大则表明压缩质量越好。如果图像差异很明显,就可能会得到15甚至更低的值。**PSNR算法简单,检查的速度也很快。但是其呈现的差异值有时候和人的主观感受不成比例。所以有另外一种称作 结构相似性 的算法做出了这方面的改进。**
建议你阅读一些关于SSIM算法的文献来更好的理解算法,然而及时你直接看下面的源代码,应该也能建立一个不错的映像。请参考下面深度解析SSIM的文章:
”Z. Wang, A. C. Bovik, H. R. Sheikh and E. P. Simoncelli, “Image quality assessment: From error visibility to structural similarity,” IEEE Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004.”
Scalar getMSSIM( const Mat& i1, const Mat& i2)
{const double C1 = 6.5025, C2 = 58.5225;/***************************** INITS **********************************/int d = CV_32F;
Mat I1, I2;i1.convertTo(I1, d); // 不能在单字节像素上进行计算,范围不够。i2.convertTo(I2, d);
Mat I2_2 = I2.mul(I2); // I2^2Mat I1_2 = I1.mul(I1); // I1^2Mat I1_I2 = I1.mul(I2); // I1 * I2
/***********************初步计算 ******************************/
Mat mu1, mu2; //GaussianBlur(I1, mu1, Size(11, 11), 1.5);GaussianBlur(I2, mu2, Size(11, 11), 1.5);
Mat mu1_2 = mu1.mul(mu1);Mat mu2_2 = mu2.mul(mu2);Mat mu1_mu2 = mu1.mul(mu2);
Mat sigma1_2, sigma2_2, sigma12;
GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);sigma1_2 -= mu1_2;
GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);sigma2_2 -= mu2_2;
GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);sigma12 -= mu1_mu2;
/ 公式 Mat t1, t2, t3;
t1 = 2 * mu1_mu2 + C1;t2 = 2 * sigma12 + C2;t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
t1 = mu1_2 + mu2_2 + C1;t2 = sigma1_2 + sigma2_2 + C2;t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
Mat ssim_map;divide(t3, t1, ssim_map); // ssim_map = t3./t1;
Scalar mssim = mean( ssim_map ); // mssim = ssim_map的平均值return mssim;
}
这个操作会针对图像的每个通道返回一个相似度,取值范围应该在0到1之间,取值为1时代表完全符合。然而尽管SSIM能产生更优秀的数据,但是由于高斯模糊很花时间,所以在一个实时系统(每秒24帧)中,人们还是更多地采用PSNR算法。
正是这个原因,最开始的源码里,我们用PSNR算法去计算每一帧图像,而仅当PSNR算法计算出的结果低于输入值的时候,用SSIM算法去验证。为了展示数据,我们在例程里用两个窗口显示了原图像和测试图像并且在控制台上输出了PSNR和SSIM数据。就像4运行结果。
3 代码
3.1如何读取一个视频流(摄像头或者视频文件)?¶
总的来说,视频捕获需要的所有函数都集成在 VideoCapture C++ 类里面。虽然它底层依赖另一个FFmpeg开源库,但是它已经被集成在OpenCV里所以你不需要额外地关注它的具体实现方法。你只需要知道一个视频由一系列图像构成,我们用一个专业点儿的词汇来称呼这些构成视频的图像:“帧”(frame)。此外在视频文件里还有个参数叫做“帧率”(frame rate)的,用来表示两帧之间的间隔时间,帧率的单位是(帧/秒)。这个参数只和视频的播放速度有关,对于单独的一帧图像来说没有任何用途。
你需要先定义一个 VideoCapture 类的对象来打开和读取视频流。具体可以通过 constructor 或者通过 open 函数来完成。如果使用整型数当参数的话,就可以将这个对象绑定到一个摄像机,将系统指派的ID号当作参数传入即可。例如你可以传入0来打开第一个摄像机,传入1打开第二个摄像机,以此类推。如果使用字符串当参数,就会打开一个由这个字符串(文件名)指定的视频文件。例如在上面的例子里传入如下参数:
video/Megamind.avi video/Megamind_bug.avi //35 10
前两个参数传入了两个文件名,分别代表原始参考视频和测试视频。这里使用了相对地址,这也代表着系统会从软件的工作目录下的video子目录里寻找文件。然后程序将针对这些参数开始进行相似性检查
const string sourceReference = argv[1],sourceCompareWith = argv[2];
VideoCapture captRefrnc(sourceReference);
// 或者
VideoCapture captUndTst;
captUndTst.open(sourceCompareWith);
你可以用 isOpened 函数来检查视频是否成功打开与否:
if ( !captRefrnc.isOpened()){cout << "Could not open reference " << sourceReference << endl;return -1;}
当析构函数调用时,会自动关闭视频。如果你希望提前关闭的话,你可以调用 release 函数. 视频的每一帧都是一幅普通的图像。因为我们仅仅需要从 VideoCapture 对象里释放出每一帧图像并保存成 Mat 格式。因为视频流是连续的,所以你需要在每次调用 read 函数后及时保存图像或者直接使用重载的>>操作符。
Mat frameReference, frameUnderTest;
captRefrnc >> frameReference;
captUndTst.open(frameUnderTest);
如果视频帧无法捕获(例如当视频关闭或者完结的时候),上面的操作就会返回一个空的 Mat 对象。我们可以用下面的代码检查是否返回了空的图像:
if( frameReference.empty() || frameUnderTest.empty())
{// 退出程序
}
读取视频帧的时候也会自动进行解码操作。你可以通过调用 grab 和 retrieve 函数来显示地进行这两项操作。
视频通常拥有很多除了视频帧图像以外的信息,像是帧数之类,有些时候数据较短,有些时候用4个字节的字符串来表示。所以 get 函数返回一个double(8个字节)类型的数据来表示这些属性。然后你可以使用位操作符来操作这个返回值从而得到想要的整型数据等。这个函数有一个参数,代表着试图查询的属性ID。在下面的例子里我们会先获得食品的尺寸和帧数。
Size refS = Size((int) captRefrnc.get(CV_CAP_PROP_FRAME_WIDTH),(int) captRefrnc.get(CV_CAP_PROP_FRAME_HEIGHT)),
cout << "参考帧的分辨率: 宽度=" << refS.width << " 高度=" << refS.height<< " of nr#: " <<<< endl;
当你需要设置这些值的时候你可以调用 set 函数。函数的第一个参数是需要设置的属性ID,第二个参数是需要设定的值,如果返回true的话就表示成功设定,否则就是false。接下来的这个例子很好地展示了如何设置视频的时间位置或者帧数:
captRefrnc.set(CV_CAP_PROP_POS_MSEC, 1.2); // 跳转到视频1.2秒的位置
captRefrnc.set(CV_CAP_PROP_POS_FRAMES, 10); // 跳转到视频的第10帧
// 然后重新调用read来得到你刚刚设置的那一帧
你可以参考 get 和 set 函数的文档来得到更多信息。
//src = imread("C:\\Users\\guoqi\\Desktop\\ch7\\4.jpg", 1);
#include <iostream> // for standard I/O
#include <string> // for strings
#include <iomanip> // for controlling float print precision
#include <sstream> // string to number conversion
#include <opencv2/imgproc/imgproc.hpp> // Gaussian Blur
#include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
#include <opencv2/highgui/highgui.hpp> // OpenCV window I/O
using namespace std;
using namespace cv;
double getPSNR(const Mat& I1, const Mat& I2);
Scalar getMSSIM(const Mat& I1, const Mat& I2);
int main(int argc, char *argv[], char *window_name)
{//if (argc != 5)//{// cout << "Not enough parameters" << endl;// //return -1;//}stringstream conv;
/*const string sourceReference = argv[1], sourceCompareWith = argv[2];*/const string sourceReference = "C:\\Users\\guoqi\\Desktop\\ch7\\Megamind.avi";const string sourceCompareWith = "C:\\Users\\guoqi\\Desktop\\ch7\\Megamind_bugy.avi";
int psnrTriggerValue = 35;int delay = 10;//conv << argv[3] << endl << argv[4]; // put in the strings//conv >> psnrTriggerValue >> delay; // take out the numbers
char c;int frameNum = -1; // Frame counter
VideoCapture captRefrnc(sourceReference),captUndTst(sourceCompareWith);
if (!captRefrnc.isOpened()){cout << "Could not open reference " << sourceReference << endl;return -1;}
if (!captUndTst.isOpened()){cout << "Could not open case test " << sourceCompareWith << endl;return -1;}
Size refS = Size((int)captRefrnc.get(CV_CAP_PROP_FRAME_WIDTH),(int)captRefrnc.get(CV_CAP_PROP_FRAME_HEIGHT)),uTSi = Size((int)captUndTst.get(CV_CAP_PROP_FRAME_WIDTH),(int)captUndTst.get(CV_CAP_PROP_FRAME_HEIGHT));
if (refS != uTSi){cout << "Inputs have different size!!! Closing." << endl;return -1;}
const char* WIN_UT = "Under Test";const char* WIN_RF = "Reference";
// WindowsnamedWindow(WIN_RF, CV_WINDOW_AUTOSIZE);namedWindow(WIN_UT, CV_WINDOW_AUTOSIZE);cvMoveWindow(WIN_RF, 400, 0); //750, 2 (bernat =0)cvMoveWindow(WIN_UT, refS.width, 0); //1500, 2
cout << "Reference frame resolution: Width=" << refS.width << " Height=" << refS.height<< " of nr#: " << captRefrnc.get(CV_CAP_PROP_FRAME_COUNT) << endl;
cout << "PSNR trigger value " <<setiosflags(ios::fixed) << setprecision(3) << psnrTriggerValue << endl;
Mat frameReference, frameUnderTest;double psnrV;Scalar mssimV;
while (true) //Show the image captured in the window and repeat{captRefrnc >> frameReference;captUndTst >> frameUnderTest;
if (frameReference.empty() || frameUnderTest.empty()){cout << " < < < Game over! > > > ";break;}
++frameNum;cout << "Frame:" << frameNum << "# ";
/ PSNR psnrV = getPSNR(frameReference, frameUnderTest); //get PSNRcout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";
MSSIM /if (psnrV < psnrTriggerValue && psnrV){mssimV = getMSSIM(frameReference, frameUnderTest);
cout << " MSSIM: "<< " R " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[2] * 100 << "%"<< " G " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[1] * 100 << "%"<< " B " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[0] * 100 << "%";}
cout << endl;
// Show Image /imshow(WIN_RF, frameReference);imshow(WIN_UT, frameUnderTest);
c = cvWaitKey(delay);if (c == 27) break;}cv::waitKey();return 0;
}
double getPSNR(const Mat& I1, const Mat& I2)
{Mat s1;absdiff(I1, I2, s1); // |I1 - I2|s1.convertTo(s1, CV_32F); // cannot make a square on 8 bitss1 = s1.mul(s1); // |I1 - I2|^2
Scalar s = sum(s1); // sum elements per channel
double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
if (sse <= 1e-10) // for small values return zeroreturn 0;else{double mse = sse / (double)(I1.channels() * I1.total());double psnr = 10.0*log10((255 * 255) / mse);return psnr;}
}
Scalar getMSSIM(const Mat& i1, const Mat& i2)
{const double C1 = 6.5025, C2 = 58.5225;/***************************** INITS **********************************/int d = CV_32F;
Mat I1, I2;i1.convertTo(I1, d); // cannot calculate on one byte large valuesi2.convertTo(I2, d);
Mat I2_2 = I2.mul(I2); // I2^2Mat I1_2 = I1.mul(I1); // I1^2Mat I1_I2 = I1.mul(I2); // I1 * I2
/*************************** END INITS **********************************/
Mat mu1, mu2; // PRELIMINARY COMPUTINGGaussianBlur(I1, mu1, Size(11, 11), 1.5);GaussianBlur(I2, mu2, Size(11, 11), 1.5);
Mat mu1_2 = mu1.mul(mu1);Mat mu2_2 = mu2.mul(mu2);Mat mu1_mu2 = mu1.mul(mu2);
Mat sigma1_2, sigma2_2, sigma12;
GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);sigma1_2 -= mu1_2;
GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);sigma2_2 -= mu2_2;
GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);sigma12 -= mu1_mu2;
/ FORMULA Mat t1, t2, t3;
t1 = 2 * mu1_mu2 + C1;t2 = 2 * sigma12 + C2;t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
t1 = mu1_2 + mu2_2 + C1;t2 = sigma1_2 + sigma2_2 + C2;t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
Mat ssim_map;divide(t3, t1, ssim_map); // ssim_map = t3./t1;
Scalar mssim = mean(ssim_map); // mssim = average of ssim mapreturn mssim;
}
4 运行效果
相关文章:

struts2提交list
2019独角兽企业重金招聘Python工程师标准>>> Action: private List<User> users; jsp: <input type"text" name"users[0].name" value"aaa" /> <input type"text" name"users[1].name" value&q…

双阈值法的实现
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" int main( ) {// 图像读取及判断cv::Mat srcImage cv::imread("..\\images\\hand1.jpg");if( !srcImage.data ) return 1;// 灰度转换cv::Mat srcGray;cv::cvt…

设计模式 小记
观察者模式 1.被观察者是单例模式。 生成这模式 1.Director中对于Builder的引用不一定是Strong,根据情况也有可能是Copy。 2.Director 聚合 Builder, 所以Builder本身可以单独拿出来使用。 转载于:https://juejin.im/post/5ca8c24df265da3094116c18

【MATLAB】————matlab raw图转bmp实现
image_path [layer_3_list_folder,\,layer_3_list_name]; img_raw_path fopen(image_path,r);%%打开图像 img_raw fread(img_raw_path,[Width,Height],uint16);% uchar为无符号字符型 mg_raw uint8(img_raw);%%unit8表示无符号整数,范围0-255,u…

人工手动冷备不完全恢复介绍(purge表不完全恢复)
不完全恢复不完全恢复的基本类型:1)基于时间点 (until time): 使整个数据库恢复到过去的一个时间点前2)基于scn (until change): 使整个数据库恢复到过去的某个SCN前3)基于cancel (u…

半阈值法的实现
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> using namespace std; using namespace cv; int main( ) {// 读取源图像及判断cv::Mat srcImage cv::imread("..\\images\\hand1.jpg&q…

novaclient的api调用流程与开发
novaclient的api调用流程与开发 2015年07月05日 19:27:17 qiushanjushi 阅读数:3915 http://blog.csdn.net/tpiperatgod/article/details/18375387?utm_sourcetuicool 另一个地址:http://neuromancer.sinaapp.com/?p64 从nova client的入口查看 cat /u…

【C++】 保存内容到文件工具
1. c 输出到文件 // save mean distance between center and descriptorsstd::string filename configuration_.debug_output_path() "ref_max_mean_distance.txt";FILE* fp fopen(filename.c_str(), "w");try {if (fp nullptr) {return error::Failed…

你知道实习对你有多重要吗?
大学生就业一直是个永恒不变的话题,在过去几年中,每次临近毕业季,我们肯定会一次次的听到“史上最难就业季”之说。而每一年的数据也会不断突破前一年的数字。在国新办举行的新闻发布会上,人力资源社会保障部部长尹蔚民提到…

灰度直方图的实现
#include <opencv2\opencv.hpp> int main() {// 图像源获取及判断 cv::Mat Image, ImageGray;Image cv::imread("..\\images\\flower3.jpg"); if(Image.empty()) return -1;cv::imshow("Image",Image);// 转换为灰度图像cv::cvtColor(Image,Image…

Android笔记之ViewModel的使用示例
依赖 implementation android.arch.lifecycle:extensions:1.1.1 implementation com.squareup.retrofit2:retrofit:2.5.0 implementation com.squareup.retrofit2:converter-gson:2.5.0 implementation com.squareup.retrofit2:adapter-rxjava2:2.5.0 implementation io.reacti…

vscode配置记录
vscode配置记录 按照官网教程安装好vs:实际上只需要配置launch.json的”program“如下所示 "configurations": [{"name": "(gdb) Launch","type": "cppdbg","request": "launch","program":…

unity 2d 游戏优化之路 遇坑记录
情景说明: unity 出的Android包,在目前一些主流机型跑都没有问题,但是在 小米3 这种比较老的机器上跑,报如下错误 GLSL compilation failed, no infolog provided 起先,我们一直以为是在低端机器上某个特定的GLSL 在这…

自定义直方图实现
#include <opencv2/opencv.hpp> int main() {// 图像获取及判断cv::Mat srcImage cv::imread("..\\images\\flower3.jpg");if( !srcImage.data ) return 1;cv::imshow("srcImage",srcImage);// 灰度转换 cv::Mat srcGray;cv::cvtColor(srcImage,sr…

js数组指定位置添加删除
示例参考:http://www.w3school.com.cn/jsref/jsref_splice.asp转载于:https://www.cnblogs.com/CarryYou-lky/p/10669859.html

【C++】对象实例化/成员函数/成员变量的内存管理
文章目录1. 对象实例化的内存管理总结2.C成员函数在内存中的存储方式3.C类的实例化对象的大小之sizeof()实例一:实例二:实例三:实例四:实例五:实例六:实例七:实例八:实例九ÿ…

HTML form 标签的 enctype 属性
1. enctype 的定义和用法 enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。 默认地,表单数据会编码为 "application/x-www-form-urlencoded"。 就是说,在发送到服务器之前,所有字符都会进行编码(空格转…

灰度直方图均衡化实现
#include <opencv2/opencv.hpp> int main() {cv::Mat srcImage cv::imread("..\\images\\flower3.jpg");if( !srcImage.data ) return 1;cv::Mat srcGray;cv::cvtColor(srcImage, srcGray, CV_BGR2GRAY);cv::imshow("srcGray", srcGray);// 直方图均…

oracle汉化包下载地址
2019独角兽企业重金招聘Python工程师标准>>> https://www.allroundautomations.com/bodyplsqldevreg.html 转载于:https://my.oschina.net/u/3141521/blog/3034655

【C++】C/C++ 中 static 的用法全局变量与局部变量
C/C 中 static 的用法全局变量与局部变量 目录 1. 什么是static? 1.1 static 的引入 1.2 静态数据的存储 2. 在 C/C 中static的作用 2.1 总的来说 2.2 静态变量与普通变量 2.3 静态局部变量有以下特点: 实例 3. static 用法 3.1 在 C 中 3.2 静态类相关…

浅谈C/C++中的static和extern关键字
一.C语言中的static关键字 在C语言中,static可以用来修饰局部变量,全局变量以及函数。在不同的情况下static的作用不尽相同。 (1)修饰局部变量 一般情况下,对于局部变量是存放在栈区的,并且局部变量的生命周期在该语句块执行结束时…

彩色直方图均衡化实现
#include <opencv2/opencv.hpp> int main() {// 图像获取及验证cv::Mat srcImage cv::imread("..\\images\\flower3.jpg");if( !srcImage.data ) return 1;// 存储彩色直方图及图像通道向量cv::Mat colorHeqImage; std::vector<cv::Mat> BGR_plane; …

二、python小功能记录——监听鼠标事件
1.原文链接 #-*- coding:utf-8 -*- from pynput.mouse import Button, Controller## ## 控制鼠标 ## # 读鼠标坐标 mouse Controller() print(The current pointer position is {0}.format(mouse.position)) # 设置鼠标坐标 mouse.position (10, 20) print(No…

【Smart_Point】C/C++ 中智能指针
C11智能指针 目录 C11智能指针 1.1 C11智能指针介绍 1.2 为什么要使用智能指针 1.2.1 auto_ptr(C98的方案,C11已经抛弃)采用所有权模式。 1.2.2 unique_ptr 1.2.3 shared_ptr 1.2.4 weak_ptr 1.3 share_ptr和weak_ptr的核心实现 1.…

Linux 虚拟内存和物理内存的理解【转】
转自:http://www.cnblogs.com/dyllove98/archive/2013/06/12/3132940.html 首先,让我们看下虚拟内存: 第一层理解 1. 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构 2. 一个新进程建立的时候,…

直方图变换——查找
#include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> int main() {// 图像获取及验证cv::Mat srcImage cv::imread("..\\images\\flower3.jpg");if( !srcImage.data ) return 1;//…

【C++】C/C++ 中default/delete特性
C类的特殊成员函数及default/delete特性 本文内容侧重个人理解,深入理解其原理推荐https://www.geeksforgeeks.org 目录 目录 C类的特殊成员函数及default/delete特性 前言 1. 构造函数和拷贝构造函数 2. 拷贝赋值运算符 3. C11特性之default关键字(P237, P4…

Celery--任务调度利器
2019独角兽企业重金招聘Python工程师标准>>> Celery文档: http://docs.jinkan.org/docs/celery/getting-started/first-steps-with-celery.html 安装celery和celery-with-redis pip install Celery pip install celery-with-redis开始编写task.py # tasks.py import…

直方图变换——累计
#include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> int main() {// 图像获取及验证cv::Mat srcImage cv::imread("..\\images\\flower3.jpg");if( !srcImage.data ) return 1;//…

iOS开发经验总结,我的2019进阶之路!
4G改变了生活,抓住机会的人已经在这个社会有了立足之地,马上迎来5G的时代,你做好准备了吗!对于即将迎来的5G时代,无疑会是音视频的又一个高潮!那么作为程序员的我们,应该怎么样去迎接它呢~~ 改变…