图像相减操作对于入门的小伙伴来说很简单原理如halcon给的算子解释手册一样:
其中 g1和g2是输入图片对应的像素,mult则是比例系数
对于halcon来说,实现图像相减使用的算子就是 abs_diff_image
对于opencv来说可以自己实现访问像素也可以用函数实现:
#include "cv.h"
#include "highgui.h"
#include "cxcore.h"
int main(int argc,char** argv)
{cvNamedWindow("a",0);
IplImage* img=cvLoadImage("11.jpg");
cvShowImage("a",img);
cvNamedWindow("b",0);
IplImage* img1=cvLoadImage("12.jpg");
cvShowImage("b",img1);
IplImage* diff=cvCreateImage(cvGetSize(img),img->depth,img->nChannels);
cvAbsDiff(img,img1,diff);
cvNamedWindow("r",0);
cvShowImage("r",diff);
while (1)
{if (cvWaitKey(100)==27) break;
}
cvDestroyWindow("a");
cvDestroyWindow("b");
cvReleaseImage(&img);
cvReleaseImage(&img1);
cvReleaseImage(&diff);
return 0;
}
最近在研究图像的底层实现c++的图像实现如下:
void diffImage(unsigned char* src, unsigned char* src1, uint32_t nW, uint32_t nH, uint32_t k, unsigned char* dst)
{
int diff_value;
//-------开始像素遍历----------------
for (int i = 0; i < nH ; i++)
{
for (int j = 0; j < nW ; j++)
{
diff_value = abs(src1[i*nW + j] - src[i*nW + j])*k;
if (diff_value > 255)
diff_value = 255;
dst[i*nW + j] = diff_value;
}
}
}
重点来了,sse加速版本的操作,循环跑了1000次,2000w像素的图像,6.8ms,测得比halcon(10ms以上)快。(这里有个骚操作)
void diffImage_SSE(unsigned char* src, unsigned char* src1, uint32_t Width, uint32_t Height, uint32_t channle, uint32_t k, unsigned char* dst)
{
__m128i mult = _mm_set1_epi16(k);
__m128i Zero = _mm_set1_epi8(0);
__m128i m127 = _mm_set1_epi8(127);
int BlockSize = 16; //一次来十六个
int Block = Width*Height*channle/BlockSize;
for (int i = 0; i < BlockSize*Block; i += BlockSize)
{
if (i == 1408)
{
int k = 0;
}
//先load进两张图片的数据
__m128i inputdataOne = _mm_loadu_si128((__m128i*)(src+i));
__m128i inputdataTwo = _mm_loadu_si128((__m128i*)(src1 + i));
//做无符号的减法先(a-b)然后(b-a)然后相加,其实就是做了abs(a-b)操作
__m128i imagedataDst1 =_mm_subs_epu8(inputdataOne, inputdataTwo);
__m128i imagedataDst2 = _mm_subs_epu8(inputdataTwo, inputdataOne);
__m128i imagedataDst = _mm_adds_epu8(imagedataDst1, imagedataDst2);
//unpack高低位
__m128i highDst = _mm_unpackhi_epi8(imagedataDst, Zero);
__m128i lowhDst = _mm_unpacklo_epi8(imagedataDst, Zero);
//做完乘法取低16位就可以
__m128i highDst1 = _mm_mullo_epi16(highDst, mult);
__m128i lowhDst1 = _mm_mullo_epi16(lowhDst, mult);
//用pack将高低位链接,注意第一个参数个低位第二个参数是高位
__m128i imagedata = _mm_packs_epi16(lowhDst1, highDst1);
_mm_storeu_si128((__m128i*)(dst + i), imagedataDst);
}
for (int j = BlockSize*Block; j < Width*Height*channle; j++)
{
dst[j] = IM_ClampToByte((src[j] - src1[j])*k);
}
}
inline unsigned char IM_ClampToByte(int Value)
{
if (Value < 0)
return 0;
else if (Value > 255)
return 255;
else
return (unsigned char)Value;
}