图像差分的方法
差分圖像的幾個方法
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流星观测站——镜头简介