图像代数运算:平均值去噪,减去背景
代数运算,就是对两幅图像的点之间进行加、减、乘、除的运算。四种运算相应的公式为:
代数运算中比较常用的是图像相加和相减。图像相加常用来求平均值去除addtive噪声或者实现二次曝光(double-exposure)。图像相减用于减去背景或周期噪声,污染等。
图像相加
OpenCV中提供了相加的函数
void cvAcc(
const CvArr* image,//输入图像
CvArr* sum, //累积图像
const CvArr* mask=NULL//可选的运算
);
我们还需要用到一个线性变换转换函数来对相加的结果求平均
void cvConvertScale(
const CvArr* src, //输入数组
CvArr* dst,//输出数组
double scale=1,//比例
double shift=0 //缩放比例,可选
);
#define cvCvtScale cvConvertScale
#define cvScale cvConvertScale
#define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 )
实践:平均值去噪
我们用NASA的一段幸运团的视频做实验,截取视频的某几个连续帧求平均值:
int main()
{
CvCapture* capture=cvCaptureFromFile(“media.avi”);
IplImage* frame= NULL;
IplImage * imgsum =NULL;
int start=301;
int end=304;
cvSetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES, start);
int count = start;
while( cvGrabFrame(capture) && count 《= end )
{
frame = cvRetrieveFrame(capture);// 获取当前帧
if(imgsum==NULL){
imgsum=cvCreateImage(cvGetSize(frame),IPL_DEPTH_32F,3);
cvZero(imgsum);
}
cvAcc(frame,imgsum);
char testname[100];
sprintf(testname,“%s%d%s”,“image”,count,“.jpg”);
cvShowImage(testname,frame);
cvSaveImage(testname,frame);
count++;
}
IplImage * imgavg = cvCreateImage(cvGetSize(frame),IPL_DEPTH_8U,3);
cvConvertScale(imgsum,imgavg,1.0/4.0);
cvShowImage(“imageavg”,imgavg);
cvSaveImage(“imageavg_4.jpg”,imgavg);
cvWaitKey(0);
cvReleaseCapture(&capture);
return 0;
}
以下从左到右分别是连续两帧、四帧、八帧、十六帧求均值的结果:
实践:图像二次曝光
曝光和去噪是一样的,也是对几幅图像求平均
//通过求平均二次曝光
int main()
{
IplImage* image1= cvLoadImage(“psu3.jpg”);
IplImage* image2= cvLoadImage(“psu4.jpg”);
IplImage * imgsum =cvCreateImage(cvGetSize(image1),IPL_DEPTH_32F,3);
cvZero(imgsum);
cvAcc(image1,imgsum);
cvAcc(image2,imgsum);
IplImage * imgavg = cvCreateImage(cvGetSize(image1),IPL_DEPTH_8U,3);
cvConvertScale(imgsum,imgavg,1.0/2.0);
cvShowImage(“imageavg”,imgavg);
cvSaveImage(“avg.jpg”,imgavg);
cvWaitKey(0);
cvReleaseImage(&image1);
cvReleaseImage(&image2);
cvReleaseImage(&imgsum);
cvReleaseImage(&imgavg);
return 0;
}
下图是对同学街舞截图的“二次曝光”效果:
图像相减
OpenCV中用cvAbsDiff函数计算两数组的差的绝对值
void cvAbsDiff(
const CvArr* src1,//第一个输入数组
const CvArr* src2,//第二个输入数组
CvArr* dst//输出数组
);
实践:减去背景
减去背景是通过两幅图像代数相减,可以判断出前景区域和运动区域,这是最简单(很多时候也是效果很
好的)运动检测方法。
//减去背景
int main()
{
IplImage* pFrame = NULL;
IplImage* pFrImg = NULL;
IplImage* pBkImg = NULL;
CvMat* pFrameMat = NULL;
CvMat* pFrMat = NULL;
CvMat* pBkMat = NULL;
CvCapture* pCapture = NULL;
int nFrmNum = 0;
//创建窗口
cvNamedWindow(“video”, 1);
cvNamedWindow(“background”,1);
cvNamedWindow(“foreground”,1);
pCapture = cvCaptureFromFile(“media.avi”);
while(pFrame = cvQueryFrame( pCapture ))
{
nFrmNum++;
//如果是第一帧,需要申请内存,并初始化
if(nFrmNum == 1)
{
pBkImg = cvCreateImage(cvSize(pFrame-》width, pFrame-》height), IPL_DEPTH_8U,1);
pFrImg = cvCreateImage(cvSize(pFrame-》width, pFrame-》height), IPL_DEPTH_8U,1);
pBkMat = cvCreateMat(pFrame-》height, pFrame-》width, CV_32FC1);
pFrMat = cvCreateMat(pFrame-》height, pFrame-》width, CV_32FC1);
pFrameMat = cvCreateMat(pFrame-》height, pFrame-》width, CV_32FC1);
//转化成单通道图像再处理
cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY);
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
cvConvert(pFrImg, pFrMat);
cvConvert(pFrImg, pBkMat);
}
else
{
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
//当前帧跟背景图相减
cvAbsDiff(pFrameMat, pBkMat, pFrMat);
//二值化前景图
cvThreshold(pFrMat, pFrImg, 60, 255.0, CV_THRESH_BINARY);
//更新背景
cvRunningAvg(pFrameMat, pBkMat, 0.003, 0);
//将背景转化为图像格式,用以显示
cvConvert(pBkMat, pBkImg);
cvShowImage(“video”, pFrame);
cvShowImage(“background”, pBkImg);
cvShowImage(“foreground”, pFrImg);
if( cvWaitKey(2) 》= 0 )
break;
}
}
cvDestroyWindow(“video”);
cvDestroyWindow(“background”);
cvDestroyWindow(“foreground”);
cvReleaseImage(&pFrImg);
cvReleaseImage(&pBkImg);
cvReleaseMat(&pFrameMat);
cvReleaseMat(&pFrMat);
cvReleaseMat(&pBkMat);
cvReleaseCapture(&pCapture);
return 0;
}
效果图:
========
opencv连通域去噪
//Find_Connected_Component参数说明:
/*mask———一副灰度图
polygon1_hull0———用多边形拟合选1,用凸包拟合选0
scale———设置不被删除的连通轮廓大小
num————连通轮廓的最大数目
bbs——指向连通轮廓的外接矩形
center——指向连通轮廓的中心*/
#define CVCONTOUR_APPROX_LEVEL 2//数越大连通区域的边界越简单
#define CVCLOSE_ITR 1 //图像形态学运算的次数
void Find_Connected_Component(IplImage *mask,int polygon1_hull0=1,float scale=4
,int *num=0,CvRect *bbs=0,CvPoint
*centers=0)//连通域去噪声
{
static CvMemStorage* mem_storage=0;
static CvSeq* contours=0;
//先开操作,后闭操作
cvMorphologyEx(mask,mask,0,0,CV_MOP_OPEN,CVCLOSE_ITR);
cvMorphologyEx(mask,mask,0,0,CV_MOP_CLOSE,CVCLOSE_ITR);
//找到所有轮廓
if(!mem_storage)
{
mem_storage=cvCreateMemStorage(0);
}
else{
cvClearMemStorage(mem_storage);
}
CvContourScanner scanner=cvStartFindContours(mask,mem_storage,sizeof
(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
//丢弃太小的轮廓,用多边形或凸包拟合剩下的轮廓
CvSeq* c;
int numCont=0;
while(c=cvFindNextContour(scanner))
{
double len=cvContourPerimeter(c);//连通域周长
double q=(mask-》height+mask-》width)/scale;
//cout《《len《《“ ”;
if(len《q){cvSubstituteContour(scanner,NULL);}
else{
CvSeq* c_new;
if(polygon1_hull0)
{
//多边形拟合
c_new=cvApproxPoly(c,sizeof
(CvContour),mem_storage,CV_POLY_APPROX_DP,CVCONTOUR_APPROX_LEVEL,0);
}
else{
//凸包拟合
c_new=cvConvexHull2(c,mem_storage,CV_CLOCKWISE,1);
}
cvSubstituteContour(scanner,c_new);
numCont++;
}
}
contours=cvEndFindContours(&scanner);
const CvScalar CVX_WHITE=CV_RGB(0xff,0xff,0xff);//白色
const CvScalar CVX_BLACK=CV_RGB(0x00,0x00,0x00); //黑色
//重绘连通区域
cvZero(mask);
IplImage *maskTemp;
int numFilled=0;
if(num!=NULL)
{
int N=*num,i=0;
CvMoments moments;
double M00,M01,M10;
maskTemp=cvCloneImage(mask);
for(i=0,c=contours;c!=NULL;c=c-》h_next,i++)
{
if(i《N)
{
cvDrawContours(maskTemp,c,CVX_WHITE,CVX_WHITE,-1,CV_FILLED,8);
//找中心
if(centers){
cvMoments(maskTemp,&moments,1);
M00=cvGetSpatialMoment(&moments,0,0);
M10=cvGetSpatialMoment(&moments,1,0);
M01=cvGetSpatialMoment(&moments,0,1);
centers[i].x=(int)(M10/M00);
centers[i].y=(int)(M01/M00);
}
if(bbs!=NULL){
bbs[i]=cvBoundingRect(c);
}
cvZero(maskTemp);
numFilled++;
}
//画区域
cvDrawContours(mask,c,CVX_WHITE,CVX_WHITE,-1,CV_FILLED,8);
}
//*num=numFilled;
cvReleaseImage(&maskTemp);
}
else
{
for(c=contours;c!=NULL;c=c-》h_next)
cvDrawContours(mask,c,CVX_WHITE,CVX_WHITE,-1,CV_FILLED,8);
}
*num=numCont;
}
========
评论
查看更多