总共分三步:压缩,灰度化,均值化,求哈希值。
1.压缩
void secondMethod(char* filename, char* savename) { //const char* filename2 = filename.c_str(); //const char* savename2 = savename.c_str(); //第一幅图像的参数 IplImage* img = NULL; //OpenCV图像数据结构指针 int width, height; //图像的宽和高 //char* filename = "E:\Pictures\Me\portrait.png"; //要打开图像的路径 img = cvLoadImage(filename, 1); //打开图像,这个过去其实也完成了图像的解码,图像的信息存在IplImage指针所指的数据结构中 uchar* data = (uchar*)(img->imageData); //声明指针指向图像的数据区 width = img->width; //图像的宽 height = img->height; //图像的高 IplImage* img2; //均值后的图像 //char* savename = "E:\Pictures\Me\portrait4.png"; //要存储图像的路径 img2 = cvCreateImage(cvSize(8, 8), img->depth, img->nChannels); //新建8X8的图像 //图像压缩,将任意图像压缩成8x8的图像,保存到img2中 中间变量 int img2data[3][8][8];//8x8列的数组 //其实应该是[8][8][3]还是[3][8][8] //不能设为uchar类型 int widthTime, heightTime; //平均每个像素需要合并的次数 widthTime = width/8; heightTime = height/8; std::cout<<"widthTime:"<<widthTime<<","<<"heightTime:"<<heightTime<<std::endl; uchar* data2 = (uchar*)(img2->imageData); //声明指针指向第二幅图像的数据区 //保存各个区域的颜色值总量,以求平均值,先归零 for(int row=0; row<img->height; row++) for(int cols=0; cols<img->width; cols++) { if(row/heightTime<8 && cols/widthTime<8) { img2data[0][row/heightTime][cols/widthTime] = 0; //第一幅图颜色的和保存在数组,先归零 img2data[1][row/heightTime][cols/widthTime] = 0; img2data[2][row/heightTime][cols/widthTime] = 0; data2[row/heightTime*img2->widthStep/sizeof(uchar)+cols/widthTime*img2->nChannels + 0] = 0; //第二幅图的颜色值归零,一定要是img2->widthStep img2->nChannels std::cout<<"row:"<<row/heightTime<<",cols:"<<cols/widthTime<<std::endl; data2[row/heightTime*img2->widthStep/sizeof(uchar)+cols/widthTime*img2->nChannels + 1] = 0; data2[row/heightTime*img2->widthStep/sizeof(uchar)+cols/widthTime*img2->nChannels + 2] = 0; } } //求和,保存在数组 for(int row=0; row<img->height; row++) //操作数据区,要注意OpenCV的RGB的存储顺序为GBR for(int cols=0; cols<img->width; cols++) { if(row/heightTime<8 && cols/widthTime<8) { img2data[0][row/heightTime][cols/widthTime] += data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 0]; img2data[1][row/heightTime][cols/widthTime] += data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 1]; img2data[2][row/heightTime][cols/widthTime] += data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 2]; } } //求平均值后,保存到第二幅图的数据区 for(int row=0; row<8; row++) //操作数据区,要注意OpenCV的RGB的存储顺序为GBR for(int cols=0; cols<8; cols++) { if(row/heightTime<8 && cols/widthTime<8) { data2[row*img2->widthStep/sizeof(uchar)+cols*img2->nChannels + 0] = img2data[0][row][cols]/heightTime/widthTime; //G /100.0 /heightTime/widthTime std::cout<<"0:"<<img2data[0][row][cols]<<std::endl; data2[row*img2->widthStep/sizeof(uchar)+cols*img2->nChannels + 1] = img2data[1][row][cols]/heightTime/widthTime; //B /100.0 /heightTime/widthTime data2[row*img2->widthStep/sizeof(uchar)+cols*img2->nChannels + 2] = img2data[2][row][cols]/heightTime/widthTime; //R /100.0 /heightTime/widthTime } } cvSaveImage(savename, img2); //存储图像 cvNamedWindow("show", 1); //创建窗口对象用于显示 cvShowImage("show", img2); //将图像显示在窗口上 cvWaitKey(0); cvReleaseImage(&img); //释放图像数据结构指针对象所指内容 cvReleaseImage(&img2); }
2. 灰值化
void gray(char* savename, char* graysavename)//第一个参数为压缩后的保存地址,第二个参数为灰度后的保存地址 { //第一幅图像的参数 IplImage* img = NULL; //OpenCV图像数据结构指针 int width, height; //图像的宽和高 img = cvLoadImage(savename, 1); //打开图像,这个过去其实也完成了图像的解码,图像的信息存在IplImage指针所指的数据结构中 uchar* data = (uchar*)(img->imageData); //声明指针指向图像的数据区 width = img->width; //图像的宽 height = img->height; //图像的高 IplImage* img2; //灰度化的图像 //char* savename = "E:\Pictures\Me\portrait4.png"; //要存储图像的路径 img2 = cvCreateImage(cvSize(8, 8), img->depth, 1); //新建灰度的图像 //中间变量 int img2data[1][8][8];//8x8列的数组 //其实应该是[8][8][3]还是[3][8][8] //不能设为uchar类型 int img2dataB[1][8][8], img2dataG[1][8][8], img2dataR[1][8][8]; uchar* data2 = (uchar*)(img2->imageData); //声明指针指向第二幅图像的数据区 //灰度化:公式:I = 0.3B + 0.59G + 0.11R //先将值保存到img2dataB[1][8][8], img2dataG[1][8][8], img2dataR[1][8][8] for(int row=0; row<height; row++) for(int cols=0; cols<width; cols++) { //G img2dataG[0][row][cols] = data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 0]; //第一幅图颜色的和保存在数组,先归零 //B img2dataB[0][row][cols] = data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 1]; //R img2dataR[0][row][cols] = data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 2]; img2data[0][row][cols] = 0.1140*img2dataB[0][row][cols] + 0.5870*img2dataG[0][row][cols] + 0.2989*img2dataR[0][row][cols]; data2[row*img2->widthStep/sizeof(uchar)+cols*img2->nChannels + 0] = img2data[0][row][cols]; //第二幅图的颜色值归零,一定要是img2->widthStep img2->nChannels /*std::cout<<"row:"<<row/heightTime<<",cols:"<<cols/widthTime<<std::endl; data2[row*img2->widthStep/sizeof(uchar)+cols*img2->nChannels + 1] = 0; data2[row*img2->widthStep/sizeof(uchar)+cols*img2->nChannels + 2] = 0;*/ } cvSaveImage(graysavename, img2); //存储图像 cvNamedWindow("show", 1); //创建窗口对象用于显示 cvShowImage("show", img2); //将图像显示在窗口上 cvWaitKey(0); cvReleaseImage(&img); //释放图像数据结构指针对象所指内容 cvReleaseImage(&img2); }
3. 均值化
void average(char* graysavename) { //第一幅图像的参数 IplImage* img = NULL; //OpenCV图像数据结构指针 int width, height; //图像的宽和高 img = cvLoadImage(graysavename, 1); //打开图像,这个过去其实也完成了图像的解码,图像的信息存在IplImage指针所指的数据结构中 uchar* data = (uchar*)(img->imageData); //声明指针指向图像的数据区 width = img->width; //图像的宽 height = img->height; //图像的高 //第二幅图像的参数:二值化 IplImage* img2 = NULL; img2 = cvCreateImage(cvSize(8, 8), img->depth, 1); uchar* data2 = (uchar*)(img2->imageData); //计算均值 double average = 0.0; for(int row=0; row<height; row++) //操作数据区,要注意OpenCV的RGB的存储顺序为GBR for(int cols=0; cols<width; cols++) { average += data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 0]; } average /= (height*width); //得到均值 std::cout<<"average:"<<average<<std::endl; //二值化 for(int row=0; row<height; row++) for(int cols=0; cols<width; cols++) { //对比该像素点的灰度值与平均值 uchar pixeldata = data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 0]; if(pixeldata>=average) { data2[row*img2->widthStep/sizeof(uchar)+cols*img2->nChannels + 0] = 255; } else { data2[row*img2->widthStep/sizeof(uchar)+cols*img2->nChannels + 0] = 0; } } cvSaveImage("E:\Pictures\Me\portrait6.png", img2); //存储图像 cvNamedWindow("show", 1); //创建窗口对象用于显示 cvShowImage("show", img2); //将图像显示在窗口上 cvWaitKey(0); cvReleaseImage(&img); //释放图像数据结构指针对象所指内容 cvReleaseImage(&img2); }
4. 求哈希值
void getHash(char* twovaluefilename) { //第一幅图像的参数 IplImage* img = NULL; //OpenCV图像数据结构指针 int width, height; //图像的宽和高 img = cvLoadImage(twovaluefilename, 1); //打开图像,这个过去其实也完成了图像的解码,图像的信息存在IplImage指针所指的数据结构中 uchar* data = (uchar*)(img->imageData); //声明指针指向图像的数据区 width = img->width; //图像的宽 height = img->height; //图像的高 long int value1 = 0x00000000; //保存位的值,由于C++不允许以二进制保存,所以用的0x十六进制保存的,总共4*8=32位 long int value2 = 0x00000000; //value1为前32位,value2为后32位 std::cout<<"保存值之前:"<<std::bitset<32>(value1)<<std::bitset<32>(value2)<<std::endl; //计算图像的哈希值 for(int row=0; row<height/2; row++) //前半部分 for(int cols=0; cols<width; cols++) { //对比该像素点的灰度值与平均值 uchar pixeldata = data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 0]; if(pixeldata>=1) //白色 { value1 |= (1<<(32 - (row*8+cols) -1)); } else { //无 } } for(int row=height/2; row<height; row++) //后半部分 for(int cols=0; cols<width; cols++) { //对比该像素点的灰度值与平均值 uchar pixeldata = data[row*img->widthStep/sizeof(uchar)+cols*img->nChannels + 0]; if(pixeldata>=1) //白色 { value2 |= (1<<(32 - (row*8+cols) -1)); } else { //无 } } std::cout<<"保存值之后:"<<std::bitset<32>(value1)<<std::bitset<32>(value2)<<std::endl;//以二进制的形式输出 std::cout<<"十六进制结果值:"<<std::hex<<value1<<value2<<std::endl; //以十六进制的形式输出:比如abcdef //将十六进制数保存到char类型里 char char1[17]; hextochar(value1, value2, char1); //十六进制数转char std::string str1 = char1; //将char数组char1保存到string变量str1中,用于文件名 std::cout<<"字符串数组:"<<str1<<std::endl; //输出哈希值 }
结果:
原图:
压缩:
灰值化:
均值化:
哈希值:
这时候,比较两个图片的相似性,就是先计算这两张图片的hash指纹,也就是64位0或1值,然后计算不同位的个数(汉明距离)。如果这个值为0,则表示这两张图片非常相似,如果汉明距离小于5,则表示有些不同,但比较相近,如果汉明距离大于10则表明完全不同的图片。(该段转自:原文。)