zoukankan      html  css  js  c++  java
  • OpenCV —— 图像局部与部分分割(一)

    背景减除

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

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

    场景建模

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

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

    // 从视频的一行中读出所有像素的RGB值,收集这些数值并将其分成三个文件
    
    
    #include <cv.h>
    #include <highgui.h>
    
    int main(int argc,char** argv)
    {
        CvCapture* capture=cvCreateFileCapture("");
        int max_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(float scale)
    {
        cvConvertScale(IdiffF,Iscratch,scale);
        cvAdd(Iscratch,IavgF,IhiF);
        cvSplit(IhiF,Ihi1,Ihi2,Ihi3,0);
    }
    
    void setLowThreshold(float scale)
    {
        cvConvertScale(IdiffF,Iscratch,scale);
        cvSub(IavgF,Iscratch,IlowF);
        cvSplit(IlowF,Ilow1,Ilow2,Ilow3,0);
    }
    
    // 计算每一个像素的均值和方差观测 (平均绝对差分)
    void createModelsfromStats()
    {
        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);
    }
    
    
    void DeallocateImages()
    {
        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 3
    typedef struct ce
    {
        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 struct code_book{
        code_element **cb;
        int numEntries;
        int t;    // count every access
    }codeBook;
    
    // 如果一个像素值的美国通道都不在 min - learnLow 和 max + learnHigh 之间,就会生成一个新的码元。距离上次更新和陈旧的时间(t_last_update)用于删除过程中学习的很少使用的码本条目
    
    
    // 背景学习
    int update_codebook(uchar* p,codeBook& c,unsigned* cbBounds,int numChannels)
    {
        int n;
        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
        int matchChannel;
    
        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,接着用“闭”操作重建由于开操作丢失的边缘部分

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

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

  • 相关阅读:
    Linux显示2015年日历表
    Linux显示系统日期
    Dialogs 对话框
    grid 属性
    VS2010 fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏 嵌入清单
    Perl Tk grid 布局
    Perl Tk pack布局示例
    Perl Tk grid布局管理器
    Perl DBI模块
    输入文本框模型
  • 原文地址:https://www.cnblogs.com/sprint1989/p/3815558.html
Copyright © 2011-2022 走看看