簡體   English   中英

對齊X射線圖像:查找旋轉,旋轉和裁剪

[英]Align x-ray images: find rotation, rotate and crop

我想在下面的X射線圖像中(通過使用Python):

  1. 識別(不完美)矩形塊的旋轉
  2. 旋轉圖像使其處於垂直(縱向形式)
  3. 通過裁剪剩余的空白區域來移除

我猜這部分與這個問題相反,其中工具很可能與添加角點檢測器相同 我不完全確定如何最好地接近這一點,這似乎是一個人已經解決的問題。

在此輸入圖像描述

這可以使用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)

謝謝轉到subimage函數的StackOverflow答案

在Python 2.7和OpenCV 3.0上測試過

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM