python計算不規則圖形面積算法


介紹:大三上做一個醫學影像識別的項目,醫生在原圖上用紅筆標記病灶點,通過記錄紅色的坐標位置可以得到病灶點的外接矩形,但是后續會涉及到紅圈內的面積在外接矩形下的占比問題,有些外接矩形內有多個紅色標記,在使用網上的opencv的fillPoly填充效果非常不理想,還有類似python計算任意多邊形方法也不理想的情況下,自己探索出的一種效果還不錯的計算多圈及不規則圖形的面積的算法。

醫生提供的病灶標記圖和原圖,大部分長這樣

 

 

 

 

但也有一些多圈情況

 

 

 

 

 很明顯,這些圖片都是非常需要計算面積占比的,對樣本需要篩選

通過百度,用opencv的填充來計算面積,一部分效果很差,單圈畫不全,多圈都是錯(用將面積計算結果上色,方便觀察)

 

 

 

 

 

通過此算法之后,無論單圈,多圈,面積計算准確度提高許多

 

 

 

 

 能較為准確的計算出不規則圖形的面積

正文:算法的思想很簡單,遍歷圖片每一列,通過色差判斷是否遇到標記圈,將坐標全部記錄,對每一列的坐標都進行最小行和最大行記錄,確定每一列的最小和最大的坐標,然后上色(類似opencv的fillPoly的實現,但是細節有些區別),只是這樣效果並不好,將圖片旋轉90度,再做一邊,將兩個圖片的結果放在一起做與操作,得到結果就能很好的處理多圈的標記問題和多算面積的問題(比如上面的08-LM),

 

 算法實現

全程只用pillow庫

首先先用屏幕拾色器獲取目標顏色的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])

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM