python - 对齐 X 射线图像 : find rotation, 旋转和裁剪

标签 python opencv python-imaging-library scikit-image

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

  1. 识别(不完美)矩形 block 的旋转
  2. 旋转图像,使其处于垂直状态(纵向形式)
  3. 通过裁剪剩余的空白来删除

我猜这在一定程度上与this question相反其中工具很可能与添加 corner detector 相同。 。我不完全确定如何最好地解决这个问题,这似乎是有人已经解决的问题。

enter image description here

最佳答案

这可以使用 Python 绑定(bind)到 OpenCV 库来完成。以下代码是根据我已有的代码改编的,因此可能可以进一步优化和改进。

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

初始阈值图像

enter image description here

初始边界矩形

enter image description here

旋转和裁剪的图像

enter image description here

要拉伸(stretch)的多边形

enter image description here

最终裁剪图像

enter image description here

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对于subimage函数。

在 Python 2.7 和 OpenCV 3.0 上测试

关于python - 对齐 X 射线图像 : find rotation, 旋转和裁剪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32435488/

相关文章:

python - 按比例向上/向外生长

python - 如何同时给图片添加对比度、亮度等多种效果

python - 如何在PIL中的透明图像上绘制unicode字符

python - 使用 PIL 创建不循环的动画 gif

python - 使用循环将对象添加到列表(python)

python - 模块'对象没有属性 'drawMatches' opencv python

python - 将pandas.core.groupby.SeriesGroupBy转换为DataFrame

opencv - 不同相机(RGB 和红外线)的 stereoCalibrate

python - Xcode 中的纯 Python?

python - 升级到 Django 1.7。获取错误 : Cannot serialize: <storages. backends.s3boto.S3BotoStorage 对象