剔除边缘毛刺 图像处理

2018-06-27 14:16:52 weixin_42225141 阅读数 797
  • 人眼检测

    学会自己查阅opencv doxygen文档 学会编写opencv代码实现简单图像处理变换 初步了解图像算法及机器视觉基础 运用计算机视觉相关知识和opencv库来构建简单的应用程序。

    168人学习 金圣韬
    免费试看

腐蚀:去除图像表面像素,将图像逐步缩小,以达到消去点状图像的效果;作用就是将图像边缘的毛刺剔除掉

膨胀:将图像表面不断扩散以达到去除小孔的效果;作用就是将目标的边缘或者是内部的坑填掉

使用相同次数的腐蚀和膨胀,可以使目标表面更平滑;但也有场景限制,就是如果去噪不干净的话,会出现意想不到的结果,尽量别使用

大概的效果,适合降噪比较干净的图
腐蚀和膨胀

// 图像腐蚀/膨胀处理
public void erodeImg() {
    Mat outImage = new Mat();

    // size 越小,腐蚀的单位越小,图片越接近原图
    Mat structImage = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 2));

    /**
     * 图像腐蚀
     * 腐蚀说明: 图像的一部分区域与指定的核进行卷积,
     * 求核的最`小`值并赋值给指定区域。
     * 腐蚀可以理解为图像中`高亮区域`的'领域缩小'。
     * 意思是高亮部分会被不是高亮部分的像素侵蚀掉,使高亮部分越来越少。
     */
    Imgproc.erode(mat, outImage, structImage, new Point(-1, -1), 2);
    mat = outImage;

    /**
     * 膨胀
     * 膨胀说明: 图像的一部分区域与指定的核进行卷积,
     * 求核的最`大`值并赋值给指定区域。 
     * 膨胀可以理解为图像中`高亮区域`的'领域扩大'。
     * 意思是高亮部分会侵蚀不是高亮的部分,使高亮部分越来越多。
     */
    Imgproc.dilate(mat, outImage, structImage , new Point(-1, -1), 2);
    mat = outImage;

}

本文章参考了很多博客,感谢;主要是跟着一个博客来实现的https://blog.csdn.net/ysc6688/article/category/2913009 感谢

2018-04-28 14:30:32 KYJL888 阅读数 3053
  • 人眼检测

    学会自己查阅opencv doxygen文档 学会编写opencv代码实现简单图像处理变换 初步了解图像算法及机器视觉基础 运用计算机视觉相关知识和opencv库来构建简单的应用程序。

    168人学习 金圣韬
    免费试看

边缘区域的定义:图像上的一个区域位于边缘。如下图所示:

 

标号1 为一个边缘区域

 现在希望可以将图像中的边缘区域去除。一个简单的思路如下:

           遍历图像上下左右四条边界上的像素。设置一个计数器和一个最小边界阈值。当边界满足要求的像素个数大于阈值,就作为一个待处理区域。并选取其中一个坐标点作为种子点进行满水填充为0。

代码如下:

// 边缘区域去除1.cpp : 定义控制台应用程序的入口点。
// VS2010 opencv2.4.9

#include "stdafx.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat src=imread("1.jpg",1);; 
	imshow("src",src);

	//灰度化
	Mat grayImage;
	cvtColor(src,grayImage,CV_BGR2GRAY);

	//二值化原图像
	Mat thresImage=Mat::zeros(grayImage.rows,grayImage.cols,CV_8UC1);
	threshold(grayImage,thresImage,250,255,THRESH_BINARY);
	//imshow("thresImage",thresImage);


	////方法1:轮廓提取与阈值填充
	//vector<vector<Point>> contours;  
	//vector<Point>contours_sum;
	//// find  
	//findContours(thresImage,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE); 
	//for(int n =0; n<contours.size();++n)
	//{
	//	cout<<contours[n].size()<<endl;
	//	for (int i=0;i<contours[n].size();++i)
	//	{
	//		contours_sum.push_back(contours[n][i]);
	//	}
	//}
	////cout<<contours_sum.size()<<endl;
	//for (int i=0;i<contours_sum.size();++i)
	//{
	//	//cout<<contours_sum[i]<<endl;
	//	floodFill(thresImage,contours_sum[i],0);//漫水填充法
	//}
	//imshow("floodFill",thresImage);




	const int nr=thresImage.rows;
	const int nc=thresImage.cols;
	Mat edge[4];
	edge[0] = thresImage.row(0);    //up
	edge[1] = thresImage.row(nr-1); //bottom
	edge[2] = thresImage.col(0);    //left
	edge[3] = thresImage.col(nc-1); //right

	std::vector<Point> edgePts;
	const int minLength=std::min(nr,nc)/4;
	for(int i=0;i<4;++i)
	{
		std::vector<Point> line;
		Mat_<uchar>::const_iterator iter = edge[i].begin<uchar>();       //当前像素
		Mat_<uchar>::const_iterator nextIter = edge[i].begin<uchar>()+1; //下一个像素
		while(nextIter!=edge[i].end<uchar>())
		{
			if(*iter==255)
			{
				if(*nextIter==255)
				{
					Point pt = iter.pos();
					if(i==1)
						pt.y = nr-1;
					if(i==3)
						pt.x = nc-1;
					
					//line.push_back(pt);
					edgePts.push_back(pt);
				}
				//if(*nextIter!=255)
				//{
				//	if(line.size()>minLength)
				//		edgePts.push_back(line.at(line.size()/2));
				//	line.clear();
				//}
			}
			++iter;
			++nextIter;
		}
	}
	
	for(int n =0; n<edgePts.size();++n)
		//cout<<edgePts[n];
		floodFill(thresImage,edgePts[n],0);//漫水填充法
	imshow("floodFill",thresImage);
	waitKey( 0 );    
}





copyMakeBorder可以处理多通道也可以处理单通道

如何使用OpenCV函数 copyMakeBorder 设置边界(添加额外的边界)。


假设src为以下矩阵

我们首先只在一个方向上,讨论,例如 top方向:

top =5 或者 10, bottom =0,left 0,right =0;注意我们这里故意让top的值,大于 src的rows,即行数。查看结果

borderType = BORDER_REFLECT:反射


解释:当按BORDER_REFLECT,向上给src加边界时,是按照src的反射机制来加的。

borderType = BORDER_REPLICATE:复制


解释:当BORDER_REPLICATE时,代表只复制边界。

当left=5,bottom =5时,

BORDER_TYPE = BORDER_REFLECT_101:


解释:101,已经表明 0不参加反射机制。也即是以第一行为镜面,做反射

给图像添加边界

目标

本文档尝试解答如下问题:

  • 如何使用OpenCV函数 copyMakeBorder 设置边界(添加额外的边界)。

Theory

Note

 

以下内容来自于Bradski和Kaehler的大作 Learning OpenCV 。

  1. 前一节我们学习了图像的卷积操作。一个很自然的问题是如何处理卷积边缘。当卷积点在图像边界时会发生什么,如何处理这个问题?

  2. 大多数用到卷积操作的OpenCV函数都是将给定图像拷贝到另一个轻微变大的图像中,然后自动填充图像边界(通过下面示例代码中的各种方式)。这样卷积操作就可以在边界像素安全执行了(填充边界在操作完成后会自动删除)。

  3. 本文档将会探讨填充图像边界的两种方法:

    1. BORDER_CONSTANT: 使用常数填充边界 (i.e. 黑色或者 0)
    2. BORDER_REPLICATE: 复制原图中最临近的行或者列。

    源码部分给出更加详细的解释。

源码

  1. 本程序做什么?

    • 装载图像

    • 由用户决定使用哪种填充方式。有两个选项:

      1. 常数边界: 所有新增边界像素使用一个常数,程序每0.5秒会产生一个随机数更新该常数值。
      2. 复制边界: 复制原图像的边界像素。

      用户可以选择按 ‘c’ 键 (常数边界) 或者 ‘r’ 键 (复制边界)

    • 当用户按 ‘ESC’ 键,程序退出。

  2. 下面是本教程的源码, 你也可以从 这里 下载

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>

using namespace cv;

/// 全局变量
Mat src, dst;
int top, bottom, left, right;
int borderType;
Scalar value;
char* window_name = "copyMakeBorder Demo";
RNG rng(12345);

/** @函数 main  */
int main( int argc, char** argv )
{

  int c;

  /// 装载图像
  src = imread( argv[1] );

  if( !src.data )
  { return -1;
    printf(" No data entered, please enter the path to an image file \n");
  }

  /// 使用说明
  printf( "\n \t copyMakeBorder Demo: \n" );
  printf( "\t -------------------- \n" );
  printf( " ** Press 'c' to set the border to a random constant value \n");
  printf( " ** Press 'r' to set the border to be replicated \n");
  printf( " ** Press 'ESC' to exit the program \n");

  /// 创建显示窗口
  namedWindow( window_name, CV_WINDOW_AUTOSIZE );

  /// 初始化输入参数
  top = (int) (0.05*src.rows); bottom = (int) (0.05*src.rows);
  left = (int) (0.05*src.cols); right = (int) (0.05*src.cols);
  dst = src;

  imshow( window_name, dst );

  while( true )
    {
      c = waitKey(500);

      if( (char)c == 27 )
        { break; }
      else if( (char)c == 'c' )
        { borderType = BORDER_CONSTANT; }
      else if( (char)c == 'r' )
        { borderType = BORDER_REPLICATE; }

      value = Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
      copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );

      imshow( window_name, dst );
    }

  return 0;
}

解释

  1. 首先申明程序中用到的变量:

    Mat src, dst;
    int top, bottom, left, right;
    int borderType;
    Scalar value;
    char* window_name = "copyMakeBorder Demo";
    RNG rng(12345);
    

    尤其要注意变量 rng ,这是一个随机数生成器, 用来产生随机边界色彩。

  2. 装载原图像 src:

    src = imread( argv[1] );
    
    if( !src.data )
    { return -1;
      printf(" No data entered, please enter the path to an image file \n");
    }
    
  3. 在简要说明了程序的使用方法后,创建一个显示窗口:

    namedWindow( window_name, CV_WINDOW_AUTOSIZE );
    
  4. 初始化边界宽度参数(topbottomleft 和 right)。我们将它们设定为图像 src 大小的5%。

    top = (int) (0.05*src.rows); bottom = (int) (0.05*src.rows);
    left = (int) (0.05*src.cols); right = (int) (0.05*src.cols);
    
  5. 程序进入 while 循环。 如果用户按’c’键或者 ‘r’键, 变量 borderType 分别取值 BORDER_CONSTANT 或 BORDER_REPLICATE :

    while( true )
     {
       c = waitKey(500);
    
       if( (char)c == 27 )
         { break; }
       else if( (char)c == 'c' )
         { borderType = BORDER_CONSTANT; }
       else if( (char)c == 'r' )
         { borderType = BORDER_REPLICATE; }
    
  6. 每个循环 (周期 0.5 秒), 变量 value 自动更新...

    value = Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
    

    为一个由 RNG 类型变量 rng 产生的随机数。 随机数的范围在 [0,255] 之间。

  7. 最后调用函数 copyMakeBorder 填充边界像素:

    copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );
    

    接受参数:

    1. src: 原图像
    2. dst: 目标图像
    3. topbottomleftright: 各边界的宽度,此处定义为原图像尺寸的5%。
    4. borderType: 边界类型,此处可以选择常数边界或者复制边界。
    5. value: 如果 borderType 类型是 BORDER_CONSTANT, 该值用来填充边界像素。
  8. 显示输出图像

    imshow( window_name, dst );
    

结果

  1. 在编译上面的代码之后, 我们可以运行结果,将图片路径输入。 结果应该为:

    • 程序启动时边界类型为 BORDER_CONSTANT (0), 因此,一开始边界颜色任意变换。
    • 如果用户按 ‘r’ 键, 边界将会变成原图像边缘的拷贝。
    • 如果用户按 ‘c’ 键, 边界再次变为任意颜色。
    • 如果用户按 ‘ESC’ 键,程序退出。

    下面显示了几张截图演示了边界颜色如何改变,以及在边界类型为 BORDER_REPLICATE 时的情形:

    Final result after copyMakeBorder application

                                     

2016-12-20 08:35:10 cfqcfqcfqcfqcfq 阅读数 4615
  • 人眼检测

    学会自己查阅opencv doxygen文档 学会编写opencv代码实现简单图像处理变换 初步了解图像算法及机器视觉基础 运用计算机视觉相关知识和opencv库来构建简单的应用程序。

    168人学习 金圣韬
    免费试看

            边缘区域的定义:图像上的一个区域位于边缘。如下图所示:

 

标号1 为一个边缘区域

 现在希望可以将图像中的边缘区域去除。一个简单的思路如下:

           遍历图像上下左右四条边界上的像素。设置一个计数器和一个最小边界阈值。当边界满足要求的像素个数大于阈值,就作为一个待处理区域。并选取其中一个坐标点作为种子点进行满水填充为0。


代码如下:

    Mat src=imread("F:/test.JPG");
    imshow("src",src);

    //灰度化
    Mat grayImage;
    cvtColor(src,grayImage,CV_BGR2GRAY);

    //二值化原图像
    Mat thresImage=Mat::zeros(grayImage.rows,grayImage.cols,CV_8UC1);
    threshold(grayImage,thresImage,127,255,THRESH_BINARY);
    imshow("thresImage",thresImage);

    const int nr=thresImage.rows;
    const int nc=thresImage.cols;
    Mat edge[4];
    edge[0] = thresImage.row(0);    //up
    edge[1] = thresImage.row(nr-1); //bottom
    edge[2] = thresImage.col(0);    //left
    edge[3] = thresImage.col(nc-1); //right

    std::vector<Point> edgePts;
    const int minLength=std::min(nr,nc)/4;
    for(int i=0;i<4;++i)
    {
        std::vector<Point> line;
        Mat_<uchar>::const_iterator iter = edge[i].begin<uchar>();       //当前像素
        Mat_<uchar>::const_iterator nextIter = edge[i].begin<uchar>()+1; //下一个像素
        while(nextIter!=edge[i].end<uchar>())
        {
            if(*iter==255)
            {
                if(*nextIter==255)
                {
                    Point pt = iter.pos();

                    if(i==1)
                        pt.y = nr-1;
                    if(i==3)
                        pt.x = nc-1;

                    line.push_back(pt);
                }
                if(*nextIter!=255)
                {
                    if(line.size()>minLength)
                        edgePts.push_back(line.at(line.size()/2));
                    line.clear();
                }
            }
            ++iter;
            ++nextIter;
        }
    }

    for(int n =0; n<edgePts.size();++n)
        floodFill(thresImage,edgePts[n],0);//漫水填充法

 效果:

         

                                     

2018-05-08 13:29:41 dajiyi1998 阅读数 19485
  • 人眼检测

    学会自己查阅opencv doxygen文档 学会编写opencv代码实现简单图像处理变换 初步了解图像算法及机器视觉基础 运用计算机视觉相关知识和opencv库来构建简单的应用程序。

    168人学习 金圣韬
    免费试看

1.直方图:一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征。图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所占的多少。图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横坐标是灰度级,纵坐标是该灰度级出现的频率。

数字图像在范围[0,G]内总共有L个灰度级,直方图为h(rK)=nk

rK是去见[0,G]内的第K级亮度,nk是灰度级为rK的像素数。

归一化直方图:

1.1直方图对比:

方法描述:有两幅图像patch(当然也可是整幅图像),分别计算两幅图像的直方图,并将直方图进行归一化,然后按照某种距离度量的标准进行相似度的测量。

方法的思想:基于简单的向量相似度来对图像相似度进行度量。

优点:直方图能够很好的归一化,比如256个bin条,那么即使是不同分辨率的图像都可以直接通过其直方图来计算相似度,计算量适中。比较适合描述难以自动分割的图像。

缺点:直方图反应的是图像灰度值得概率分布,并没有图像的空间位置信息在里面,因此,常常出现误判;从信息论来讲,通过直方图转换,信息丢失量较大,因此单一的通过直方图进行匹配显得有点力不从心。

 

矩阵分解的方法

方法描述:将图像patch做矩阵分解,比如SVD奇异值分解和NMF非负矩阵分解等,然后再做相似度的计算。

方法思想:因为图像本身来讲就是一个矩阵,可以依靠矩阵分解获取一些更加鲁棒的特征来对图像进行相似度的计算。

基于SVD分解的方法优点:奇异值的稳定性,比例不变性,旋转不变性和压缩性。即奇异值分解是基于整体的表示,不但具有正交变换、旋转、位移、镜像映射等代数和几何上的不变性,而且具有良好的稳定性和抗噪性,广泛应用于模式识别与图像分析中。对图像进行奇异值分解的目的是得到唯一、稳定的特征描述,降低特征空间的维度,提高抗干扰能力。

基于SVD分解的方法缺点是:奇异值分解得到的奇异矢量中有负数存在,不能很好的解释其物理意义。

基于NMF分解的方法:将非负矩阵分解为可以体现图像主要信息的基矩阵与系数矩阵,并且可以对基矩阵赋予很好的解释,比如对人脸的分割,得到的基向量就是人的“眼睛”、“鼻子”等主要概念特征,源图像表示为基矩阵的加权组合,所以,NMF在人脸识别场合发挥着巨大的作用。

基于矩阵特征值计算的方法还有很多,比如Trace变换,不变矩计算等。

基于特征点方法

方法描述:统计两个图像patch中匹配的特征点数,如果相似的特征点数比例最大,则认为最相似,最匹配

方法思想:图像可以中特征点来描述,比如sift特征点,LK光流法中的角点等等。这样相似度的测量就转变为特征点的匹配了。

以前做过一些实验,关于特征点匹配的,对一幅图像进行仿射变换,然后匹配两者之间的特征点,选取的特征点有sift和快速的sift变形版本surf等。

方法优点:能被选作特征点的大致要满足不变性,尺度不变性,旋转不变等。这样图像的相似度计算也就具备了这些不变性。

方法缺点:特征点的匹配计算速度比较慢,同时特征点也有可能出现错误匹配的现象。

基于峰值信噪比(PSNR)的方法

当我们想检查压缩视频带来的细微差异的时候,就需要构建一个能够逐帧比较差视频差异的系统。最

常用的比较算法是PSNR( Peak signal-to-noise ratio)。这是个使用“局部均值误差”来判断差异的最简单的方法,假设有这两幅图像:I1和I2,它们的行列数分别是i,j,有c个通道。每个像素的每个通道的值占用一个字节,值域[0,255]。注意当两幅图像的相同的话,MSE的值会变成0。这样会导致PSNR的公式会除以0而变得没有意义。所以我们需要单独的处理这样的特殊情况。此外由于像素的动态范围很广,在处理时会使用对数变换来缩小范围。

            

              

基于结构相似性(SSIM,structural similarity (SSIM) index measurement)的方法

结构相似性理论认为,自然图像信号是高度结构化的,即像素间有很强的相关性,特别是空域中最接近的像素,这种相关性蕴含着视觉场景中物体结构的重要信息;HVS的主要功能是从视野中提取结构信息,可以用对结构信息的度量作为图像感知质量的近似。结构相似性理论是一种不同于以往模拟HVS低阶的组成结构的全新思想,与基于HVS特性的方法相比,最大的区别是自顶向下与自底向上的区别。这一新思想的关键是从对感知误差度量到对感知结构失真度量的转变。它没有试图通过累加与心理物理学简单认知模式有关的误差来估计图像质量,而是直接估计两个复杂结构信号的结构改变,从而在某种程度上绕开了自然图像内容复杂性及多通道去相关的问题.作为结构相似性理论的实现,结构相似度指数从图像组成的角度将结构信息定义为独立于亮度、对比度的,反映场景中物体结构的属性,并将失真建模为亮度、对比度和结构三个不同因素的组合。用均值作为亮度的估计,标准差作为对比度的估计,协方差作为结构相似程度的度量。

图像模板匹配:一般而言,源图像与模板图像patch尺寸一样的话,可以直接使用上面介绍的图像相似度测量的方法;如果源图像与模板图像尺寸不一样,通常需要进行滑动匹配窗口,扫面个整幅图像获得最好的匹配patch。

模板匹配:是一种在源图像中寻找与图像patch最相似的技术,常常用来进行目标的识别、跟踪与检测。其中最相似肯定是基于某种相似度准则来讲的,也就是需要进行相似度的测量。另外,寻找就需要在图像上进行逐行、逐列的patch窗口扫描,当然也不一定需要逐行逐列的扫描,当几个像素的误差比计算速度来的不重要时就可以设置扫描的行列步进值,以加快扫描和计算的时间消耗。下面就对相似度测量和模板匹配进行介绍(所有的图像都假定是灰度图)。

         

         

1.2反向投影:一种记录给定图像中像素点如何适应直方图模型像素分布方式的一种方法,也就是说首先计算某一种特征的直方图模板,然后使用模板在去寻找图像中存在的该特征的方法。  作用:反向投影用于在输入图像(通常较大)中查找特定图像(通常较小或者仅1个像素,以下将其称为模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。

反向投影如何查找(工作)?

查找的方式就是不断的在输入图像中切割跟模板图像大小一致的图像块,并用直方图对比的方式与模板图像进行比较。

反向投影的结果是什么?

反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。可以把它看成是一个二维的浮点型数组,二维矩阵,或者单通道的浮点型图像。

假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:

1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;

2)生成临时图像的直方图;

3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;

4)直方图对比结果c,就是结果图像(0,0)处的像素值;

5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;

6)重复(1)~(5)步直到输入图像的右下角。

1.3.直方图均衡化:如果一副图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是一种能仅靠输入图像直方图信息自动达到这种效果的变换函数。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展像元取值的动态范围,提高了对比度和灰度色调的变化,使图像更加清晰。直方图均衡化是图像处理领域中利用图像直方图对对比度进行调整的方法。这种方法通常用来增加许多图像的局部对比度,尤其是当图像的有用数据的对比度相当接近的时候。通过这种方法,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。

直方图均衡化处理的“中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改变成“均匀”分布直方图分布。

直方图均衡化的基本思想是把原始图的直方图变换为均匀分布的形式,这样就增加了象素灰度值的动态范围从而可达到增强图像整体对比度的效果。设原始图像在(x,y)处的灰度为f,而改变后的图像为g,则对图像增强的方法可表述为将在(x,y)处的灰度f映射为g。在灰度直方图均衡化处理中对图像的映射函数可定义为:g = EQ (f),这个映射函数EQ(f)必须满足两个条件(其中L为图像的灰度级数):

(1)EQ(f)在0≤f≤L-1范围内是一个单值单增函数。这是为了保证增强处理没有打乱原始图像的灰度排列次序,原图各灰度级在变换后仍保持从黑到白(或从白到黑)的排列。

(2)对于0≤f≤L-1有0≤g≤L-1,这个条件保证了变换前后灰度值动态范围的一致性。

这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。这种方法的一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。这种方法的一个缺点是它对处理的数据不加选择,它可能会增加背景杂讯的对比度并且降低有用信号的对比度;变换后图像的灰度级减少,某些细节消失;某些图像,如直方图有高峰,经处理后对比度不自然的过分增强。


2.滤波:图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。目的:1、消除图像中混入的噪声;2、为图像识别抽取出图像特。要求:1、不能损坏图像轮廓及边缘 ;2、图像视觉效果应当更好。

 

滤波和模糊的区别:拿高斯滤波来举例:滤波一般可以分为高通滤波和低通滤波,对于高斯低通滤波就会产生模糊效果,如果对于高斯高通滤波就会产生锐化的效果。所以通常是:高斯滤波就是指使用高斯函数进行滤波;高斯模糊就是指低通滤波。

高通:边缘增强、边缘提取 ;低通:钝化图像、去除噪音 ;带通:删除特定频率、增强中很少用

2.1.线性滤波器:在图像处理中,对邻域中的像素的计算为线性运算时,如利用窗口函数进行平滑加权求和的运算,或者某种卷积运算,都可以称为线性滤波。常见的线性滤波有:均值滤波、高斯滤波、盒子滤波、拉普拉斯滤波等等,通常线性滤波器之间只是模版系数不同。

均值滤波(normalized box filter):用其像素点周围像素的平均值代替元像素值,在滤除噪声的同时也会滤掉图像的边缘信息。在OpenCV中,可以使用boxFilter和blur函数进行均值滤波。均值滤波的核为

 

高斯滤波(Gaussian filter):高斯滤波为最常用的滤波器,具有可分离性质,可以把二维高斯运算转换为一维高斯运算,其本质上为一个低通滤波器。在OpenCV中可通过函数GaussianBlur进行操作。

 

2.2. 非线性滤波:非线性滤波利用原始图像跟模版之间的一种逻辑关系得到结果,如最值滤波器,中值滤波器。比较常用的有中值滤波器和双边滤波器。

中值滤波(median filter):中值滤波用测试像素周围邻域像素集中的中值代替原像素。中值滤波去除椒盐噪声和斑块噪声时,效果非常明显。在OpenCV中,可以使用函数medianBlur进行操作。

 

双边滤波(bilateral filter):双边滤波在平滑图像时能够很好的保留边缘特性,但是其运算速度比较慢。在OpenCV中,可以使用函数bilateralFilter进行操作。

w(x,y)为加权系数,取决于定义域核和值域核的乘积。

3边缘检测:Edge Detection 我们要找水平的边缘:需要注意的是,这里矩阵的元素和是0,所以滤波后的图像会很暗,只有边缘的地方是有亮度的。边缘是图像中灰度发生急剧变化的区域边界。图像灰度的变化情况可以用图像灰度分布的梯度来表示,数字图像中求导是利用差分近似微分来进行的,实际上常用空域微分算子通过卷积来完成。

一般图像边缘检测方法主要有如下四个步骤:

1)图像滤波:传统边缘检测算法主要是基于图像强度的一阶和二阶导数,但导数的计算对噪声很敏感,因此必须使用滤波器来改善与噪声有关的边缘检测器的性能。需要指出的是,大多数滤波器在降低噪声的同时也造成了了边缘强度的损失,因此,在增强边缘和降低噪声之间需要一个折衷的选择。

2)图像增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将邻域(或局部)强度值有显著变化的点突显出来。边缘增强一般是通过计算梯度的幅值来完成的。

3)图像检测:在图像中有许多点的梯度幅值比较大,而这些点在特定的应用领域中并不都是边缘,所以应该用某种方法来确定哪些点是边缘点。最简单的边缘检测判断依据是梯度幅值。

4)图像定位:如果某一应用场合要求确定边缘位置,则边缘的位置可在子像素分辨率上来估计,边缘的方位也可以被估计出来。近20多年来提出了许多边缘检测算子。

一阶导数算子

1)Roberts算子:是一种斜向偏差分的梯度计算方法,梯度的大小代表边缘的强度,梯度的方向与边缘的走向垂直。Roberts操作实际上是求旋转45度两个方向上微分值的和。定位精度高,在水平和垂直方向的效果好,但对噪声敏感。两个卷积核Gx、Gy分别为

                      

采用1范数衡量梯度的幅度为=如果梯度G大于某一阀值 则认为该点(x,y)为边缘点。

2)Sobel算子:是一组方向算子,从不同的方向检测边缘。Sobel算子不是简单的求平均再差分,而是加强了中心像素上下左右4个方向像素的权值,运算结果是一副边缘图。Sobel算子通常对灰度渐变和噪声较多的图像处理的比较好。两个卷积核Gx、Gy分别为:

                               

采用范数衡量梯度的幅度为如果梯度G大于某一阀值 则认为该点(x,y)为边缘点。

3)Prewitt算子:是一种边缘样板算子,利用像素点上下左右邻点灰度差,在边缘处达到极值检测边缘,对噪声具有平滑的作用。由于边缘点像素的灰度值与其邻域点的灰度值显著不同,在实际应用中通常采用微分算子和模板匹配的方法检测图像的边缘。Prewitt算子不仅能检测边缘点,而且能抑制噪声的影响,因此对灰度和噪声较多的图像处理得比较好。两个卷积核Gx、Gy分别为:

                     

采用范数衡量梯度的幅度为:如果梯度G大于某一阀值 则认为该点(x,y)为边缘点。

二阶导数算子也可以检测边缘,利用二阶导数算子检测阶梯状边缘需将检测算子与图像卷积并确定过零点。

1)  Laplacian算子:拉普拉斯算子是一种常用的二阶导数算子。实际中可根据二阶导数算子过零点的性质来确定边缘的位置。对于一个连续函数f(x,y),它在位置(x,y)的拉普拉斯值定义如下:

在图像中,计算函数的拉普拉斯值也可借助各种模板实现。这里对模板的基本要求是对应中心像素的系数应是正的,而对应中心像素邻近像素的系数应是负的,且它们的和应该是零。拉普拉斯算子检测方法常常产生双像素边界,而且这个检测方法对图像中的噪声相当敏感,不能检测边缘的方向,所以很少直接使用拉普拉斯算子进行边缘检测。常用的两种模板分别如图所示:

                   

2)LOG算法:

LOG算法步骤如下:1、 取样得到的高斯低通滤波器对输入图像滤波。2、 计算第一步得到图像的拉普拉斯。3、 找到步骤2所得图像的零交叉。

3)  Canny算子:Canny算子把边缘检测问题转换为检测单位函数极大值的问题来考虑。他利用高斯模型,借助图像滤波的概念指出一个好的边缘检测算子应该具有3个指标:1.低失误率,既要少将真的边缘丢弃,也要少将非边缘判为边缘;2.高位置精度,检测出的边缘应在真正的边界上;3.单像素边缘,即对每个边缘有唯一的响应,得到的边界为单像素宽。

考虑到上述三个条件,Canny提出了判定边缘检测算子的3个准则:信噪比准则、定位精度准则和单边缘响应准则。

1. 信噪比准则:信噪比越大,提取的边缘质量越高。信噪比SNR定义为

 

2.  定位精度准则边缘定位的精度L定义如下

其中,G’(x)和h’(x)分别是G(x)和h(x)的导数。L越大表明定位精度越高。

3. 单边缘响应准则:为了保证单边缘只有一个响应。检测算子的脉冲响应导数的零交叉点的平均距离D(f’)应满足

满足上述三个条件的算子称为Canny算子。Canny边缘检测算法的步骤如下:

(1) 用高斯滤波器平滑图像;(2)用一阶偏导的有限差分来计算梯度的幅值和方向;(3)对梯度幅值进行非极大值抑制;(排除非边缘像素,仅仅保留细线条)(4)用双阈值算法进行检测和链接边缘。

Robert:边缘定位精度较高,对于陡峭边缘且噪声低的图像效果较好,但没有进行平滑处理,没有抑制噪声的能力。

sobel和prewitt:进行了平滑处理,对噪声具有一定抑制能力,但容易出现多像素宽度。精度不高,边缘较粗糙。

Laplacian:对噪声较为敏感,使噪声能力成分得到加强,容易丢失部分边缘方向信息,造成一些不连续的检测边缘,抗噪声能力较差。

log:抗噪声能力较强,但会造成一些尖锐的边缘无法检测到。

canny:最优化思想的边缘检测算子,同时采用高斯函数对图像进行平滑处理,但会造成将高频边缘平滑掉,造成边缘丢失,采用双阈值算法检测和连接边缘。

 

4.形态学:在特殊领域运算形式——结构元素(Sturcture Element),在每个像素位置上与二值图像对应的区域进行特定的逻辑运算。运算结构是输出图像的相应像素。运算效果取决于结构元素大小内容以及逻辑运算性质。形态学,即数学形态学(mathematical Morphology),是图像处理中应用最为广泛的技术之一,主要用于从图像中提取对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质〈最具区分能力-most discriminative)的形状特征,如边界和连通区域等。同时像细化、像素化和修剪毛刺等技术也常应用于图像的预处理和后处理中,成为图像增强技术的有力补充。

膨胀、腐蚀、开、闭运算是数学形态学最基本的变换。

结构元素:简单地定义为像素的结构(形状)以及一个原点(又称为锚点),使用形态学滤波涉及对图像的每个像素应用这个结构元素,当结构元素的原点与给定的像素对齐时,它与图像相交部分定义了一组进行形态学运算的像素。原则上,结构元素可以是任何形状,但通常使用简单的形状,比如方形、圆形和菱形,而原点位于中心位置(基于效率的考虑)。设有两幅图像A, S。若A是被处理的对象, 而S是用来处理A的, 则称S为结构元素。结构元素通常都是一些比较小的图像, A与S的关系类似于滤波中图像和模板的关系.

腐蚀和膨胀两个滤波操作也运算在每个像素周围像素集合上(邻域),这是由结构元素定义的。当应用到一个给定的像素时,结构元素的锚点与该像素的位置对齐,而所有与他相交的像素都被包括在当前像素集合中。腐蚀替换当前像素为像素集合中找到的最小的像素值,而膨胀则替换为像素集合中找到的最大像素值。当然,对于二值图像,每个像素只能被替换为白色像素或黑色像素

腐蚀和膨胀的主要功能:1)消除噪声(2)分割出独立的图像元素,在图像中连接相邻的元素(3)寻找图像中的极大值或者极小值区域(4)求出图像的梯度。

4.1、膨胀(dilate):膨胀就是求局部最大值的操作。从数学角度来说,就是将图像与核进行卷积,计算核B覆盖区域的像素点的最大值,并把这个最大值赋值给参考点指定的元素。这样就会使图像中的高亮区域逐渐增长。模板和输入图像对应位置的元素只要有一个与的结果不为0,则结果不为0.给图像中的对象边界添加元素。用3x3的结构元素,扫描二值图像中的每一个像素,用结构元素与其覆盖的二值图像做与运算,如果都为0,则结果图像中值为0,否则为1。结果:输入图像中的前景对象扩大一圈。

膨胀的作用和腐蚀相反, 膨胀能使物体边界扩大, 具体的膨胀结果与图像本身和结构元素的形状有关。膨胀常用于将图像中原本断裂开来的同一物体桥接起来, 对图像进行二值化之后, 很容易使一个连通的物体断裂为两个部分, 而这会给后续的图像分析(如要基于连通区域的分析统计物体的个数〉造成困扰,此时就可借助膨胀桥接断裂的缝隙

4.2、腐蚀(erode):腐蚀和膨胀是相反的操作,腐蚀是求局部最小值的操作。腐蚀操作会使图像中的高亮区逐渐减小。模板和输入图像中对应位置的元素相与的结果全不为0时,结果才为0。删除对象边界的某些像素。用3x3的结构元素,扫描二值图像的每一个像素,用结构元素与其覆盖的二值图像做与运算,如果都为1,则结果图像中值为1,否则为0.结果:前景对象减小一圈。

随着腐蚀结构元素的逐步增大,小于结构元素的物体相继消失。由于腐蚀运算具有上述的特点,可以用于滤波。选择适当大小和形状的结构元素,可以滤除掉所有不能 完全包含结构元素的噪声点。然而,利用腐蚀滤除噪声有一个缺点,即在去除噪声点的同时,对图像中前景物体的形状也会有影响,但当我们只关心物体的位置或者个数时,则影响不大。

4.3、开运算:开运算是先腐蚀后膨胀。主要用于消除小物体,在纤细点处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积,同时抑制比结构元小的亮细节。

4.4、闭运算:是先膨胀后腐蚀。用来填充物体内细小空洞、连接邻近物体、平滑其边界的同时并不明显改变其面积,同时抑制比结构元小的暗细节。

4.5、形态学梯度:就是将膨胀图和腐蚀图相减。对二值化图像进行这一操作可以将边缘突出来,可以使用形态学梯度来保留物体的边缘轮廓。

4.6、顶帽变换:就是用源图像减去开运算图像。因为开运算带来的结果是放大了裂缝或者局部低亮度的区域。因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围区域更明亮的区域。顶帽一般用于校正不均匀光照的影响(补充:均匀光照在从背景中提取目标的处理中扮演核心的角色)。

4.7、黑帽变换:就是用闭运算减去源图像。黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域。黑帽运算一般用来分离比邻近点暗一些的斑块。

4.8、为什么开运算可以去除目标外的孤立点?

目标外的孤立点是和目标像素值一样的点,而非背景像素点,即为1而非0(0表示选取的空洞或背景像素值)。

使用腐蚀,背景扩展,该孤立点被腐蚀掉,但是腐蚀会导致目标区域缩小一圈,因此需要再进行膨胀操作,将目标区域扩展回原来大小。所以,要使用开运算去除目标外的孤立点。

4.9、为什么闭运算可以去除目标内的孔?

目标内的孔,属于周围都是值为1,内部空洞值为0.目的是去除周围都是1的像素中间的0值。

闭运算首先进行膨胀操作,目标区域扩张一圈,将目标区域的0去除,但是目标区域同时也会向外扩张一圈,因此需要使用腐蚀操作,使得图像中的目标区域恢复到之前的大小。

 

5、漫水填充:将与种子点相连的像素相近的连通域替换成指定颜色。如果存在mask,不会填充mask的非零像素。比如边缘检测输出图像可作为mask图,操作的结果总是某个连续的区域。

作用:1、标记或分离目标区域;2、获取掩码区域,只处理掩码指定的像素点,加速处理过程。

 

6、图像金字塔:是图像中多尺度表达的一种,最主要用于图像分割,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔式一系列以金字塔形状排列的,分辨率逐步降低且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到到达某个终止条件才停止采样。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。层级越高图像越小,分辨率越低。

 通常有两种类型的图像金字塔,分别是:

1)数字金字塔(Gaussuan pyramin)-用来向下采样,主要的图像金字塔,主要用来向下采样图像

2)拉普拉斯金字塔(Laplaican pyramid)-用来从金字塔底层图像重建上层未采样的还原,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用,是从金字塔底层图像中向上采样,重建一个图像。

要从金字塔的第i层生成第i+1层(将第i层表示为Gi),先要用高斯核对Gi进行卷积,然后删除所有偶数行和列,新得到的图像面积会变为源图像的1/4.按上述过程对输入图像G0执行操作就可以得到整个金字塔。

当图像金字塔的上层移动时,尺寸和分辨率会降低。在OpenCV中,从金字塔上一层图像生成下一级图像时可以使用PryDown,而通过PryUp将现有的图像在每个维度上放大两倍。

注意:PryDown和PryUp函数互逆的,PryUp不是降采样的逆操作。图像首先在每个维度上扩大为原来的两倍,新增的行(偶数行)以0填充,然后给指定的滤波器进行卷积(实际上是在每个维度都扩大为原来两倍的过滤器)去估计“丢失”像素的近似值。

6.1.高斯金字塔

高斯金字塔是通过高斯平滑和亚采样获得一些采样图像,即是第K层高斯金字塔通过平滑、亚采样就可以获得第K+1层高斯图像。高斯金字塔包括一些列的低通滤波器,其截止频率从上一层到下一层以因子2逐渐增加,所以高斯金字塔可以跨越很大的频率范围。

     a.对图像向下采样

    为了获得第G(i+1)的金字塔图像,采取如下方法:

         (1)对图像G(i)进行高斯内核卷积

         (2)将所有偶数列和偶数行去除

  得到的图像即为G(i+1)的图像,很明显G(i+1)只有源图像的四分之一,通过对输入图像G(i)(原始图像)不停的迭代上述步骤就会得到整个金字塔,即向下采样会丢失图像的信息,缩小了图像

     b.对图像的向上采样

如果想放大图像,则需要通过向上取样操作得到

       (1)将图像在每个方向上扩大为原来的两倍,新增的行和列以0填充

       (2)使用先前同样的内核(乘以4)与放大后的图像卷积,获得“新增像素”的近似值。

得到的图像即为放大后的图像,但是与源图像想必会发现比较模糊,因为在缩放中已经丢失了一些信息。如果想在缩放过程中减少信息的丢失,这些数据就形成了拉普拉斯金字塔。

6.2.拉普拉斯金字塔

  i层的拉普拉斯金字塔的数学定义为:

                   L(i) = G(i) -UP(G(i+1))&g

式中的G(i)表示第i层的图像,UP()操作是将源图像中位置为(x,y)的像素映射到目标图像的(2X+1,2Y+1)位置,即在进行向上取样,&表示卷积,g为5*5的高斯内核.

使用OpenCV中函数直接进行拉普拉斯运算: L(i) = G(i) -PryUP(G(i+1))

图像金字塔的一个重要应用就是图像分割

6.3.尺寸调整:resize()函数

     resize()函数是OpenCV中专门用来调整图像大小的函数

     此函数将源图像精确的转换为指定尺寸的目标图像。如果源图像中设置了ROI(Region Of Internet,感兴趣区域),那么resize()函数会对源图像的ROI区域进行调整图像尺寸的操作,来输出到目标图像中。若目标中已经设置了ROI区域,不难理解resize()函数将会对源图像进行尺寸调整并填充到目标图像的ROI中。

 

7二值化

7.1全局二值化:一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,最常用的方法就是设定一个全局的阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。将大于T的像素群的像素值设定为白色(或者黑色),小于T的像素群的像素值设定为黑色(或者白色)。

全局二值化,在表现图像细节方面存在很大缺陷。为了弥补这个缺陷,出现了局部二值化方法。

局部二值化的方法就是按照一定的规则将整幅图像划分为N个窗口,对这N个窗口中的每一个窗口再按照一个统一的阈值T将该窗口内的像素划分为两部分,进行二值化处理。

7.2局部自适应二值化:局部二值化也有一个缺陷。存在于那个统一阈值的选定。这个阈值是没有经过合理的运算得来,一般是取该窗口的平局值。这就导致在每一个窗口内仍然出现的是全局二值化的缺陷。为了解决这个问题,就出现了局部自适应二值化方法。

局部自适应二值化,该方法就是在局部二值化的基础之上,将阈值的设定更加合理化。该方法的阈值是通过对该窗口像素的平均值E,像素之间的差平方P,像素之间的均方根值Q等各种局部特征,设定一个参数方程进行阈值的计算,例如:T=a*E+b*P+c*Q,其中a,b,c是自由参数。这样得出来的二值化图像就更能表现出二值化图像中的细节。

根据阈值选取的不同,二值化的算法分为固定阈值和自适应阈值。 比较常用的二值化方法则有:双峰法、P参数法、迭代法和OTSU法等

 

8、霍夫变换:霍夫变换是图像处理中用来从图像中分离出具有某种相同特征的几何形状(通常,直线,圆等)的常用方法。经典的霍夫变换常用来检测直线,圆,椭圆等。它是利用图像全局特性而将边缘像素连接起来组成区域封闭边界的一种方法。在预先知道区域形状的条件下,利用霍夫变换可以方便地得到边界曲线而将不连续的边缘像素点连接起来。霍夫变换的主要优点是受噪声和曲线间断的影响小。利用霍夫变换还可以直接检测某些已知形状的目标。

 

9.重映射:把一个图像中一个位置的像素放置到另一个图片指定位置的过程.简单的说就是改变图片的位置(左,右,上,下,颠倒)。

为了完成映射过程, 有必要获得一些插值为非整数像素坐标,因为源图像与目标图像的像素坐标不是一一对应的.

10、仿射变换: 仿射变换(Affine Transformation)是空间直角坐标系的变换,从一个二维坐标变换到另一个二维坐标,仿射变换是一个线性变换,他保持了图像的“平行性”和“平直性”,即图像中原来的直线和平行线,变换后仍然保持原来的直线和平行线,仿射变换比较常用的特殊变换有平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和剪切(Shear)。

 

11、图像匹配:目前图像匹配中,局部特征匹配占据了绝大部分,常用的局部特征匹配方法有Harris、SIFT、SURF、ORB等等,不同的特征点检测和匹配方法尤其独特的优势和不足;

特征点匹配经过Ransac算法优化后仍存在错误匹配点对,需要优化后的匹配结果进行量化评价;

特征点检测和匹配评价一般包括两个部分,分别为检测和匹配的评价。

1.  特征检测(feature detection)、特征提取(extraction)和匹配(matching) 这三步,可以看做是目标检测、图像配准和拼接等工作的非常重要的一步。

2.  特征检测、特征选择、特征提取、特征描述和特征匹配

       特征检测: 根据用户的需求在图像中寻找满足定义的特征,包括角点、Blob点和边缘点。检测的结果:有或没有。

       特征选择: 为了选择稳定和可靠的特征,在检测到的特征集合中,需要进一步约束,通过类似于非极大值抑制、对比度阈值约束等条件保留显著特征。选择的结果:特征子集。

       特征提取: 特征选择确定稳定可靠的特征子集后,需要提取特征的位置(Location)、方向(Orientation)和尺度(Scale)信息。方向和尺度信息主要是为支持旋转和尺度变化。

       特征描述: 结合特征(点)邻域信息,使用一定的描述规则来对特征区域进行量化并抽取能代表该特征的描述信息,为了后续的匹配,一般用特征向量(feature vector)表示。

      特征匹配: 对提取到的特征,需要通过使用一定的方法来进一步判断对应的特征是否相同(或近似),对特征向量一般使用欧式距离或最邻近距离比(NNDR)进行判定,满足一定的条件约束,则认为两个特征相近,否则剔除。一般还会通过RANSAC进一步约束剔除误匹配点。   

 

 

 

2019-12-31 18:10:14 qq_33628827 阅读数 262
  • 人眼检测

    学会自己查阅opencv doxygen文档 学会编写opencv代码实现简单图像处理变换 初步了解图像算法及机器视觉基础 运用计算机视觉相关知识和opencv库来构建简单的应用程序。

    168人学习 金圣韬
    免费试看

打开halcon,按下ctrl+e打开halcon自带例程。工业领域->橡胶,人造材料,金属薄片->fin.hdev

* fin.hdev: Detection of a fin
* 
dev_update_window ('off')
read_image (Fins, 'fin' + [1:3])
get_image_size (Fins, Width, Height)
dev_close_window ()
dev_open_window (0, 0, Width[0], Height[0], 'black', WindowID)
set_display_font (WindowID, 14, 'mono', 'true', 'false')
for I := 1 to 3 by 1
    select_obj (Fins, Fin, I)
    dev_display (Fin)
    *二值化处理,选中“亮”的那部分区域
    binary_threshold (Fin, Background, 'max_separability', 'light', UsedThreshold)
    dev_set_color ('blue')
    dev_set_draw ('margin')
    dev_set_line_width (4)
    dev_display (Background)
    disp_continue_message (WindowID, 'black', 'true')
    stop ()
    *闭运算,为什么闭运算能把那个“翼”形去掉,闭运算是先膨胀,后腐蚀。所谓膨胀就是把黑色边缘部分用白色替换,
    *当这个用作运算的卷积核足够大,白色就把那个黑色的区域整个覆盖掉了
    *腐蚀是为了把原来的边缘还原
    closing_circle (Background, ClosedBackground, 250)
    dev_set_color ('green')
    dev_display (ClosedBackground)
    disp_continue_message (WindowID, 'black', 'true')
    stop ()
    *比较两幅图像,选中两张图像中不相同的区域
    difference (ClosedBackground, Background, RegionDifference)
    *开运算
    opening_rectangle1 (RegionDifference, FinRegion, 5, 5)
    dev_display (Fin)
    dev_set_color ('red')
    dev_display (FinRegion)
    *获取区域中心位置坐标
    area_center (FinRegion, FinArea, Row, Column)
    if (I < 3)
        disp_continue_message (WindowID, 'black', 'true')
        stop ()
    endif
endfor

毛刺图片

剔除部分