Advertisement
mgordon

X-ray auto_crop v2

Sep 9th, 2015
265
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.77 KB | None | 0 0
  1. import numpy as np
  2. import cv2
  3. import math
  4.  
  5. def subimage(image, center, theta, width, height):
  6.     if 45 < theta <= 90:
  7.         theta = theta - 90
  8.         width, height = height, width
  9.  
  10.     theta *= math.pi / 180 # convert to rad
  11.     v_x = (math.cos(theta), math.sin(theta))
  12.     v_y = (-math.sin(theta), math.cos(theta))
  13.     s_x = center[0] - v_x[0] * (width / 2) - v_y[0] * (height / 2)
  14.     s_y = center[1] - v_x[1] * (width / 2) - v_y[1] * (height / 2)
  15.     mapping = np.array([[v_x[0],v_y[0], s_x], [v_x[1],v_y[1], s_y]])
  16.     return cv2.warpAffine(image, mapping, (width, height), flags=cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_REPLICATE)
  17.  
  18. def get_threshold_clr(image_source):
  19.     h, w = image_source.shape[:2]
  20.     inset = math.ceil(.1*(h + w)/2)
  21.     mismatch = 0
  22.     corners = ((inset, inset), (w-inset, h-inset), (inset, h-inset), (w-inset, inset))
  23.     clr = None
  24.     for crnr1 in corners:
  25.         for crnr2 in corners:
  26.             if (np.linalg.norm(image_src[crnr1] - image_src[crnr2]) > 3):
  27.                 mismatch += 1
  28.                 break
  29.             else:
  30.                 clr = image_src[crnr1]
  31.    
  32.     if (mismatch > 3 + 3 or clr == None):
  33.         return None
  34.     else:
  35.         # Get one threshold value
  36.         clr = np.mean(clr)
  37.         if (clr > 250):
  38.             return 240
  39.         elif(clr < 10):
  40.             return 20
  41.         else:
  42.             None
  43.    
  44. def auto_crop(image_source):
  45.     # First slightly crop edge - some images had a rogue 2 pixel black edge on one side
  46.     init_crop = 5
  47.     h, w = image_source.shape[:2]
  48.     image_source = image_source[init_crop:init_crop+(h-init_crop*2), init_crop:init_crop+(w-init_crop*2)]
  49.  
  50.     threshold = get_threshold_clr(image_source)
  51.     if (threshold == None):
  52.         return image_source
  53.    
  54.     edge_clr = 255
  55.     if (threshold < 128):
  56.         edge_clr = 0
  57.  
  58.     # Add back a white border
  59.     image_source = cv2.copyMakeBorder(image_source, 5,5,5,5, cv2.BORDER_CONSTANT, value=(edge_clr, edge_clr, edge_clr))
  60.  
  61.     image_gray = cv2.cvtColor(image_source, cv2.COLOR_BGR2GRAY)
  62.    
  63.        
  64.     _, image_thresh = cv2.threshold(image_gray, threshold, 255, cv2.THRESH_BINARY)
  65.  
  66.     image_thresh2 = image_thresh.copy()
  67.     image_thresh2 = cv2.Canny(image_thresh2, 100, 100, apertureSize=3)
  68.     points = cv2.findNonZero(image_thresh2)
  69.  
  70.     centre, dimensions, theta = cv2.minAreaRect(points)
  71.     rect = cv2.minAreaRect(points)
  72.  
  73.     width = int(dimensions[0])
  74.     height = int(dimensions[1])
  75.  
  76.     box = cv2.boxPoints(rect)
  77.     box = np.int0(box)
  78.  
  79.     temp = image_source.copy()
  80.     cv2.drawContours(temp, [box], 0, (255,0,0), 2)
  81.  
  82.     M = cv2.moments(box)    
  83.     cx = int(M['m10']/M['m00'])
  84.     cy = int(M['m01']/M['m00'])
  85.  
  86.     image_patch = subimage(image_source, (cx, cy), theta+90, height, width)
  87.  
  88.     # add back a small border
  89.     image_patch = cv2.copyMakeBorder(image_patch, 1,1,1,1, cv2.BORDER_CONSTANT, value=(edge_clr,edge_clr,edge_clr))
  90.  
  91.     # Convert image to binary, edge is black. Do edge detection and convert edges to a list of points.
  92.     # Then calculate a minimum set of points that can enclose the points.
  93.     _, image_thresh = cv2.threshold(image_patch, threshold, 255, 1)
  94.     image_thresh = cv2.Canny(image_thresh, 100, 100, 3)
  95.     points = cv2.findNonZero(image_thresh)
  96.     hull = cv2.convexHull(points)
  97.  
  98.     # Find min epsilon resulting in exactly 4 points, typically between 7 and 21
  99.     # This is the smallest set of 4 points to enclose the image.
  100.     for epsilon in range(3, 50):
  101.         hull_simple = cv2.approxPolyDP(hull, epsilon, 1)
  102.  
  103.         if len(hull_simple) == 4:
  104.             break
  105.  
  106.     hull = hull_simple
  107.  
  108.     # Find closest fitting image size and warp/crop to fit
  109.     # (ie reduce scaling to a minimum)
  110.  
  111.     x,y,w,h = cv2.boundingRect(hull)
  112.     target_corners = np.array([[0,0],[w,0],[w,h],[0,h]], np.float32)
  113.  
  114.     # Sort hull into tl,tr,br,bl order.
  115.     # n.b. hull is already sorted in clockwise order, we just need to know where top left is.
  116.  
  117.     source_corners = hull.reshape(-1,2).astype('float32')
  118.     min_dist = 100000
  119.     index = 0
  120.  
  121.     for n in xrange(len(source_corners)):
  122.         x,y = source_corners[n]
  123.         dist = math.hypot(x,y)
  124.  
  125.         if dist < min_dist:
  126.             index = n
  127.             min_dist = dist
  128.  
  129.     # Rotate the array so tl is first
  130.     source_corners = np.roll(source_corners , -(2*index))
  131.  
  132.     try:
  133.         transform = cv2.getPerspectiveTransform(source_corners, target_corners)
  134.         return cv2.warpPerspective(image_patch, transform, (w,h))
  135.  
  136.     except:
  137.         print "Warp failure"
  138.         return image_patch
  139.  
  140.  
  141. if __name__ == "__main__":
  142.     import os
  143.     import re
  144.     import sys
  145.  
  146.     cv2.namedWindow("Original")
  147.     cv2.namedWindow("Cropped")
  148.    
  149.     if (len(sys.argv) < 2):
  150.         sys.exit()
  151.     else:
  152.         source_dir = sys.argv[1]
  153.         if (not re.search("^/", source_dir)):
  154.             source_dir = "{0}/{1}".format(os.path.dirname(os.path.realpath(__file__)),
  155.                                           source_dir)
  156.    
  157.     if not os.path.isdir(source_dir):
  158.         print "The source directory '{0}' does not exist".format(source_dir)
  159.         sys.exit()
  160.    
  161.     for (directory, _, files) in os.walk(source_dir):
  162.         for f in files:
  163.             if re.search("\\.png$", f) and\
  164.                 not re.search("^cropped_", f):
  165.                 path = os.path.join(directory, f)
  166.                 image_src = cv2.imread(path)
  167.                 image_cropped = auto_crop(image_src)
  168.                 cv2.imwrite(os.path.join(directory, "cropped_{0}".format(f)),
  169.                             image_cropped)
  170.                 print "Processed file {0}".format(f)
  171.                 cv2.imshow("Original", image_src)
  172.                 cv2.imshow("Cropped", image_cropped)
  173.                 cv2.waitKey(0)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement