OpenCV —— 图像局部与部分分割(一)

摘要:
背景减除一旦背景模型建立,将背景模型和当前的图像进行比较,然后减去这些已知的背景信息,则剩下的目标物大致就是所求的前景目标了缺点——该方法基于一个不长成立的假设:所有像素点是独立的场景建模新的前景(物体移动的新位置)——旧的前景(物体离开后留下的“空洞”)——背景cvInitLineIterator()和CV_NEXT_LINE_POINT()对任意直线上的像素进行采样//从视频的一行中读出所有像

背景减除

一旦背景模型建立,将背景模型和当前的图像进行比较,然后减去这些已知的背景信息,则剩下的目标物大致就是所求的前景目标了

缺点 —— 该方法基于一个不长成立的假设:所有像素点是独立的

场景建模

新的前景(物体移动的新位置) —— 旧的前景 (物体离开后留下的“空洞”)—— 背景

cvInitLineIterator() 和 CV_NEXT_LINE_POINT() 对任意直线上的像素进行采样

//从视频的一行中读出所有像素的RGB值,收集这些数值并将其分成三个文件

#include <cv.h>#include <highgui.h>

int main(int argc,char**argv)
{
    CvCapture* capture=cvCreateFileCapture("");
    intmax_buffer;
    IplImage *rowImage;
    int r[10000],g[10000],b[10000];
    CvLineIterator iterator;

    FILE *fptrb=fopen("blines.csv","w");    //store the data here
    FILE *fptrg=fopen("glines.csv","w");
    FILE *fptrr=fopen("rlines.csv","w");

    CvPoint pt1,pt2;

    for(;;)
    {
        if(!cvGrabFrame(capture))
            break;
        rowImage=cvRetrieveFrame(capture);
        max_buffer=cvInitLineIterator(rowImage,pt1,pt2,&iterator,8,0);

        for (int j=0;j<max_buffer;j++)
        {
            fprintf(fptrb,"%d",iterator.ptr[0]);
            fprintf(fptrg,"%d",iterator.ptr[1]);
            fprintf(fptrr,"%d",iterator.ptr[2]);

            iterator.ptr[2]=255;    //mark this sample in red
CV_NEXT_LINE_POINT(iterator);

            fprintf(fptrb,"");
            fprintf(fptrg,"");
            fprintf(fptrr,"");

        }
    }

    cvReleaseCapture(capture);
    fclose(fptrb);
    fclose(fptrg);
    fclose(fptrr);

    return 0;
}

帧差 —— 用一帧减去另一帧,然后将足够大的差别标为前景,这种方法往往能捕捉运动目标的边缘

cvAbsDiff cvThreshold (忽略很小的差异——比如小于15,标识其余的作为较大的差别

可以用cvErode() 函数或者用连通域去噪

对于彩色图像,我们用相同的代码对每个颜色通道分别处理,之后在调用cvOr() 函数将所有的通道拼接在一起


平均背景法

—— 计算每个像素的平均值和标准差作为他的背景模型

平均背景法使用四个OpenCV函数 :

cvAcc 累计图像

cvAbsDiff 计算一定时间内的每帧图像之差

cvInRange 将图像分割成前景区和背景区域 (背景模型在已经学习的情况下)

cvOr 将不同的彩色通道图像中合成一个掩码图像

//背景法 --- 只能用于背景场景中不包含运动的部分

//为需要的不同临时图像和统计属性图像创建指针
IplImage *IavgF,IdiffF,*IprevF,*IhiF,*IlowF;

IplImage *Iscratch,*Iscratch2;

IplImage *Igray1,*Igray2,*Igray3;
IplImage *Ilow1,*Ilow2,*Ilow3;
IplImage *Ihi1,*Ihi2,*Ihi3;

IplImage *Imaskt;

float Icount;    //couts number of images learned for averaging later

//创建一个函数给需要的所有临时图像分配内存

void AllocateImages(IplImage*I)
{
    CvSize sz=cvGetSize(I);

    IavgF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    IdiffF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    IprevF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    IhiF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    IlowF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    Ilow1=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ilow2=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ilow3=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ihi1=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ihi2=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ihi3=cvCreateImage(sz,IPL_DEPTH_32F,1);

    cvZero(IavgF);
    cvZero(IdiffF);
    cvZero(IprevF);
    cvZero(IhiF);
    cvZero(IlowF);
    Icount = 0.00001;    //Protect against divide by zero
Iscratch =cvCreateImage(sz,IPL_DEPTH_32F,3);
    Iscratch2 =cvCreateImage(sz,IPL_DEPTH_32F,3);
    Igray1 =cvCreateImage(sz,IPL_DEPTH_32F,1);
    Igray2 =cvCreateImage(sz,IPL_DEPTH_32F,1);
    Igray3 =cvCreateImage(sz,IPL_DEPTH_32F,1);
    Imaskt =cvCreateImage(sz,IPL_DEPTH_32F,1);

    cvZero(Iscratch);
    cvZero(Iscratch2);

}

//学习积累背景图像和每一帧图像差值的绝对值
void accumulateBackground(IplImage*I)
{
    static int first=1;
    cvCvtScale(I,Iscratch,1,0);
    if (!first)
    {
        cvAcc(Iscratch,IavgF);
        cvAbsDiff(Iscratch,IprevF,Iscratch2);
        cvAcc(Iscratch,IdiffF);
        Icount+=1.0;
    }
    first=0;
    cvCopy(Iscratch,IprevF);
}

void setHighThreshold(floatscale)
{
    cvConvertScale(IdiffF,Iscratch,scale);
    cvAdd(Iscratch,IavgF,IhiF);
    cvSplit(IhiF,Ihi1,Ihi2,Ihi3,0);
}

void setLowThreshold(floatscale)
{
    cvConvertScale(IdiffF,Iscratch,scale);
    cvSub(IavgF,Iscratch,IlowF);
    cvSplit(IlowF,Ilow1,Ilow2,Ilow3,0);
}

//计算每一个像素的均值和方差观测 (平均绝对差分)
voidcreateModelsfromStats()
{
    cvConvertScale(IavgF,IavgF,(double)(1.0/Icount));
    cvConvertScale(IdiffF,IdiffF,(double)(1.0/Icount));

    //make sure diff is always something
    cvAddS(IdiffF,cvScalar(1.0,1.0,1.0),IdiffF);
    setHighThreshold(7.0);
    setLowThreshold(6.0);
    //对每一帧图像的绝对差大于平均值7倍的像素都认为是前景
}

//有了背景模型,同时给出了高低阈值,就可以用它将图像分割成前景(不能被背景模型解释的图像部分)和背景(在背景模型中,任何高低阈值之间的图像部分)

void backgroundDiff(IplImage* I,IplImage*Imask)
{
    cvCvtScale(I,Iscratch,1,0);    //To float
cvSplit(Iscratch,Igray1,Igray2,Igray3);

    //channel 1
    cvInRange(Igray1,Ilow1,Ihi1,Imask);    //是否在高低阈值之间

    //channel 2
cvInRange(Igray2,Ilow2,Ihi2,Imask);

    //channel 3
cvInRange(Igray3,Ilow3,Ihi3,Imask);

    cvOr(Imask,Imaskt,Imask);

    //finally , invert the result
    cvSubRS(Imask,255,Imask);
}


voidDeallocateImages()
{
    cvReleaseImage(&IavgF);
    cvReleaseImage(&IdiffF);
    cvReleaseImage(&IprevF);
    cvReleaseImage(&IhiF);
    cvReleaseImage(&IlowF);
    cvReleaseImage(&Ilow1);
    cvReleaseImage(&Ilow2);
    cvReleaseImage(&Ilow3);
    cvReleaseImage(&Ihi1);
    cvReleaseImage(&Ihi2);
    cvReleaseImage(&Ihi3);
    cvReleaseImage(&Iscratch);
    cvReleaseImage(&Iscratch2);
    cvReleaseImage(&Igray1);
    cvReleaseImage(&Igray2);
    cvReleaseImage(&Igray3);
    cvReleaseImage(&Imaskt);

}

累计均值,方差和协方差

均值漂移

cvRunningAvg —— 更新时,源图像占一定权重 —— 也称为,跟踪器(前一帧图像褪色的影响,参数a本质上上是设置所需要的时间)

计算方差 —— cvSquareAcc —— 单个像素的方差正好是平方的均值减去均值的平方

计算协方差 —— cvMultiplyAcc


高级背景模型

复杂的运动目标 —— 得到美国像素或一组像素的时间序列模型 ,这种模型能够很好的处理时间起伏,缺点是需要消耗大量的内存

codebook (编码本) —— 将一个像素现在的观测值和先前的观测值作比较。如果两个值很接近,它被建模为那种颜色下的扰动,如果两个值不接近,它可以产生与该像素相关的一组色彩。

从经验角度看绝大部分背景中的变化倾向于沿着亮度轴,而不是颜色轴

在背景学习模型的codebook方法中,在每一个三颜色轴上,每一个box用两个阈值(最大和最小)定义。如果新的背景模型落到学习的阈值(learnHigh 和 learnLow 之间,这些box的边界将膨胀 (最大阈值变大,最小阈值变小)。如果新的背景样本在box和学习阈值外,将开始生成一个新的box,在背景差分模型中,也能容纳maxMod和minMod阈值。使用这些阈值。可以说,如果一个像素和box边界最大值和最小值非常接近,我们就认为它在box里面。再次调整阈值,允许模型适应特殊情形)

codebook box 容纳呈现多维不连续分布的像素,所以能更好的模拟像素的不连续分布

使用codebook 背景模型

1,使用 函数 update_codebook 在几秒钟或几分钟时间内训练一个基本的背景模型

2,使用 clear_stale_entries 清除stale索引

3,调整阈值 minMod 和 maxMod ui已知前景达到最好的分割

4,保持一个更高级别的场景模型

5,通过 background_diff 使用训练好的模型将前景从背景中分割出来

6,定期更新学习的背景像素

7,在一个频率较慢的情况下,使用函数 clear_stale_entries 定期清理 stale 的codebook 索引

部分代码:

#include <cv.h>#include <highgui.h>

#define CHANNELS 3typedef structce
{
    uchar learnHigh[CHANNELS];    
    uchar learnLow[CHANNELS];
    uchar max[CHANNELS];    //High side of box boundary
uchar min[CHANNELS];
    int t_last_update;    //allow us to kill stale entries
    int stale;    //max negative run
}code_element;

typedef structcode_book{
    code_element **cb;
    intnumEntries;
    int t;    //count every access
}codeBook;

//如果一个像素值的美国通道都不在 min - learnLow 和 max + learnHigh 之间,就会生成一个新的码元。距离上次更新和陈旧的时间(t_last_update)用于删除过程中学习的很少使用的码本条目


//背景学习
int update_codebook(uchar* p,codeBook& c,unsigned* cbBounds,intnumChannels)
{
    intn;
    unsigned int high[3],low[3];
    for (n=0;n<numChannels;n++)
    {
        high[n]=*(p+n)+*(cbBounds+n);
        if(high[n]>255)
            high[n]=255;
        low[n]=*(p+n)-*(cbBounds+n);
        if(low[n]<0)
            low[n]=0;
    }

    //see if this fits an existing codeword
    intmatchChannel;

    for (int i=0;i<c.numEntries;i++)
    {
        matchChannel=0;
        for (n=0;n<numChannels;n++)
        {
            if((c.cb[i]->learnLow<=*(p+n))&&(*(p+n)<=c.cb[i]->learnHigh[n]))
                matchChannel++;

            if (matchChannel==numChannels)    //if an entry war found
{
                c.cb[i]->t_last_update=c.t;
                    //adjust this codeword for the first channel

                for (n=0;n<numChannels;n++)
                {
                    if (c.cb[i]->max[n]<*(p+n))
                    {
                        c.cb[i]->max[n]=*(p+n);
                    }
                    else if (c.cb[i]->min[n]>*(p+n))
                    {
                        c.cb[i]->min[n]=*(p+n);
                    }
                }
                break;
            }
        }
    }


    //overhead to track potential stale entries

    for (int s=0;s<c.numEntries;s++)
    {
        int negRun=c.t-c.cb[s]->t_last_update;
        if (c.cb[s]->stale<negRun)
        {
            c.cb[s]->stale=negRun;
        }
    }

    //enter a new codeword if needed

}

//学习有移动前景目标的背景

//背景差分,寻找前景目标

用于前景清除的连通部分

包含噪声输入掩模图像,然后利用形态学“开”操作将小的噪声缩小至0,接着用“闭”操作重建由于开操作丢失的边缘部分

没有任何理由相信噪声有很大的空间相关性,这些信号又大量的非常小的区域来描述

一个功能强大的在背景中减去噪声的技术

免责声明:文章转载自《OpenCV —— 图像局部与部分分割(一)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇郁闷的一本书《深入解析Windows操作系统》Asp.net中使用ActiveX控件下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

OpenCV学习(13) 细化算法(1)

程序编码参考经典的细化或者骨架算法文章: T. Y. Zhang and C. Y. Suen, “A fast parallel algorithm for thinning digital patterns,” Comm. ACM, vol. 27, no. 3, pp. 236-239, 1984. 它的原理也很简单:       我们对一副二值图像...

RestTemplate 发送 get 请求使用误区 多个参数传值为null

RestTemplate 发送 get 请求使用误区 多个参数传值为null(转载) 首先看一下官方文档是怎么描述的,传递多个值的情况(注意例子中用到的@pathParam,一般要用@queryParam) RestTemplate 实例 @Configuration public class RestConfiguration { @...

当微信小程序遇到AR(二)

当微信小程序遇到AR,会擦出怎么样的火花?期待与激动...... 通过该教程,可以从基础开始打造一个微信小程序的AR框架,所有代码开源,提供大家学习。 本课程需要一定的基础:微信开发者工具,JavaScript,Html,Css 第二章:基石-摄像头数据获取 【前情提要】 上一章,我们了解了微信小程序与AR相遇一些前因后果,同时也,罗列出来是要实现这样...

字体单位大小对照换算表(字号、磅、英寸、像素)

字号“数值”越大,字就越小。   中文字号 英文字号(磅) 毫米 像素 1英寸 72pt 25.30mm 95.6px  大特号 63pt 22.14mm 83.7px 特号 54pt 18.97mm 71.7px  初号 42pt 14.82mm 56px 小初 36pt 12.70mm 48px 一号 26pt 9.17mm...

人脸识别评价算法指标

首先了解相关指标名称 误识率FAR false acceptance rate FAR=NFA/NIRA NIRA是类间测试次数(假冒者尝试的总次数),NFA是错误接收次数 FAR越低,假冒者被接受的可能性越低,系统安全性越高 误拒绿FRR false rejection rate FRR=NFR/NGRA NGRA是类内测试次数(合法用户尝试的总次数),...

限流的几种方式

先来描述一下什么是限流   限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。 一般做接口限流主要是为了应对突发流量,避免突发流量拖垮服务。如...