Python 提取矩形的尺寸
所以我在做一个声纳图像的图像处理项目。更具体地说,我试图提取声纳扫描仪拍摄的水池图像的尺寸。我能够提取池的矩形区域,但我不知道如何获得每个边的像素尺寸。我正在Python中使用OpenCV 如果有人能就如何做到这一点给我任何建议,我将不胜感激 我已经尝试过寻找hough线的线交点,但没有什么好结果 代码到目前为止Python 提取矩形的尺寸,python,opencv,image-processing,Python,Opencv,Image Processing,所以我在做一个声纳图像的图像处理项目。更具体地说,我试图提取声纳扫描仪拍摄的水池图像的尺寸。我能够提取池的矩形区域,但我不知道如何获得每个边的像素尺寸。我正在Python中使用OpenCV 如果有人能就如何做到这一点给我任何建议,我将不胜感激 我已经尝试过寻找hough线的线交点,但没有什么好结果 代码到目前为止 导入cv2 将numpy作为np导入 从scipy导入ndi图像作为ndi 从scipy.ndimage.measurements导入标签 def最大_组件(索引): #此函数获取一个
导入cv2
将numpy作为np导入
从scipy导入ndi图像作为ndi
从scipy.ndimage.measurements导入标签
def最大_组件(索引):
#此函数获取一个表示
#图像的白色区域,并返回最大值
#连通指数的白色对象
return\u arr=np.zeros((512512),dtype=np.uint8)
对于索引中的索引:
返回\u arr[索引[0]][索引[1]]=255
返回
image=cv2.imread('sonar\u dataset/usive/sonarXY\u 5.bmp',0)
图像\u高斯=ndi.高斯滤波器(图像,4)
image\u gaussian\u inv=cv2。按位\u not(image\u gaussian)
内核=np.ones((3,3),np.uint8)
#双阈值提取矩形的边
ret1,img1=cv2.阈值(图像\u高斯\u inv,120255,cv2.阈值\u二进制)
ret2,img2=cv2.阈值(图像\u高斯\u inv,150255,cv2.阈值\u二进制)
双阈值=img1-img2
关闭=cv2.morphologyEx(双阈值,cv2.Morpho\u关闭,内核1)
已标记,ncomponents=标签(关闭,内核)
index=np.index(closing.shape).T[:,:,[1,0]]
twos=索引[标记==2]
区域=[ncomponents+1]范围内的val的np.sum(标记==val)]
矩形=最大的_分量(两个)
cv2.imshow('矩形',矩形)
cv2.等待键(0)
原始图像和提取的对象如下所示
这并不完美,但这种简单的方法应该是您的良好起点:
import cv2, math
import numpy as np
img = cv2.imread(R'D:\dev\projects\stackoverflow\dimensions_of_rectangle\img1.png')
print(img.shape)
img_moments=cv2.moments(img[:,:,0]) #use only one channel here (cv2.moments operates only on single channels images)
print(img_moments)
# print(dir(img_moments))
# calculate centroid (center of mass of image)
x = img_moments['m10'] / img_moments['m00']
y = img_moments['m01'] / img_moments['m00']
# calculate orientation of image intensity (it corresponds to the image intensity axis)
u00 = img_moments['m00']
u20 = img_moments['m20'] - x*img_moments['m10']
u02 = img_moments['m02'] - y*img_moments['m01']
u11 = img_moments['m11'] - x*img_moments['m01']
u20_prim = u20/u00
u02_prim = u02/u00
u11_prim = u11/u00
angle = 0.5 * math.atan(2*u11_prim / (u20_prim - u02_prim))
print('The image should be rotated by: ', math.degrees(angle) / 2.0, ' degrees')
cols,rows = img.shape[:2]
# rotate the image by half of this angle
rotation_matrix = cv2.getRotationMatrix2D((cols/2,rows/2), math.degrees(angle / 2.0), 1)
img_rotated = cv2.warpAffine(img, rotation_matrix ,(cols,rows))
# print(img_rotated.shape, img_rotated.dtype)
cv2.imwrite(R'D:\dev\projects\stackoverflow\dimensions_of_rectangle\img1_rotated.png', img_rotated)
img_rotated_clone = np.copy(img_rotated)
img_rotated_clone2 = np.copy(img_rotated)
# first method - just calculate bounding rect
bounding_rect = cv2.boundingRect(img_rotated[:, :, 0])
cv2.rectangle(img_rotated_clone, (bounding_rect[0], bounding_rect[1]),
(bounding_rect[0] + bounding_rect[2], bounding_rect[1] + bounding_rect[3]), (255,0,0), 2)
# second method - find columns and rows with biggest sums
def nlargest_cols(a, n):
col_sums = [(np.sum(col), idx) for idx, col in enumerate(a.T)]
return sorted(col_sums, key=lambda a: a[0])[-n:]
def nlargest_rows(a, n):
col_sums = [(np.sum(col), idx) for idx, col in enumerate(a[:,])]
return sorted(col_sums, key=lambda a: a[0])[-n:]
top15_cols_indices = nlargest_cols(img_rotated[:,:,0], 15)
top15_rows_indices = nlargest_rows(img_rotated[:,:,0], 15)
for a in top15_cols_indices:
cv2.line(img_rotated_clone, (a[1], 0), (a[1], rows), (0, 255, 0), 1)
for a in top15_rows_indices:
cv2.line(img_rotated_clone, (0, a[1]), (cols, a[1]), (0, 0, 255), 1)
cv2.imwrite(R'D:\dev\projects\stackoverflow\dimensions_of_rectangle\img2.png', img_rotated_clone)
当然,您需要调整路径。img1.png是您问题中的第二幅图像,img1_旋转是旋转图像的结果:
img2是最终输出:
蓝色矩形是method1(只是一个边界矩形),绿色和红色线条(15条红色和15条绿色-所有1个像素宽)是第二种方法
算法非常简单:
希望这就是你想要的,让我知道你会有任何问题。所以我想到了一个办法——这有点耗费人力,但最终我们还是找到了正确的答案。我将直接使用您在最后一张图像中显示的连接组件输出
rho
和theta
,告诉我们在骨架化图像中检测到哪些线。我们可以使用OpenCV来实现这一点。在骨架化图像上执行此操作非常重要,否则我们将有许多候选线与边界框的真实轮廓平行,您将无法区分它们from skimage.morphology import skeletonize
import cv2
import numpy as np
# Step #1 - Skeletonize
im = cv2.imread('K7ELI.png', 0)
out = skeletonize(im > 0)
# Convert to uint8
out = 255*(out.astype(np.uint8))
# Step #2 - Hough Transform
lines = cv2.HoughLines(out,1,np.pi/180,60)
# Step #3 - Find points of intersection
pts = []
for i in range(lines.shape[0]):
(rho1, theta1) = lines[i,0]
m1 = -1/np.tan(theta1)
c1 = rho1 / np.sin(theta1)
for j in range(i+1,lines.shape[0]):
(rho2, theta2) = lines[j,0]
m2 = -1 / np.tan(theta2)
c2 = rho2 / np.sin(theta2)
if np.abs(m1 - m2) <= 1e-8:
continue
x = (c2 - c1) / (m1 - m2)
y = m1*x + c1
if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:
pts.append((int(x), int(y)))
# Step #4 - Find convex hull
pts = np.array(pts)
pts = pts[:,None] # We need to convert to a 3D numpy array with a singleton 2nd dimension
hull = cv2.convexHull(pts)
# Step #5 - K-Means clustering
# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS
# Apply KMeans
# The convex hull points need to be float32
z = hull.copy().astype(np.float32)
compactness,labels,centers = cv2.kmeans(z,4,None,criteria,10,flags)
# Step #6 - Find the lengths of each side
centers = cv2.convexHull(centers)[:,0]
for (i, j) in zip(range(4), [1, 2, 3, 0]):
length = np.sqrt(np.sum((centers[i] - centers[j])**2.0))
print('Length of side {}: {}'.format(i+1, length))
# Draw the sides of each rectangle in the original image
out5 = cv2.imread('no8BP.png') # Note - grayscale image read in as colour
for (i, j) in zip(range(4), [1, 2, 3, 0]):
cv2.line(out5, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)
# Show the image
cv2.imshow('Output', out5); cv2.waitKey(0); cv2.destroyAllWindows()
骨架化
,我们可以对上面显示的连接组件图像进行骨架化。请注意,在继续之前,需要将图像转换为二进制。调用该方法后,我们需要在余下的过程中转换回无符号8位整数。我已经下载了上面的图片并保存在本地。我们可以在以下情况下运行skeletonize
方法:
from skimage.morphology import skeletonize
im = cv2.imread('K7ELI.png', 0)
out = skeletonize(im > 0)
# Convert to uint8
out = 255*(out.astype(np.uint8))
我们得到这样的图像:
lines = cv2.HoughLines(out,1,np.pi/180,60)
out3 = np.dstack([im, im, im])
for pt in centers:
cv2.circle(out3, tuple(pt), 2, (0, 255, 0), 2)
步骤2-使用Hough变换
使用Hough变换,我们可以检测此图像中最突出的线条:
lines = cv2.HoughLines(out,1,np.pi/180,60)
out3 = np.dstack([im, im, im])
for pt in centers:
cv2.circle(out3, tuple(pt), 2, (0, 255, 0), 2)
她
out2 = np.dstack([im, im, im])
for pt in hull[:,0]:
cv2.circle(out2, tuple(pt), 2, (0, 255, 0), 2)
# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS
# Apply KMeans
# The convex hull points need to be float32
z = hull.copy().astype(np.float32)
compactness,labels,centers = cv2.kmeans(z,4,None,criteria,10,flags)
array([[338.5 , 152.5 ],
[302.6667, 368.6667],
[139. , 340. ],
[178.5 , 127. ]], dtype=float32)
out3 = np.dstack([im, im, im])
for pt in centers:
cv2.circle(out3, tuple(pt), 2, (0, 255, 0), 2)
centers = cv2.convexHull(centers)[:,0]
for (i, j) in zip(range(4), [1, 2, 3, 0]):
length = np.sqrt(np.sum((centers[i] - centers[j])**2.0))
print('Length of side {}: {}'.format(i+1, length))
Length of side 1: 219.11654663085938
Length of side 2: 166.1582489013672
Length of side 3: 216.63160705566406
Length of side 4: 162.019287109375
out4 = np.dstack([im, im, im])
for (i, j) in zip(range(4), [1, 2, 3, 0]):
cv2.line(out4, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)
out5 = cv2.imread('no8BP.png') # Note - grayscale image read in as colour
for (i, j) in zip(range(4), [1, 2, 3, 0]):
cv2.line(out5, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)
from skimage.morphology import skeletonize
import cv2
import numpy as np
# Step #1 - Skeletonize
im = cv2.imread('K7ELI.png', 0)
out = skeletonize(im > 0)
# Convert to uint8
out = 255*(out.astype(np.uint8))
# Step #2 - Hough Transform
lines = cv2.HoughLines(out,1,np.pi/180,60)
# Step #3 - Find points of intersection
pts = []
for i in range(lines.shape[0]):
(rho1, theta1) = lines[i,0]
m1 = -1/np.tan(theta1)
c1 = rho1 / np.sin(theta1)
for j in range(i+1,lines.shape[0]):
(rho2, theta2) = lines[j,0]
m2 = -1 / np.tan(theta2)
c2 = rho2 / np.sin(theta2)
if np.abs(m1 - m2) <= 1e-8:
continue
x = (c2 - c1) / (m1 - m2)
y = m1*x + c1
if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:
pts.append((int(x), int(y)))
# Step #4 - Find convex hull
pts = np.array(pts)
pts = pts[:,None] # We need to convert to a 3D numpy array with a singleton 2nd dimension
hull = cv2.convexHull(pts)
# Step #5 - K-Means clustering
# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS
# Apply KMeans
# The convex hull points need to be float32
z = hull.copy().astype(np.float32)
compactness,labels,centers = cv2.kmeans(z,4,None,criteria,10,flags)
# Step #6 - Find the lengths of each side
centers = cv2.convexHull(centers)[:,0]
for (i, j) in zip(range(4), [1, 2, 3, 0]):
length = np.sqrt(np.sum((centers[i] - centers[j])**2.0))
print('Length of side {}: {}'.format(i+1, length))
# Draw the sides of each rectangle in the original image
out5 = cv2.imread('no8BP.png') # Note - grayscale image read in as colour
for (i, j) in zip(range(4), [1, 2, 3, 0]):
cv2.line(out5, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)
# Show the image
cv2.imshow('Output', out5); cv2.waitKey(0); cv2.destroyAllWindows()