1. 程式人生 > 程式設計 >Python計算不規則圖形面積演算法實現解析

Python計算不規則圖形面積演算法實現解析

這篇文章主要介紹了Python計算不規則圖形面積演算法實現解析,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

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

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

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

演算法實現

全程只用pillow庫

首先先用螢幕拾色器獲取目標顏色的rgb值,我這種情況下就是(237,28,36),前期擷取外接矩形也是要這一步的,顏色也一致

 def pixel_wanted(pix):
   return pix==(237,28,36)

每一列都設定翻轉位初始為False,如果上一個畫素點不是目標色,當前是目標色則開始記錄,一旦不是目標色,停止檢測

top_Pixel都設定為黑色(0,0,0)因為有圖片最上方就是目標色,導致判定出問題,直接讓最上面的畫素初始化是黑色

coordinate_List記錄了所有符合的點座標

coordinate_List = []
top_Pixel = (0,0)
for x in range(im.size[0]):
  flag = False #初始化每一列翻轉位為False
  for y in range(im.size[1]):
    current_pixel = im.getpixel((x,y))
    last_pixel = im.getpixel((x,y-1)) if y>0 else top_Pixel
    #翻轉判定
    if pixel_wanted(current_pixel) and \
        not pixel_wanted(last_pixel):
      flag = True
    if flag and not pixel_wanted(current_pixel):
      flag = False
    if(flag):
      coordinate_List.append((x,y))

coordinate_List中的點如下圖

然後就是將上面獲得coordinate列表進行處理

將coordinate列表中每一列的最小座標和最大座標進行記錄

因為每一列記錄的數量並不確定(應該可以在上一步改進一下),所以需要遍歷多次

首先找到第一個列出現的座標,將它的行資訊記錄(行資訊最小確定),

然後遍歷出全部的同列的座標,比較行座標,如果大的就將最大的代替(行資訊最大確定),用一個新的列表記錄資料

coordinate_Min_Max_List = []
#找最小最大
for i in range(im.size[0]):
  min=-1
  max=-1
  for coordinate in coordinate_List:
    if coordinate[0] == i:
      min = coordinate[1]
      max = coordinate[1]
      break
  for coordinate in coordinate_List:
    if coordinate[0] == i:
      if coordinate[1]>max:
        max = coordinate[1]
  coordinate_Min_Max_List.append(min)
  coordinate_Min_Max_List.append(max)

其中要將min和max都初始化為一個座標不存在的值比如-1,為了在下一步多圈且有空隙情況下,不會出現殘影現象,如下圖

上一步的最後得到一個列表,第n列的最小行和最大行分別是第2n和2n+1元素,結果中的-1,為了讓下一步不會畫進去

然後就是繪製圖片了,每一列將列表中對應的最小行到最大行塗滿

#上色
for x in range(im.size[0]):
  for y in range(im.size[1]):
    min = coordinate_Min_Max_List[x*2]
    max = coordinate_Min_Max_List[x*2+1]
    if min<y<max:
      im.putpixel((x,y),(0,255,0))
    else:
      #可以把非紅圈的上掩膜遮住
      pass

至此,就是類似opencv的演算法實現,雖然還差翻轉做與操作,但是已經比opencv生成的效果好,寫成函式後續呼叫,

然後就是簡單的翻轉90度,再呼叫一次這個函式再做一遍

def Cal_S(im):
  im_0 = im.rotate(0)
  im_90 = im.rotate(90,expand=True)
  
  im_0 = fillPoly(im_0)
  im_90 = fillPoly(im_90)
  im_90 = im_90.rotate(-90,expand=True)

  i=0
  for x in range(im.size[0]):
    for y in range(im.size[1]):
      if(im_0.getpixel((x,y))==(0,0) and
      im_90.getpixel((x,0)):
        im.putpixel((x,0))
        i+=1
  return i/(im.size[0]*im.size[1])

做兩遍的效果圖

可以看到效果非常不錯,但是依舊有個別影象有問題,比如十字分佈的,

但現在的話誤差已經降低非常多了,這些極其個別的十字現象可以手動把原圖切割一下,或者乾脆不處理了

所有程式碼,畫出綠圖片為了方便直觀的檢視,函式中可以把圖片順便儲存一下,總體看一下效果

from PIL import Image

def pixel_wanted(pix):
  return pix==(237,36)

def fillPoly(im):
  coordinate_List = []

  top_Pixel = (0,0)
  for x in range(im.size[0]):
    flag = False #初始化每一列翻轉位為False
    for y in range(im.size[1]):
      current_pixel = im.getpixel((x,y))
      last_pixel = im.getpixel((x,y-1)) if y>0 else top_Pixel
      #翻轉判定
      if pixel_wanted(current_pixel) and \
          not pixel_wanted(last_pixel):
        flag = True
      if flag and not pixel_wanted(current_pixel):
        flag = False
      if(flag):
        coordinate_List.append((x,y))
  coordinate_Min_Max_List = []
  #找最小最大
  for i in range(im.size[0]):
    min=-1
    max=-1
    for coordinate in coordinate_List:
      if coordinate[0] == i:
        min = coordinate[1]
        max = coordinate[1]
        break
    for coordinate in coordinate_List:
      if coordinate[0] == i:
        if coordinate[1]>max:
          max = coordinate[1]
    coordinate_Min_Max_List.append(min)
    coordinate_Min_Max_List.append(max)
  #上色
  for x in range(im.size[0]):
    for y in range(im.size[1]):
      min = coordinate_Min_Max_List[x*2]
      max = coordinate_Min_Max_List[x*2+1]
      if min<y<max:
        im.putpixel((x,0))
      else:
        #可以把非紅圈的上掩膜遮住
        pass
  return im

def Cal_S(im):
  im_0 = im.rotate(0)
  im_90 = im.rotate(90,expand=True)

  im_0 = fillPoly(im_0)
  im_90 = fillPoly(im_90)
  im_90 = im_90.rotate(-90,0))
        i+=1
  return i/(im.size[0]*im.size[1])

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。