在一些国内的论文中看到尖角检测方法,感觉有点类似以前学过的“种子填充”或者“扫描线”。
算法描述
按照从上到下,从左到右的顺序扫描整个二值图像。遇到像素为白色时不做任何标记,继续扫描下一像素;遇到黑色像素,则判断周围像素是否被标记的情况来决定本像素的标记情况。可分为4种情况:
- 若该像素的上方和左方除了0和255外没有其它的标记,则将该像素设置一个新的标记;
- 若该像素上方有除了0和255外其它的标记而左方没有,则设置为和上方像素相同的标记;
- 若该像素左方有除了0和255外其它的标记而上方没有,则设置为和左方像素相同的标记;
- 若该像素的上方和左方除了0和255外都有其它的标记,如果上方和左方的标记相同,则将该像素
设置为和该标记相同的标记;如果上方和左方的标记不相同,将该像素设置为和上方像素相同的标记,然后将左方重新标记。重新标记是将该像素的左方与之连续且和它左边的像素标号一样的像素点都重新标记为和当前像素一样的标记,当遇到亮点即像素值为255或图像边界或与需要重新标记的标号不一致时重新标记结束。处理结束后扫描下一像素,扫描完一行进入下一行,遇到黑色的像素点处理方法和以上相同。所有处理结束后再对整个图像扫描一遍,统计尖角的数目,计算每个尖角所在联通区域的面积(即该区域内的像素点数)以及它们的高度。
论文中的一个示意图:
代码实现
自己写的比较笨拙的代码:
- void FireDetector::CheckTaperAngle(Mat &img)
- {
- int dilation_type = MORPH_RECT;
- int dilation_size=5;
- Mat element = getStructuringElement( dilation_type,
- Size( 2*dilation_size + 1, 2*dilation_size+1 ),
- Point( dilation_size, dilation_size ) );
- Mat copyImage,angGray;
- img.copyTo(copyImage);
- dilate(copyImage,copyImage,Mat());
- dilate(copyImage,copyImage,Mat());
- copyImage.copyTo(angGray);
- //imshow("dilage",img);
- int nrows=angGray.rows;
- int ncols=angGray.cols;
- //cvtColor(copyImage,angGray,CV_BGR2GRAY);
- int flag=1;
- for(int i=0;i<nrows;i++){
- uchar *pimg=copyImage.ptr<uchar>(i);
- uchar *pgray=angGray.ptr<uchar>(i);
- for(int j=0;j<ncols;j++){
- if(pimg[j]>0){
- if(i>0&&j>0){
- uchar* ppgray=angGray.ptr<uchar>(i-1);
- if(ppgray[j]==0&&pgray[j-1]==0)
- pgray[j]=flag++;
- else if(ppgray[j]>0&&pgray[j-1]==0){
- pgray[j]=ppgray[j];
- }
- else if(pgray[j-1]>0&&ppgray[j]==0){
- pgray[j]=pgray[j-1];
- }
- else if(pgray[j-1]>0&&ppgray[j]>0&&pgray[j-1]==ppgray[j]){
- pgray[j]=pgray[j-1];
- }
- else{
- ppgray[j]=flag++;
- pgray[j]=ppgray[j];
- uchar tmp=pgray[j-1];
- for(int k=j-1;k>0;k--){
- if(pgray[k]==tmp)
- pgray[k]=pgray[j];
- }
- }
- }// end if: i>0&&j>0
- else if(j>0&&i==0){
- if(pgray[j-1]>0)
- pgray[j]=pgray[j-1];
- else
- pgray[j]=flag++;
- } //end else if: j>0&&i==0
- else if (i>0&&j==0){
- uchar *ppgray =angGray.ptr<uchar>(i-1);
- if(ppgray[j]>0)
- pgray[j]=ppgray[j];
- else
- ppgray[j]=flag++;
- } //end else if: i>0&&j==0
- else{
- pgray[j]=flag++;
- }
- } // end if: pimg[j]>0
- } //end for: int j=0;j<ncols;j++
- } //end for: int i=0;i<nrows;i++
- //imshow("AngleGray",angGray);
- //标记完后画尖角的图
- Mat table = (Mat_<double>(10,3) << 0, 0, 1 , 0.5, 0.4, 0.25, 0.5, 0.5, 0.5, 0, 1, 0, 1, 0.8, 0, 1, 0.5, 1, 1, 0, 1 , 1, 0, 0, 1, 1, 1, 1, 1, 0 );
- cvtColor(angGray,angleImg,CV_GRAY2BGR);
- int channels=angleImg.channels();
- for(int i=0;i<nrows;i++){
- uchar* pgray=angGray.ptr<uchar>(i);
- uchar* pang=angleImg.ptr<uchar>(i);
- for(int j=0,jcol=0;j<ncols,jcol<ncols*channels;j++,jcol+=channels){
- uchar flagvalue=pgray[j];
- if(flagvalue>0){
- for(int k=0;k<3;k++){
- int indexpix=(int)(flagvalue%10);
- double tmp= table.at<double>(indexpix,2-k);
- pang[jcol+k]=(uchar)tmp*255;
- } // end for: k
- }
- } //end for: j
- } //end for: i
- // cout<<flag<<endl;
- // imshow("Taper angle",angleImg);
- }
实验效果
代码中“画尖角图”的部分使用了很多颜色,只是希望显示出一个尖角一种颜色的视觉效果:
原场景是这样的:
这种方法就是对像素太敏感了,一个像素的突出也会计算为尖角,实用性比较差。
参考文献
[1] 顾俊傻 赵敏 吴毅杰.早期火灾火焰尖角计算算法的研究[J].青岛大学学报,2010,25(1):24-27.