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

来源:https://www.cnblogs.com/powerzzjcode/archive/2019/11/21/11908817.html
-Advertisement-
Play Games

介紹:大三上做一個醫學影像識別的項目,醫生在原圖上用紅筆標記病竈點,通過記錄紅色的坐標位置可以得到病竈點的外接矩形,但是後續會涉及到紅圈內的面積在外接矩形下的占比問題,有些外接矩形內有多個紅色標記,在使用網上的opencv的fillPoly填充效果非常不理想,還有類似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])

 

 

 

 

 

 

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 進程管理控制 這裡實現的是一個自定義timer用於統計子進程運行的時間。使用方式主要是 例如要統計 的運行時間可以直接輸入 ,其後的 是指所要運行的程式的參數。如: 。如果要指定程式運行多少時間,如5秒鐘,可以輸入 。需要註意的是,該程式對輸入沒有做異常檢測,所以要確保程式輸入正確。 Linux 程 ...
  • 線程狀態概述: 當線程被創建並啟動以後,它既不是一啟動就進入了執行狀態,也不是一直處於執行狀態。線上程的生命周期中, 有幾種狀態呢?在API中 java.lang.Thread.State 這個枚舉中給出了六種線程狀態 Timed Waiting(計時等待) Timed Waiting在API中的描 ...
  • 連接上imap服務後,什麼都不操作,我測試大約5分鐘會被服務端斷掉,測試代碼如下 為了保持住這條連接,每隔10秒列取一下郵件夾列表,這樣就可以一直保持住連接了。開三個視窗,一個視窗不停的netstat查看tcp連接情況,一個視窗運行代碼,一個視窗打開tcpdump監聽埠查看數據請求 while t ...
  • 本文介紹,PHP運行在FastCGI模式時,FPM提供的方法:fastcgi_finish_request。在說這個方法之前,我們先瞭解PHP有哪些常用的運行模式? PHP運行模式CGI 通用網關介面 / Common Gateway InterfaceCGI已經是比較老的模式了,這幾年都很少用了。 ...
  • 反射 反射的基本介紹 17.3.1 基本介紹 1) 反射可以在運行時 動態獲取變數的各種信息, 比如變數的類型(type),類別(kind) 2) 如果是結構體變數,還可以獲取到結構體本身的信息(包括結構體的 欄位、 方法) 3) 通過反射,可以修改變數的值,可以調用關聯的方法。 4) 使用反射,需 ...
  • 技術:Java;JSP;Struts2 spring hibernate資料庫: mysqlweb伺服器:tomcat集成開發工具: MyEclipse2014基於SSH的嬰幼兒產品銷售系統主要實現基本的網路購物的功能。本系統結構如下:1,游客訪問 |--系統首頁,查看所有的商品信息和相關的菜單信息 ...
  • 安裝Homebrew 如果已經安裝可以忽略,沒有安裝的請查看小明之前寫好的文章 "mac安裝homebrew" 使用Homebrew安裝Redis (1) 安裝命令 (2) 查看軟體安裝及配置文件位置 Homebrew安裝的軟體會預設在 路徑下; redis的配置文件 存放在 路徑下。 (3) 啟動 ...
  • Mapper映射文件 mapper.xml映射文件主要是用來編寫SQL語句的,以及一些結果集的映射關係的編寫,還有就是緩存的一些配置等等。 在映射文件裡面可以配置以下標簽: | 元素名稱 | 描述 | 備註 | | | | | | select | 查詢語句,最常用、最複雜的元素之一 | 可以自定義 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...