介绍:大三上作一个医学影像识别的项目,医生在原图上用红笔标记病灶点,经过记录红色的坐标位置能够获得病灶点的外接矩形,可是后续会涉及到红圈内的面积在外接矩形下的占比问题,有些外接矩形内有多个红色标记,在使用网上的opencv的fillPoly填充效果很是不理想,还有相似python计算任意多边形方法也不理想的状况下,本身探索出的一种效果还不错的计算多圈及不规则图形的面积的算法。python
医生提供的病灶标记图和原图,大部分长这样算法
但也有一些多圈状况app
很明显,这些图片都是很是须要计算面积占比的,对样本须要筛选函数
经过百度,用opencv的填充来计算面积,一部分效果不好,单圈画不全,多圈都是错(用将面积计算结果上色,方便观察)spa
经过此算法以后,不管单圈,多圈,面积计算准确度提升许多3d
能较为准确的计算出不规则图形的面积code
正文:算法的思想很简单,遍历图片每一列,经过色差判断是否遇到标记圈,将坐标所有记录,对每一列的坐标都进行最小行和最大行记录,肯定每一列的最小和最大的坐标,而后上色(相似opencv的fillPoly的实现,可是细节有些区别),只是这样效果并很差,将图片旋转90度,再作一边,将两个图片的结果放在一块儿作与操做,获得结果就能很好的处理多圈的标记问题和多算面积的问题(好比上面的08-LM),blog
算法实现图片
全程只用pillow库get
首先先用屏幕拾色器获取目标颜色的rgb值,我这种状况下就是(237,28,36),前期截取外接矩形也是要这一步的,颜色也一致
1 def pixel_wanted(pix): 2 return pix==(237,28, 36)
每一列都设定翻转位初始为False,若是上一个像素点不是目标色,当前是目标色则开始记录,一旦不是目标色,中止检测
top_Pixel都设定为黑色(0,0,0)由于有图片最上方就是目标色,致使断定出问题,直接让最上面的像素初始化是黑色
coordinate_List记录了全部符合的点坐标
1 coordinate_List = [] 2 top_Pixel = (0,0,0) 3 for x in range(im.size[0]): 4 flag = False #初始化每一列翻转位为False 5 for y in range(im.size[1]): 6 current_pixel = im.getpixel((x,y)) 7 last_pixel = im.getpixel((x,y-1)) if y>0 else top_Pixel 8 #翻转断定 9 if pixel_wanted(current_pixel) and \ 10 not pixel_wanted(last_pixel): 11 flag = True 12 if flag and not pixel_wanted(current_pixel): 13 flag = False 14 if(flag): 15 coordinate_List.append((x,y))
coordinate_List中的点以下图
而后就是将上面得到coordinate列表进行处理
将coordinate列表中每一列的最小坐标和最大坐标进行记录
由于每一列记录的数量并不肯定(应该能够在上一步改进一下),因此须要遍历屡次
首先找到第一个列出现的坐标,将它的行信息记录(行信息最小肯定),
而后遍历出所有的同列的坐标,比较行坐标,若是大的就将最大的代替(行信息最大肯定),用一个新的列表记录数据
1 coordinate_Min_Max_List = [] 2 #找最小最大 3 for i in range(im.size[0]): 4 min=-1 5 max=-1 6 for coordinate in coordinate_List: 7 if coordinate[0] == i: 8 min = coordinate[1] 9 max = coordinate[1] 10 break 11 for coordinate in coordinate_List: 12 if coordinate[0] == i: 13 if coordinate[1]>max: 14 max = coordinate[1] 15 coordinate_Min_Max_List.append(min) 16 coordinate_Min_Max_List.append(max)
其中要将min和max都初始化为一个坐标不存在的值好比-1,为了在下一步多圈且有空隙状况下,不会出现残影现象,以下图
上一步的最后获得一个列表,第n列的最小行和最大行分别是第2n和2n+1元素,结果中的-1,为了让下一步不会画进去
而后就是绘制图片了,每一列将列表中对应的最小行到最大行涂满
1 #上色 2 for x in range(im.size[0]): 3 for y in range(im.size[1]): 4 min = coordinate_Min_Max_List[x*2] 5 max = coordinate_Min_Max_List[x*2+1] 6 if min<y<max: 7 im.putpixel((x,y),(0,255,0)) 8 else: 9 #能够把非红圈的上掩膜遮住 10 pass
至此,就是相似opencv的算法实现,虽然还差翻转作与操做,可是已经比opencv生成的效果好,写成函数后续调用,
而后就是简单的翻转90度,再调用一次这个函数再作一遍
1 def Cal_S(im): 2 im_0 = im.rotate(0) 3 im_90 = im.rotate(90, expand=True) 4 5 im_0 = fillPoly(im_0) 6 im_90 = fillPoly(im_90) 7 im_90 = im_90.rotate(-90, expand=True) 8 9 i=0 10 for x in range(im.size[0]): 11 for y in range(im.size[1]): 12 if(im_0.getpixel((x,y))==(0,255,0) and 13 im_90.getpixel((x,y))==(0,255,0)): 14 im.putpixel((x,y),(0,255,0)) 15 i+=1 16 return i/(im.size[0]*im.size[1])
作两遍的效果图
能够看到效果很是不错,可是依旧有个别图像有问题,好比十字分布的,
但如今的话偏差已经下降很是多了,这些极其个别的十字现象能够手动把原图切割一下,或者干脆不处理了
全部代码,画出绿图片为了方便直观的查看,函数中能够把图片顺便保存一下,整体看一下效果
1 from PIL import Image 2 3 def pixel_wanted(pix): 4 return pix==(237,28, 36) 5 6 def fillPoly(im): 7 coordinate_List = [] 8 9 top_Pixel = (0,0,0) 10 for x in range(im.size[0]): 11 flag = False #初始化每一列翻转位为False 12 for y in range(im.size[1]): 13 current_pixel = im.getpixel((x,y)) 14 last_pixel = im.getpixel((x,y-1)) if y>0 else top_Pixel 15 #翻转断定 16 if pixel_wanted(current_pixel) and \ 17 not pixel_wanted(last_pixel): 18 flag = True 19 if flag and not pixel_wanted(current_pixel): 20 flag = False 21 if(flag): 22 coordinate_List.append((x,y)) 23 coordinate_Min_Max_List = [] 24 #找最小最大 25 for i in range(im.size[0]): 26 min=-1 27 max=-1 28 for coordinate in coordinate_List: 29 if coordinate[0] == i: 30 min = coordinate[1] 31 max = coordinate[1] 32 break 33 for coordinate in coordinate_List: 34 if coordinate[0] == i: 35 if coordinate[1]>max: 36 max = coordinate[1] 37 coordinate_Min_Max_List.append(min) 38 coordinate_Min_Max_List.append(max) 39 #上色 40 for x in range(im.size[0]): 41 for y in range(im.size[1]): 42 min = coordinate_Min_Max_List[x*2] 43 max = coordinate_Min_Max_List[x*2+1] 44 if min<y<max: 45 im.putpixel((x,y),(0,255,0)) 46 else: 47 #能够把非红圈的上掩膜遮住 48 pass 49 return im 50 51 def Cal_S(im): 52 im_0 = im.rotate(0) 53 im_90 = im.rotate(90, expand=True) 54 55 im_0 = fillPoly(im_0) 56 im_90 = fillPoly(im_90) 57 im_90 = im_90.rotate(-90, expand=True) 58 59 i=0 60 for x in range(im.size[0]): 61 for y in range(im.size[1]): 62 if(im_0.getpixel((x,y))==(0,255,0) and 63 im_90.getpixel((x,y))==(0,255,0)): 64 im.putpixel((x,y),(0,255,0)) 65 i+=1 66 return i/(im.size[0]*im.size[1])