2015-09-07 137 views

回答

3

這可以使用Python綁定到OpenCV庫來完成。下面的代碼已經從我以前的東西改編而來,所以它可能會進一步優化和改進。

您給出的圖像不僅是旋轉的,它也不是矩形的,因爲這樣的腳本在兩個主要階段中起作用。首先它確定圖像上的旋轉並旋轉並在最小矩形周圍進行裁剪。然後拉伸產生的圖像以適應所得到的矩形。

初始閾值圖像

enter image description here

初始邊界矩形

enter image description here

旋轉並且裁剪圖像

enter image description here

多邊形舒展從

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 answersubimage功能。

測試Python 2.7和OpenCV 3.0

+0

謝謝!一直試圖在一組不同的圖像集,我想能夠使用uint8的numpy矩陣作爲輸入,但我還沒有完全想到如何以最好的方式做到這一點。其次,我在零(int [M ['m10']/M ['m00'])'(應該很容易修復),斷言錯誤「src.checkVector(2,CV_32F)== 4 && dst.checkVector(2,CV_32F)== 4「at'transform = cv2.getPerspectiveTransform(source_corners,target_corners)',和一個神祕的」(-215)total> = 0 &&(depth == CV_32F || depth == CV_32S )「線上的錯誤'hull = cv2.convexHull(points)'。有任何想法嗎? –

+0

是的,它確實需要測試一系列文件。我的其他代碼遇到了'findContours'返回許多輪廓的問題。我覺得隨着你的進行,最容易顯示圖像。也可以使用drawContours來查看它認爲邊緣的位置。不確定在uint8上,需要調查。 –

+0

我做了一些改變。它不再使用findContours。我還發現,如果在拉伸之前添加白色邊框,效果會更好。這對我自己的測試圖像效果更好。 –