一、背景
????在去年學(xué)習(xí)opencv的過程當(dāng)中,做過一張瓶蓋分割的練習(xí)。目的就是為了分割出每個(gè)瓶蓋,當(dāng)時(shí)想著,除了霍夫圓檢測思路之外,能不能根據(jù)相連瓶蓋的特征進(jìn)行分割呢?于是便想到了根據(jù)角點(diǎn)檢測其相連位置,然后在相連位置之間畫一根線進(jìn)行切除。是不是想法很單純,覺得很好實(shí)現(xiàn)?其實(shí)實(shí)現(xiàn)過程中遇到不少問題,檢測的角點(diǎn)很多,如何過濾掉剩下粘連處的角點(diǎn)?那么多個(gè)角點(diǎn),如何保證點(diǎn)跟另一個(gè)點(diǎn)剛好是相連位置的兩個(gè)點(diǎn)?下面附上代碼的整體實(shí)現(xiàn)思路,本次文章制作簡單的分享,后續(xù)有時(shí)間會更新,逐步簡潔實(shí)現(xiàn)流程以及原理。
二、思路
一、基于****Harrs****的角點(diǎn)檢測
圓形相粘物體之間會存在凹凸區(qū)域、可以通過對相連區(qū)域進(jìn)行角點(diǎn)檢測、或者凹凸點(diǎn)檢測。檢測點(diǎn)是否齊全決定分割的準(zhǔn)確度。為了保證分割效果,角點(diǎn)檢測階段經(jīng)可能檢測多一些角點(diǎn)。二、均值去噪點(diǎn)
對所有點(diǎn)求最近點(diǎn)求最小歐式距離,通過對距離進(jìn)行求均值以及中位數(shù)。其中均值指標(biāo)適用于雜點(diǎn)較多以及距離較大的情況。對于雜點(diǎn)較多,距離較大的情況。通過均值指標(biāo)或者中位數(shù)指標(biāo)能去掉部分雜點(diǎn)。三、ROI取樣
為了更進(jìn)一步篩選掉部分雜點(diǎn),以及更好第判斷兩點(diǎn)之間是否為物體相連部分,因?yàn)橥ㄟ^相連部分的兩個(gè)角點(diǎn),進(jìn)行旋轉(zhuǎn)90°獲得一個(gè)正方向的ROI區(qū)域,假設(shè)ROI區(qū)域?yàn)橄噙B部分,其ROI區(qū)域中的填充率較大。因此利用本特征進(jìn)行ROI取樣。其中只有正方形ROI才能更好地衡量其填充率。四、坐標(biāo)變換
在對圖像坐標(biāo)進(jìn)行運(yùn)算的時(shí)候,需要進(jìn)行坐標(biāo)變換,將坐標(biāo)原點(diǎn)移動(dòng)到圖像的中心。五、旋轉(zhuǎn)矩陣獲取
對獲取到兩個(gè)角點(diǎn),通過坐標(biāo)變換、旋轉(zhuǎn)矩陣的獲取,使圖像中任意點(diǎn)能繞一點(diǎn)進(jìn)行旋轉(zhuǎn)一定角度。六、透視變換
因?yàn)樾枰?jì)算填充率需要進(jìn)行透視變換,求ROI輪廓面積與ROI面積之比,因?yàn)椴煌倪呅蔚慕嵌炔灰粯?,所以需要對所有點(diǎn)進(jìn)行排序,同時(shí)又一種特殊情況就是四邊形對角線如果是豎直的情況,則不需要及進(jìn)行排序,不然會出現(xiàn)矯正錯(cuò)誤。七、填充率計(jì)算
通過取樣的ROI進(jìn)行計(jì)算二值化輪廓面積與ROI面積之比,獲取填充率,設(shè)定一個(gè)值,假如大于閾值,就進(jìn)行分割。
三、代碼
????將下面代碼放在.py文件當(dāng)中,讀取圖片,運(yùn)行之后,便會在當(dāng)前路徑自動(dòng)創(chuàng)建文件夾將所有圖片保存里面。
"""
作者:馮耿鑫
時(shí)間:2021/1/9
功能:對相連的圓形物體進(jìn)行分割
思路:
=>>創(chuàng)新:形態(tài)學(xué)操作的小技巧可以定義一個(gè)卷積核、然后在本卷積核上畫圓,就是一個(gè)圓形的卷積了
=>>基于Harris角點(diǎn)檢測、得出dist圖像,因?yàn)樵俟战翘帟泻芏鄠€(gè)角點(diǎn),為了只求一個(gè),所以進(jìn)行二值化,膨脹,求拐點(diǎn)的形心。
=>>對角點(diǎn)進(jìn)行x方向的排序
=>>進(jìn)行坐標(biāo)變換、以及旋轉(zhuǎn)矩陣求出垂直的另一條直線
=>>進(jìn)行透視變換,矯正ROI區(qū)域,需要通過透視變換來求得,其中ROI的透視變換用到了坐標(biāo)排序,其中需要注意一種對角線豎直的情況,然后求包含物體的飽和率,從而進(jìn)行篩選。
=>>利用連通域進(jìn)行顏色顯示
"""
# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
import cv2
import math
import os
class SegmentationConnectObject(object):
def __init__(self,img,binary):
self.img = img # 原圖
self.binary = binary # 二值化圖片
self.number = 0 # 第幾個(gè)輪廓
self.answer = False # 一開始默認(rèn)是不是相連的
self.H,self.W,self.C = img.shape
# 尋找距離最小的兩個(gè)點(diǎn)
def main_find_mindist_points(self):
"""
:function: 用來尋找兩個(gè)最近的點(diǎn),用來進(jìn)行區(qū)域分析
:return:
"""
"""=>>圓形卷積核進(jìn)行形態(tài)學(xué)操作,消除雜點(diǎn)噪聲、以及光滑變換<<="""
k2 = np.zeros((24, 24), np.uint8) # <==定義一個(gè)卷24x24的卷積核
cv2.circle(k2, (12, 12), 12, (1, 1, 1), -1, cv2.LINE_AA) # <==在這個(gè)卷積核上進(jìn)行畫一個(gè)鵑形的卷積核
open = cv.morphologyEx(self.binary, cv.MORPH_OPEN, k2) # 進(jìn)行開操作,也就是先腐蝕后膨脹
"""=>>利用Harris進(jìn)行角點(diǎn)檢測<<= """
harris = cv2.cornerHarris(open, 2, 5, 0.04) #<== 進(jìn)行角點(diǎn)檢測,blockSize:角點(diǎn)檢測中要考慮的領(lǐng)域大小||ksize - Sobel:求導(dǎo)中使用的窗口大小||k - Harris:角點(diǎn)檢測方程中的自由參數(shù), 取值參數(shù)為[0, 04, 0.06]
harris = cv2.dilate(harris, None) # 對角點(diǎn)進(jìn)行一個(gè)簡單的膨脹、不然的話輪廓會不好尋找
# img[harris > 0.2 * harris.max()] = [0, 0, 255] #<== 通過角點(diǎn)檢測之后只有邊緣像素是有值的,拐角的地方是比較大,所以利用這個(gè)條件進(jìn)行顯示
pix_max = 0.2 * harris.max() # <==獲取選擇角點(diǎn)的閾值
ret, binary_p = cv.threshold(harris, pix_max, 255, cv.THRESH_BINARY) # 因?yàn)檫x擇出來的像素太多了,又類似與輪廓,所以我們直接進(jìn)行閾值分割發(fā)現(xiàn)輪廓
binary_p = np.uint8(binary_p) # 二值化前需要對位數(shù)進(jìn)行轉(zhuǎn)換
contours = cv2.findContours(binary_p, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0] # 發(fā)現(xiàn)角點(diǎn)的輪廓,用來發(fā)現(xiàn)其角點(diǎn)的質(zhì)心
"""=>>找到所有的可能坐標(biāo)點(diǎn)<<= """
points = [] # 用來儲存所有的角點(diǎn)坐標(biāo)
for c in contours: # 橫向
# 獲取矩形框的四個(gè)參數(shù)
mm = cv.moments(c) # 幾何重心的獲取
cx, cy = int(mm['m10'] / mm['m00']), int(mm['m01'] / mm['m00'])
points.append((int(cx), int(cy))) # 將坐標(biāo)保留在points列表中
cv.circle(self.img, (int(cx), int(cy)), 3, (0, 0, 255), -1)
"""=>>對所有的角點(diǎn)排序,方向?yàn)閤從小到大<<= """
points_sorted_x = self.sort_x(points)
# for i, p in enumerate(points_sorted_x):
# cv.circle(self.img, p, 4, (255, 0, 0), -1) # 畫出點(diǎn)
# cv.putText(self.img,str(i) , p, cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 100, 255), 2)
"""=>>因?yàn)橛泻芏嘟屈c(diǎn),如何獲得最小相連的兩個(gè)點(diǎn)呢,通過遍歷所有點(diǎn),獲取最小距離用來求平均值,用來作為指標(biāo)作為閾值,篩選太遠(yuǎn)的點(diǎn)<<= """
points_sorted_x_2 = points_sorted_x.copy() # 因?yàn)橐闅v兩次,所以賦值一份方便后面更改
distance_1 = [] # 所有點(diǎn)都進(jìn)行遍歷、儲存每個(gè)點(diǎn)相連做近的點(diǎn)
for p1 in points_sorted_x: # p1作為父點(diǎn)
x1, y1 = p1 # p1的坐標(biāo)
distance_2 = [] # 用來儲存所有子點(diǎn)p2到p1的距離,然后獲取最小距離給diatance_1
for p2 in points_sorted_x_2: # p2作為父點(diǎn)
x2, y2 = p2 # p2的坐標(biāo)
if x1 == x2 and y1 == y2: # 因?yàn)閮蓚€(gè)列表是一樣的,所以會有遇到相同的點(diǎn),需要跳過,不然distance_2中最小的都是0
continue # 循環(huán)到原來的帶點(diǎn)就不進(jìn)行計(jì)算
else:
l = pow(abs(x1 - x2) ** 2 + abs(y1 - y2) ** 2, 0.5) # 計(jì)算父點(diǎn)與子點(diǎn)的歐式距離
distance_2.append(l) # 將所有歐式距離保存在distance_2中
distance_1.append(min(distance_2)) # 獲取每個(gè)父點(diǎn)到子點(diǎn)的最小歐式距離
mean_dist = np.mean(distance_1) # 這里設(shè)置了兩個(gè)指標(biāo),一個(gè)是平均值,適合密集點(diǎn)
median_dist = np.median(distance_1) # 一個(gè)是中間數(shù),適合雜點(diǎn)較少情況
"""=>>上面根據(jù)模型求出距離指標(biāo),下面將通過設(shè)定閾值進(jìn)行求解<<= """
choose = [] # choose列表使用來記錄已經(jīng)檢測完畢的兩個(gè)點(diǎn),用來判斷,如果沒有檢測成功,就繼續(xù)檢測,如果檢測成功,那就跳過避免重復(fù)檢測
for number, p1 in enumerate(points_sorted_x): # 遍歷父點(diǎn)
x1, y1 = p1 # 父點(diǎn)坐標(biāo)
for p2 in points_sorted_x: # 遍歷子點(diǎn)
x2, y2 = p2 # 子點(diǎn)坐標(biāo)
if x1 == x2 and y1 == y2: # 過濾相同的點(diǎn)
continue
else: # 求歐式距離
l = pow(abs(x1 - x2) ** 2 + abs(y1 - y2) ** 2, 0.5)
if l > mean_dist * 0.3 and l < mean_dist * 1.6: #<<== 設(shè)定約束條件,如果在這個(gè)閾值范圍就可以進(jìn)行后續(xù)的分割功能
if p1 in choose or p2 in choose: # 如果點(diǎn)在choose中就代表這兩個(gè)點(diǎn)已經(jīng)檢測成功
continue
self.check_connect(x1, y1, x2, y2) #<<==判斷是否連接函數(shù)
if self.answer: # 當(dāng)分割成功的時(shí)候,用來記錄p1,p2這兩個(gè)點(diǎn)
choose.append(p1)
choose.append(p2)
self.answer = False # 需要重新賦值,不然邊True之后就會一直默認(rèn)正確
color,result = self.connect_domain()
return self.img,self.binary, open,color,result
# 冒泡排序?qū)屈c(diǎn)坐標(biāo)進(jìn)行排序
def sort_x(self,points):
"""
function:冒泡排序算法實(shí)現(xiàn)對x方向進(jìn)行排序
"""
l = len(points)
for i in range(l - 1):
for j in range(l - 1 - i):
# if points[j][1]>points[j+1][1]:
# temp = points[j]
# points[j] = points[j+1]
# points[j+1] = temp
if points[j][0] > points[j + 1][0]:
temp = points[j]
points[j] = points[j + 1]
points[j + 1] = temp
return points
# 通過旋轉(zhuǎn)矩陣,實(shí)現(xiàn)任一點(diǎn)的旋轉(zhuǎn)
def rota(self,x1, y1, x2, y2):
"""
:function:以任意點(diǎn)為中線,通過坐標(biāo)平移,然后通過旋轉(zhuǎn),再平移回來,最終完成旋轉(zhuǎn)。
:return:
"""
# 獲取直線的中點(diǎn)
cx, cy = (x1 + x2) / 2, (y1 + y2) / 2
# 偏移矩陣
C = np.array([[cx], [cy]])
# 旋轉(zhuǎn)角度
degree = math.radians(90)
# 旋轉(zhuǎn)矩陣
A = np.array([[math.cos(degree), -math.sin(degree)],
[math.sin(degree), math.cos(degree)]])
# 輸入坐標(biāo)
X1 = np.array([[x1], [y1]])
X2 = np.array([[x2], [y2]])
# 進(jìn)行偏移,將中間點(diǎn)轉(zhuǎn)換為中間坐標(biāo)
X1 = X1 - C
X2 = X2 - C
# 利用矩陣的乘積求出旋轉(zhuǎn)坐標(biāo)
Y1 = np.dot(A, X1)
Y2 = np.dot(A, X2)
# 轉(zhuǎn)換絕對坐標(biāo)的形式
Y1 = Y1 + C
Y2 = Y2 + C
out_x1, out_y1, out_x2, out_y2 = int(Y1.ravel()[0]), int(Y1.ravel()[1]), int(Y2.ravel()[0]), int(Y2.ravel()[1])
return out_x1, out_y1, out_x2, out_y2
# 檢查是否為相連物體
def check_connect(self,x1, y1, x2, y2):
"""
:function:檢查這兩個(gè)點(diǎn)是否為相連接的兩個(gè)點(diǎn)
"""
"""==>>因?yàn)楹竺嫘枰玫綀D像坐標(biāo)的各種運(yùn)算,所以需要先進(jìn)行坐標(biāo)變換<<=="""
x1, y1 = self.change_coordinate_lt_center(x1, y1) # 將第一個(gè)點(diǎn)也就是父點(diǎn)轉(zhuǎn)換為笛卡爾坐標(biāo)系
x2, y2 = self.change_coordinate_lt_center(x2, y2) # 將第二個(gè)點(diǎn)也就是子點(diǎn)轉(zhuǎn)換為笛卡爾坐標(biāo)系
"""==>>進(jìn)行旋轉(zhuǎn)90°,分別獲得父、子的旋轉(zhuǎn)坐標(biāo)<<=="""
x3, y3, x4, y4 = self.rota(x1, y1, x2, y2) #x3,y3是父點(diǎn)的逆時(shí)針旋轉(zhuǎn)點(diǎn)、x4,y4是子點(diǎn)的旋轉(zhuǎn)坐標(biāo)點(diǎn)
"""==>>轉(zhuǎn)為圖像坐標(biāo)系<<=="""
x1, y1 = self.change_coordinate_center_lt(x1, y1) # 將父點(diǎn)坐標(biāo)轉(zhuǎn)為圖像坐標(biāo)
x2, y2 = self.change_coordinate_center_lt(x2, y2) # 將子點(diǎn)坐標(biāo)轉(zhuǎn)為圖像坐標(biāo)
x3, y3 = self.change_coordinate_center_lt(x3, y3) # 將父點(diǎn)坐標(biāo)旋轉(zhuǎn)坐標(biāo)轉(zhuǎn)為圖像坐標(biāo)
x4, y4 = self.change_coordinate_center_lt(x4, y4) # 將子點(diǎn)坐標(biāo)旋轉(zhuǎn)坐標(biāo)轉(zhuǎn)為圖像坐標(biāo)
"""==>>進(jìn)行透視變換、因?yàn)槭莾A斜的矩形,必須透視變換,不然的話沒辦法求比例<<=="""
pts = [(x1, y1), (x3, y3), (x2, y2), (x4, y4)]
"""==>>進(jìn)行透視變換、因?yàn)槭莾A斜的矩形,必須透視變換,不然的話沒辦法求比例<<=="""
self.Perspective_transformation(pts)
"""==>>answer表示的是,檢測區(qū)域?yàn)橄噙B接部分,<<=="""
if self.answer:
arrPt = np.array(pts, np.int32).reshape((-1, 1, 2)) # 將坐標(biāo)轉(zhuǎn)換為n行兩列的形式
cv.polylines(img, [arrPt], True, (0, 100, 255), 1)
# 圖片坐標(biāo)系轉(zhuǎn)為笛卡爾坐標(biāo)系
def change_coordinate_lt_center(self,x_in, y_in):
"""
x_out = x_in-1/2W
y_out = -(y_in-1/2H) = 1/2H-y_in
"""
x_out = x_in - 1 / 2 * self.W
y_out = 1 / 2 * self.H - y_in
return x_out, y_out
# 笛卡爾坐標(biāo)系轉(zhuǎn)為圖片的坐標(biāo)系
def change_coordinate_center_lt(self,x_in, y_in):
"""
x_out = x_in+1/2W
y_out = -(y_in-1/2H) = 1/2H-y_in =>>y_in = 1/2H-y_out ==>> y_out = 1/2H-yin
"""
x_out = x_in + 1 / 2 * self.W
y_out = 1 / 2 * self.H - y_in
return int(x_out), int(y_out)
def order_points(self,pts):
# initialzie a list of coordinates that will be ordered
# such that the first entry in the list is the top-left,
# the second entry is the top-right, the third is the
# bottom-right, and the fourth is the bottom-left
rect = np.zeros((4, 2), dtype="float32")
# the top-left point will have the smallest sum, whereas
# the bottom-right point will have the largest sum
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
# now, compute the difference between the points, the
# top-right point will have the smallest difference,
# whereas the bottom-left will have the largest difference
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
# return the ordered coordinates
return rect
# 進(jìn)行透視變換
def Perspective_transformation(self,pts):
"""==>>獲取四個(gè)點(diǎn)的坐標(biāo),依次是父點(diǎn)、父點(diǎn)旋轉(zhuǎn)點(diǎn)、子點(diǎn)、子點(diǎn)旋轉(zhuǎn)點(diǎn)、同時(shí)對對角線豎直的情況進(jìn)行單獨(dú)分析<<=="""
(x1, y1), (x2, y2), (x3, y3), (x4, y4) = pts[0], pts[1], pts[2], pts[3]
pts1 = np.float32(pts) # 透視變換前坐標(biāo)需要轉(zhuǎn)換為32位
if x1 == x3 or x2 == x4: # 有一張?zhí)厥馇闆r,就是對角線是豎直線,這樣的經(jīng)過排序之后就會出現(xiàn)變形,漏檢測,
rect = pts1
else:
rect = self.order_points(pts1)
(tl, tr, br, bl) = rect
"""==>>計(jì)算ROI區(qū)域的長寬<<=="""
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
"""==>>獲取變換后圖片的坐標(biāo)點(diǎn)、獲得旋轉(zhuǎn)矩陣、同時(shí)進(jìn)行透視變換<<=="""
dst = np.array([[0, 0],[maxWidth - 1, 0],[maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]], dtype="float32") # in the top-left, top-right, bottom-right, and bottom-left
matrix = cv2.getPerspectiveTransform(rect, dst) # 獲得旋轉(zhuǎn)矩陣
roi = cv.warpPerspective(self.binary, matrix, (maxHeight, maxWidth)) # 獲取roi區(qū)域
"""==>>對獲取到的目標(biāo)區(qū)域進(jìn)行面積統(tǒng)計(jì)<<=="""
contours = cv2.findContours(roi, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0] # 發(fā)現(xiàn)最外邊輪廓
area_list = [] #定義一個(gè)列表用來儲存所有的面積
for cnt in contours:
area_list.append(cv.contourArea(cnt))
if len(area_list) == 0:
max_cnts = 0 # 如果區(qū)域沒有面積,sum會報(bào)錯(cuò),所以需要單獨(dú)賦值為0
else:
max_cnts = sum(area_list) # 獲取面積綜合
area = maxWidth * maxHeight # ROI的一個(gè)面積
ratio = max_cnts / area # 二值化面積比
if ratio > 0.8:
self.number +=1
cv.circle(self.img, ((x1 + x3) // 2, int(y1 + y3) // 2), 4, (0, 0, 255), -1)
cv.circle(self.img, (x1, y1), 3, (255, 0, 0), -1)
cv.circle(self.img, (x2, y2), 3, (255, 0, 0), -1)
cv.circle(self.img, (x3, y3), 3, (255, 0, 0), -1)
cv.circle(self.img, (x4, y4), 3, (255, 0, 0), -1)
cv.line(self.img, (x1, y1), (x2, y2), (0, 255, 255), 1)
cv.line(self.img, (x3, y3), (x4, y4), (0, 255, 255), 1)
cv.line(self.binary, (x1, y1), (x3, y3), (0, 0, 0), 2)
cv.putText(self.img, str(self.number), ((x1 + x3) // 2, int(y1 + y3) // 2 - 10), cv.FONT_HERSHEY_SIMPLEX, 0.8,(255, 0, 0), 1)
roi_img = cv.warpPerspective(self.img, matrix, (maxHeight, maxWidth))
cv.imwrite(".\\roi\\" +name+"\\"+ str(self.number) + ".png", roi_img)
self.answer = True
def connect_domain(self):
# # 連通域分析
num_labels, labels, stats, centers = cv2.connectedComponentsWithStats(self.binary, connectivity=8)
# 利用連通域進(jìn)行不同輪廓畫出不同顏色
color = np.zeros((self.H, self.W, 3), np.uint8)
for i in range(1, num_labels):
mask = labels == i
color[:, :, 0][mask] = np.random.randint(0, 255)
color[:, :, 1][mask] = np.random.randint(0, 255)
color[:, :, 2][mask] = np.random.randint(0, 255)
result = cv2.addWeighted(img, 0.8, color, 0.5, 0) # 圖像權(quán)重疊加
for i in range(1, len(centers)):
cv2.drawMarker(result, (int(centers[i][0]), int(centers[i][1])), (0, 0, 255), 1, 20, 2)
return color,result
def get_binary(img):
# 圖像預(yù)處理
blurred = cv.pyrMeanShiftFiltering(img, 10, 100) # 邊緣保留濾波能夠進(jìn)行去噪是同時(shí)有效地保留邊緣
gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY) # 進(jìn)行灰度化為二值化做準(zhǔn)備
ret, binary = cv.threshold(gray, thresh=70, maxval=255, type=cv.THRESH_BINARY) # 固定閾值二值化,將大于thresh得像素點(diǎn)設(shè)置為maxval,
return binary
# 創(chuàng)建文件進(jìn)行圖片保存
def make_dir_save_img(path,img, binary,open,color,result ):
if not os.path.exists(name): # 判斷是否存在
os.makedirs( name) # 不存在就創(chuàng)建文件夾
if not os.path.exists("roi\\"+name): # 判斷是否存在
os.makedirs("roi\\" +name) # 不存在就創(chuàng)建文件夾
cv.imwrite(name+"\\img.png",img)
cv.imwrite(name+"\\binary.png",binary)
cv.imwrite(name+"\\open.png",open)
cv.imwrite(name+"\\color.png",color)
cv.imwrite(name+"\\result.png",result)
if __name__ == '__main__':
path = "001.png"
name = os.path.splitext(path)[0] # 文件名
img = cv.imread(path) # 讀取圖片
# 獲取二值化圖片
binary = get_binary(img)
# 創(chuàng)建實(shí)例
seg = SegmentationConnectObject(img,binary)
# 調(diào)用第一個(gè)函數(shù)開始執(zhí)行功能,返回二值化、開操作、黑底顏色、結(jié)果、原圖
img, binary,open,color,result = seg.main_find_mindist_points()
# 進(jìn)行圖片保存
make_dir_save_img(path,img, binary,open,color,result )
cv.namedWindow("img",0)
cv.imshow("img",img)
cv.namedWindow("binary",0)
cv.imshow("binary",binary)
cv.namedWindow("open",0)
cv.imshow("open",open)
cv.namedWindow("color",0)
cv.imshow("color",color)
cv.namedWindow("result",0)
cv.imshow("result",result)
cv.waitKey(0)
cv.destroyAllWindows()
四、效果




五、聲明
- 因?yàn)闆]用同分辨率下的多張瓶蓋圖片,本代碼沒進(jìn)行批量測量,同時(shí)因?yàn)閭€(gè)人能力有限,算法以及代碼都有不少需要地方,本次分享主要為代碼分享,后續(xù)如果有時(shí)間,我會整理,進(jìn)行每個(gè)步驟講解。謝謝大家的支持,如有問題請?jiān)u論區(qū)留意,大家一起討論進(jìn)步?。?!
六、 其他文章
1.理論系列:
第一章:pycharm、anaconda、opencv、pytorch、tensorflow、paddlex等環(huán)境配置大全總結(jié)【圖像處理py版本】
第二章:OpenCv算法的基本介紹與應(yīng)用
第三章:OpenCv圖片、視頻讀寫操作與基本應(yīng)用
第四章:OpenCv閾值分割/二值化(單通道、多通道圖片)總結(jié)
2.項(xiàng)目系列:
文章來源:http://www.zghlxwxcb.cn/news/detail-709097.html
項(xiàng)目一:四六級改卷系統(tǒng)
==》項(xiàng)目二:實(shí)戰(zhàn)篇:粘連物體分割——利用幾何分割實(shí)現(xiàn)瓶蓋分割檢測
項(xiàng)目三:實(shí)戰(zhàn)篇:粘連物體分割——利用幾何分割實(shí)現(xiàn)硬幣分割檢測
項(xiàng)目四:實(shí)戰(zhàn)篇:粘連物體分割——利用幾何分割實(shí)現(xiàn)細(xì)胞分割檢測
項(xiàng)目五:實(shí)戰(zhàn)篇:粘連物體分割——利用分水嶺算法實(shí)現(xiàn)糖豆分割檢測文章來源地址http://www.zghlxwxcb.cn/news/detail-709097.html
到了這里,關(guān)于【實(shí)戰(zhàn)篇:粘連物體分割——利用幾何分割實(shí)現(xiàn)瓶蓋分割檢測】的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!