您好,登錄后才能下訂單哦!
這篇文章主要介紹了Python計算不規(guī)則圖形面積算法實現(xiàn)解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
介紹:大三上做一個醫(yī)學(xué)影像識別的項目,醫(yī)生在原圖上用紅筆標記病灶點,通過記錄紅色的坐標位置可以得到病灶點的外接矩形,但是后續(xù)會涉及到紅圈內(nèi)的面積在外接矩形下的占比問題,有些外接矩形內(nèi)有多個紅色標記,在使用網(wǎng)上的opencv的fillPoly填充效果非常不理想,還有類似python計算任意多邊形方法也不理想的情況下,自己探索出的一種效果還不錯的計算多圈及不規(guī)則圖形的面積的算法。
能較為準確的計算出不規(guī)則圖形的面積
正文:算法的思想很簡單,遍歷圖片每一列,通過色差判斷是否遇到標記圈,將坐標全部記錄,對每一列的坐標都進行最小行和最大行記錄,確定每一列的最小和最大的坐標,然后上色(類似opencv的fillPoly的實現(xiàn),但是細節(jié)有些區(qū)別),只是這樣效果并不好,將圖片旋轉(zhuǎn)90度,再做一邊,將兩個圖片的結(jié)果放在一起做與操作,得到結(jié)果就能很好的處理多圈的標記問題和多算面積的問題(比如上面的08-LM),
算法實現(xiàn)
全程只用pillow庫
首先先用屏幕拾色器獲取目標顏色的rgb值,我這種情況下就是(237,28,36),前期截取外接矩形也是要這一步的,顏色也一致
def pixel_wanted(pix): return pix==(237,28, 36)
每一列都設(shè)定翻轉(zhuǎn)位初始為False,如果上一個像素點不是目標色,當(dāng)前是目標色則開始記錄,一旦不是目標色,停止檢測
top_Pixel都設(shè)定為黑色(0,0,0)因為有圖片最上方就是目標色,導(dǎo)致判定出問題,直接讓最上面的像素初始化是黑色
coordinate_List記錄了所有符合的點坐標
coordinate_List = [] top_Pixel = (0,0,0) for x in range(im.size[0]): flag = False #初始化每一列翻轉(zhuǎn)位為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 #翻轉(zhuǎn)判定 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列表中每一列的最小坐標和最大坐標進行記錄
因為每一列記錄的數(shù)量并不確定(應(yīng)該可以在上一步改進一下),所以需要遍歷多次
首先找到第一個列出現(xiàn)的坐標,將它的行信息記錄(行信息最小確定),
然后遍歷出全部的同列的坐標,比較行坐標,如果大的就將最大的代替(行信息最大確定),用一個新的列表記錄數(shù)據(jù)
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,為了在下一步多圈且有空隙情況下,不會出現(xiàn)殘影現(xiàn)象,如下圖
上一步的最后得到一個列表,第n列的最小行和最大行分別是第2n和2n+1元素,結(jié)果中的-1,為了讓下一步不會畫進去
然后就是繪制圖片了,每一列將列表中對應(yīng)的最小行到最大行涂滿
#上色 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的算法實現(xiàn),雖然還差翻轉(zhuǎn)做與操作,但是已經(jīng)比opencv生成的效果好,寫成函數(shù)后續(xù)調(diào)用,
然后就是簡單的翻轉(zhuǎn)90度,再調(diào)用一次這個函數(shù)再做一遍
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,255,0) and im_90.getpixel((x,y))==(0,255,0)): im.putpixel((x,y),(0,255,0)) i+=1 return i/(im.size[0]*im.size[1])
做兩遍的效果圖
可以看到效果非常不錯,但是依舊有個別圖像有問題,比如十字分布的,
但現(xiàn)在的話誤差已經(jīng)降低非常多了,這些極其個別的十字現(xiàn)象可以手動把原圖切割一下,或者干脆不處理了
所有代碼,畫出綠圖片為了方便直觀的查看,函數(shù)中可以把圖片順便保存一下,總體看一下效果
from PIL import Image def pixel_wanted(pix): return pix==(237,28, 36) def fillPoly(im): coordinate_List = [] top_Pixel = (0,0,0) for x in range(im.size[0]): flag = False #初始化每一列翻轉(zhuǎn)位為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 #翻轉(zhuǎn)判定 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,y),(0,255,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, 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,255,0) and im_90.getpixel((x,y))==(0,255,0)): im.putpixel((x,y),(0,255,0)) i+=1 return i/(im.size[0]*im.size[1])
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。