图像差分的方法
差分圖像的幾個方法
2017/7/25
 
在處理圖像的時候,特別是處理視頻流圖像的時候,往往會用到圖像差分的方法。顧名思義,圖像差分,就是把兩幅圖像的對應(yīng)像素值相減,以削弱圖像的相似部分,突出顯示圖像的變化部分。例如,差分圖像往往能夠檢測出運動目標(biāo)的輪廓,能夠提取出閃爍導(dǎo)管的軌跡等等。
?
???????? 那么,該如何對圖像進行差分呢,或者說輸入一個視頻流,如何進行差分處理呢。
???????? 懂點OpenCV的同學(xué)可能就要說了,opencv里不是有現(xiàn)成的函數(shù)實現(xiàn)差分的嗎,直接拿來用就可以了,廢話那么多作甚。沒錯,OpenCV里跟差分相關(guān)的函數(shù)有兩個,一個是cvSub()函數(shù),一個是cvAbsDiff()函數(shù)。先來看看這兩個函數(shù)的參數(shù)。
?
void cvSub(const CvArr* src1, const CvArr* src2, CvArr* dst,const CvArr* mask=NULL);
?
兩個輸入圖像src1和src2和一個輸出圖像dst具有相同的類型和大小。cvSub適用于IplImage以及cvMat兩種結(jié)構(gòu)。一個簡單的例子如下:
#include <opencv2/opencv.hpp>
?
int main()
{
??? IplImage*src1 = cvLoadImage("E:\\testvideo\\test1.png");
??? IplImage*src2 = cvLoadImage("E:\\testvideo\\test2.png");
??? assert(src1);
??? IplImage*dst = cvCreateImage(cvGetSize(src1),src1->depth,src1->nChannels);
??? cvSub(src1,src2,dst);
??? cvShowImage("1",src1);
??? cvShowImage("2",src2);
??? cvShowImage("dst",dst);
??? cvWaitKey(0);
}
 
 
執(zhí)行結(jié)果:
 
如果將代碼cvSub(src1,src2,dst);改為cvSub(src2,src1,dst);則差分結(jié)果為:
這說明,cvSub()函數(shù)是直接將兩者的像素值相減,差值小于零的歸一到零處理,而沒有取差的絕對值。同時,也說明了cvSub()不僅支持灰度圖像,也支持三通道圖像。
?
而cvAbsDiff()函數(shù)計算了兩幅圖像中差的絕對值。其參數(shù)跟cvSub()函數(shù)類似,如下所示,
?
void cvAbsDiff(const CvArr* src1, const CvArr* src2, CvArr* dst );
?
所以cvAbsDiff(src2,src1,dst);和cvAbsDiff(src1,src2,dst);的執(zhí)行結(jié)果一樣,如下:
 
 
但是,很多人肯定想著自己實現(xiàn)代碼,并進行優(yōu)化,
/*圖像的差分
*要求輸入輸出圖像有相同的格式和大小
*/
void? cvSub(IplImage* src1,IplImage*src2,IplImage* dst)
{
??? IplImage*src1_gray = cvCreateImage(cvGetSize(src1),8,1);
??? IplImage*src2_gray = cvCreateImage(cvGetSize(src2),8,1);
??? cvCvtColor(src1,src1_gray,CV_RGB2GRAY);
??? cvCvtColor(src2,src2_gray,CV_RGB2GRAY);
??? CvScalarpixel;
??? for (int i = 0;i <src1->height; i++)
??????? for (int j = 0; j< src1->width; j++)
??????? {
??????????? CvScalarp1 = cvGet2D(src1_gray,i,j);
??????????? CvScalarp2 = cvGet2D(src2_gray,i,j);
??????????? pixel.val[0]= abs(p1.val[0] - p2.val[0])*120/(p1.val[0]);?//相對灰度值
??????????? cvSet2D(dst,i,j,pixel);
??????? }
??????? cvReleaseImage(&src1_gray);
??????? cvReleaseImage(&src2_gray);
??????? cvShowImage("result",dst);
}
int main()
 
 {
 
 ?? ?IplImage*src1 = cvLoadImage("D:\\test1.png");
 
 ?? ?IplImage*src2 = cvLoadImage("D:\\test2.png");
 
 ?? ?assert(src1);
 
 ?? ?IplImage*dst = cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
 
 ?? ?cvSub(src1, src2, dst);
 
 ?? ?cvShowImage("1", src1);
 
 ?? ?cvShowImage("2", src2);
 
 ?? ?cvShowImage("dst", dst);
 
 ?? ?cvWaitKey(0);
 
 }
 
運行效果如下:
 
//圖像差分
 #include <stdio.h>
 #include <stdlib.h>
 #include <iostream>
 #include "opencv2/opencv.hpp"
 #include "opencv2/imgproc.hpp"
 #include "opencv2/highgui.hpp"
 #include "opencv2/ml.hpp"
 
 using namespace std;
 
 void Image_Minus(IplImage *X, IplImage *Y, IplImage *X_Y)
 {
 ?? ?//圖像差分函數(shù),將圖像1中像素和圖像2中對應(yīng)像素想減,要求X、Y、X_Y大小相同
 ?? ?int i, j, width, height, step, chanel;
 ?? ?unsigned char *dataX, *dataY, *dataX_Y;
 
 ?? ?width = X->width;
 ?? ?height = X->height;
 
 ?? ?//存入矩陣數(shù)據(jù)
 ?? ?dataX = (unsigned char *)X->imageData;
 ?? ?dataY = (unsigned char *)Y->imageData;
 ?? ?dataX_Y = (unsigned char *)X_Y->imageData;
 
 ?? ?//計算步長
 ?? ?step = X->widthStep / sizeof(char);
 ?? ?chanel = X->nChannels;
 
 ?? ?//一個個數(shù)據(jù)處理
 ?? ?for (i = 0; i<height; i++)
 ?? ?for (j = 0; j<width*chanel; j++)
 ?? ??? ?dataX_Y[i*step + j] = abs(dataX[i*step + j] - dataY[i*step + j]);
 }
 
 
 int main()
 {
 ?? ?IplImage* pImgX;
 ?? ?IplImage* pImgY;
 ?? ?IplImage* pImgX_Y;
 ?? ?CvSize dest_size;
 ?? ?pImgX = cvLoadImage("D:\\test1.png", -1);
 ?? ?pImgY = cvLoadImage("D:\\test2.png", -1);
 
 ?? ?if (pImgX == 0 || pImgY == 0)
 ?? ?{
 ?? ??? ?printf("載入文件失敗!/n");
 ?? ??? ?return -1;
 ?? ?}
 ?? ?dest_size.width = pImgX->width;
 ?? ?dest_size.height = pImgX->height;
 
 ?? ?cout << "width == " << dest_size.width << endl;
 ?? ?cout << "height == " << dest_size.height << endl;
 
 ?? ?pImgX_Y = cvCreateImage(dest_size, pImgX->depth, pImgX->nChannels);
 
 ?? ?//圖像差分,最最關(guān)鍵的一步
 ?? ?Image_Minus(pImgX, pImgY, pImgX_Y);
 
 
 ?? ?//創(chuàng)建窗口
 ?? ?cvNamedWindow("Picture X:", 1);
 ?? ?cvNamedWindow("Picture Y:", 1);
 ?? ?cvNamedWindow("Picture X-Y:", 1);
 ?? ?//顯示圖像
 ?? ?cvShowImage("Picture X:", pImgX);
 ?? ?cvShowImage("Picture Y:", pImgY);
 ?? ?cvShowImage("Picture X-Y:", pImgX_Y);
 ?? ?cvWaitKey(0);
 ?? ?//銷毀窗口
 ?? ?cvDestroyWindow("Picture X:");
 ?? ?cvDestroyWindow("Picture Y:");
 ?? ?cvDestroyWindow("Picture X-Y:");
 ?? ?//釋放圖像
 ?? ?cvReleaseImage(&pImgX);
 ?? ?cvReleaseImage(&pImgY);
 ?? ?cvReleaseImage(&pImgX_Y);
 ?? ?return 0;
 }
效果如下圖:
 
 
總結(jié)
                            
                        - 上一篇: Android实现录屏直播(二)需求才是
 - 下一篇: 04 DIY流星观测站——镜头简介