对齐X射线图像:查找旋转,旋转和裁剪

时间:2015-09-07 09:38:35

标签: python opencv pillow scikit-image

我想在下面的X射线图片中(通过使用Python):

  1. 识别(不完美)矩形块的旋转
  2. 旋转图像使其垂直(纵向)
  3. 通过裁剪剩余的空白区域删除
  4. 我猜这部分与this question相反,其中工具很可能与添加corner detector相同。我不完全确定如何最好地解决这个问题,这似乎是某个人已经解决的问题。

    enter image description here

1 个答案:

答案 0 :(得分:3)

这可以使用到sendSynchronousRequest库的Python绑定来完成。以下代码已根据我已经改编的内容进行了改编,因此可以进一步优化和改进。

您提供的图像不仅旋转,而且也不是矩形,因此脚本在两个主要阶段工作。首先,它确定图像上的旋转并旋转并围绕最小矩形裁剪。然后它会拉伸生成的图像以适合生成的矩形。

初始阈值图像

enter image description here

初始边界矩形

enter image description here

旋转和裁剪的图像

enter image description here

拉伸的多边形

enter image description here

最终裁剪图像

enter image description here

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上测试