算法的整体描述:
1.从上到下,从左到右,依次遍历整幅图像
2.如上图A所示,A点为遇到的外轮廓点(步骤1遍历的过程中遇到的第一个像素值为255的点即为外轮廓点),且没有被标记过,则给A一个新的标记号;从A点出发,按照一定的规则,将A所在的外轮廓点全部跟踪到,然后回到A点,并将路径上的点全部标记为A的标号.
3.如上图B所示,如果遇到已经标记过的点A',则从A'开始向右,将它的右边所有的点都标记为A'的标号,直到遇到像素值为0的点为止.
4.如上图C所示,如果遇到了一个已经标记的点B,且是内轮廓点(它的正下方像素值为0,且不在外轮廓上),则从B开始跟踪内轮廓,将路径上的点全部标记为B的标号,又因为B已经标记过,且和A相同,所以,外轮廓\内轮廓将用相同的标号进行标记.
5.如上图D所示,如果遇到已经标记的内轮廓点B',则从B'开始向右,将它右边所有的点均标记为B'的标号,直到遇到像素值为0的点为止.
6.结束
算法的详细描述:
对于一个需要标记的图像image,定义一个与它相对应的标记图像label,用来保存标记信息,开始将label上的所有像素的值全部置为0,同时设置一个标签变量C,初始化为1.然后开始扫描图像image,遇到像素值为255的点,设置这个点为P点,下面针对不同的情况进行不同的处理:
1)如果P(i,j)点是一个像素值为255的点,在label图像上这个点未被标记,且P点的上方是像素值为0的点,则P点是一个新的外轮廓点,这时将C的标签值标记给label图像上P点的位置(x,y),即label.data[x*width+y]=C,接着沿着P点做轮廓跟踪,将轮廓上的所有点对应的label图像上都标记为C,完成整个轮廓的标记后,将回到P点,最后,将C的值加1.整个标记过程如上图所示.
2)如果P点的下方是unMark(参考步骤3)的点,则P点一定是内轮廓上的点,这时候有两种情况,一种是P点在label上已经被标记过,说明它同时属于外轮廓上的点;另一种情况是P点还没有被标记过,按步骤1)介绍的情况来说,P点的左边一定是一个已经被标记的点,则此时采用P点左边点的标号来标记P点,接着从P点开始来跟踪内轮廓,把内轮廓上的点全部标记为P点的标号.如上图所示,两幅图像分别对应不同的情况,通过左边的那张图可以看出P点既是外轮廓点又是内轮廓点,
3)如果一个点P不是上述两种情况,则它的左边的点一定被标记过,只需要用它左边的标号来标记label上的P点.
下面介绍一下,什么是unMark点,因为内轮廓的开始点P的下方肯定是像素值为0的点,很显然,像素值为0的点并不一定是unMark点,如上图右图所示的Q点,它的下面像素值为0,但Q点并不在内轮廓上.
实际上,在外轮廓跟踪的过程中,我们在外轮廓的周围也做了标记,在轮廓的周围像素点上对应的label图像上被标记为负值,这样Q点的下方就不是一个unMark点,所以,unMark点指的是label图像上没有被修改过的0点.
算法的重点是轮廓的查找与标记,下面将介绍一下外轮廓和内轮廓的查找跟踪规则.
对某一像素点的周围的8个像素点进行分析,做一个标号0-7,因为在遍历图像的过程中,遇到的第一个点肯定是外轮廓点,所以,首先来确定外轮廓点的搜索策略,对于外轮廓点P,有两种情况,
1)如果P是外轮廓的起点,即是从P点开始跟踪的,那么,从P的右上角即7的位置P1开始,看它的像素值是否为255,如果是,则把这个点加入外轮廓点,并将它标记为与P相同的标号,如果它的像素值为0,则按顺序7-0-1-2-3-4-5-6进行搜索,直到遇到像素值为255的点为止,把这个点赋给P1,并加入外轮廓,并把它的标号设置为P的标号.假设2号点位置处的像素值为255,则7-0-1处的点都已经被搜索过,所以将这些点处对应的label图像上标为负值,如下图所示,
2)如果P不是外轮廓的起点,则它肯定是由某一个点进入的,我们设置为P1点,P1点的位置为x则0 <= x <= 7,则P点从(x+2)mod 8开始搜索下一个路径,其中表达式的意思是+2取模,它反应在图像上就是从P1点开始顺时针数2个格子的位置.
外轮廓的跟踪方式,确定之后,开始进行内轮廓的标记,不同之处在于如果P点是内轮廓的开始点,则它的开始搜索位置是从3处而不是7的位置开始的.