我想在下面的X射线图片中(通过使用Python):
我猜这部分与this question相反,其中工具很可能与添加corner detector相同。我不完全确定如何最好地解决这个问题,这似乎是某个人已经解决的问题。
答案 0 :(得分:3)
这可以使用到sendSynchronousRequest
库的Python绑定来完成。以下代码已根据我已经改编的内容进行了改编,因此可以进一步优化和改进。
您提供的图像不仅旋转,而且也不是矩形,因此脚本在两个主要阶段工作。首先,它确定图像上的旋转并旋转并围绕最小矩形裁剪。然后它会拉伸生成的图像以适合生成的矩形。
初始阈值图像
初始边界矩形
旋转和裁剪的图像
从
拉伸的多边形最终裁剪图像
OpenCV
感谢import numpy as np
import cv2
import math
THRESHOLD = 240
def subimage(image, center, theta, width, height):
if 45 < theta <= 90:
theta = theta - 90
width, height = height, width
theta *= math.pi / 180 # convert to rad
v_x = (math.cos(theta), math.sin(theta))
v_y = (-math.sin(theta), math.cos(theta))
s_x = center[0] - v_x[0] * (width / 2) - v_y[0] * (height / 2)
s_y = center[1] - v_x[1] * (width / 2) - v_y[1] * (height / 2)
mapping = np.array([[v_x[0],v_y[0], s_x], [v_x[1],v_y[1], s_y]])
return cv2.warpAffine(image, mapping, (width, height), flags=cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_REPLICATE)
def auto_crop(image_source):
# First slightly crop edge - some images had a rogue 2 pixel black edge on one side
init_crop = 5
h, w = image_source.shape[:2]
image_source = image_source[init_crop:init_crop+(h-init_crop*2), init_crop:init_crop+(w-init_crop*2)]
# Add back a white border
image_source = cv2.copyMakeBorder(image_source, 5,5,5,5, cv2.BORDER_CONSTANT, value=(255,255,255))
image_gray = cv2.cvtColor(image_source, cv2.COLOR_BGR2GRAY)
_, image_thresh = cv2.threshold(image_gray, THRESHOLD, 255, cv2.THRESH_BINARY)
image_thresh2 = image_thresh.copy()
image_thresh2 = cv2.Canny(image_thresh2, 100, 100, apertureSize=3)
points = cv2.findNonZero(image_thresh2)
centre, dimensions, theta = cv2.minAreaRect(points)
rect = cv2.minAreaRect(points)
width = int(dimensions[0])
height = int(dimensions[1])
box = cv2.boxPoints(rect)
box = np.int0(box)
temp = image_source.copy()
cv2.drawContours(temp, [box], 0, (255,0,0), 2)
M = cv2.moments(box)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
image_patch = subimage(image_source, (cx, cy), theta+90, height, width)
# add back a small border
image_patch = cv2.copyMakeBorder(image_patch, 1,1,1,1, cv2.BORDER_CONSTANT, value=(255,255,255))
# Convert image to binary, edge is black. Do edge detection and convert edges to a list of points.
# Then calculate a minimum set of points that can enclose the points.
_, image_thresh = cv2.threshold(image_patch, THRESHOLD, 255, 1)
image_thresh = cv2.Canny(image_thresh, 100, 100, 3)
points = cv2.findNonZero(image_thresh)
hull = cv2.convexHull(points)
# Find min epsilon resulting in exactly 4 points, typically between 7 and 21
# This is the smallest set of 4 points to enclose the image.
for epsilon in range(3, 50):
hull_simple = cv2.approxPolyDP(hull, epsilon, 1)
if len(hull_simple) == 4:
break
hull = hull_simple
# Find closest fitting image size and warp/crop to fit
# (ie reduce scaling to a minimum)
x,y,w,h = cv2.boundingRect(hull)
target_corners = np.array([[0,0],[w,0],[w,h],[0,h]], np.float32)
# Sort hull into tl,tr,br,bl order.
# n.b. hull is already sorted in clockwise order, we just need to know where top left is.
source_corners = hull.reshape(-1,2).astype('float32')
min_dist = 100000
index = 0
for n in xrange(len(source_corners)):
x,y = source_corners[n]
dist = math.hypot(x,y)
if dist < min_dist:
index = n
min_dist = dist
# Rotate the array so tl is first
source_corners = np.roll(source_corners , -(2*index))
try:
transform = cv2.getPerspectiveTransform(source_corners, target_corners)
return cv2.warpPerspective(image_patch, transform, (w,h))
except:
print "Warp failure"
return image_patch
cv2.namedWindow("Result")
image_src = cv2.imread("xray.png")
image_cropped = auto_crop(image_src)
cv2.imwrite("cropped xray.png", image_cropped)
cv2.imshow("Result", image_cropped)
cv2.waitKey(0)
函数的StackOverflow answer。
在Python 2.7和OpenCV 3.0上测试