Last active
December 26, 2024 17:25
-
-
Save smeschke/aa989df78551a9050a78e0d7a8c50495 to your computer and use it in GitHub Desktop.
Revisions
-
smeschke revised this gist
Jun 26, 2019 . No changes.There are no files selected for viewing
-
smeschke revised this gist
Jun 26, 2019 . 1 changed file with 113 additions and 262 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,269 +1,120 @@ import cv2 import numpy as np src = 255 - cv2.imread('/home/stephen/Desktop/I7Ykpbs.jpg',0) scores = [] h,w = src.shape small_dimention = min(h,w) src = src[:small_dimention, :small_dimention] out = cv2.VideoWriter('/home/stephen/Desktop/rotate.avi', cv2.VideoWriter_fourcc('M','J','P','G'), 15, (320,320)) def rotate(img, angle): rows,cols = img.shape M = cv2.getRotationMatrix2D((cols/2,rows/2),angle,1) dst = cv2.warpAffine(img,M,(cols,rows)) return dst def sum_rows(img): # Create a list to store the row sums row_sums = [] # Iterate through the rows for r in range(img.shape[0]-1): # Sum the row row_sum = sum(sum(img[r:r+1,:])) # Add the sum to the list row_sums.append(row_sum) # Normalize range to (0,255) row_sums = (row_sums/max(row_sums)) * 255 # Return return row_sums def display_data(roi, row_sums, buffer): # Create background to draw transform on bg = np.zeros((buffer*2, buffer*2), np.uint8) # Iterate through the rows and draw on the background for row in range(roi.shape[0]-1): row_sum = row_sums[row] bg[row:row+1, :] = row_sum left_side = int(buffer/3) bg[:, left_side:] = roi[:,left_side:] cv2.imshow('bg1', bg) k = cv2.waitKey(1) out.write(cv2.cvtColor(cv2.resize(bg, (320,320)), cv2.COLOR_GRAY2BGR)) return k # Rotate the image around in a circle angle = 0 while angle <= 360: # Rotate the source image img = rotate(src, angle) # Crop the center 1/3rd of the image (roi is filled with text) h,w = img.shape buffer = min(h, w) - int(min(h,w)/1.5) #roi = img.copy() roi = img[int(h/2-buffer):int(h/2+buffer), int(w/2-buffer):int(w/2+buffer)] # Create background to draw transform on bg = np.zeros((buffer*2, buffer*2), np.uint8) # Threshold image _, roi = cv2.threshold(roi, 140, 255, cv2.THRESH_BINARY) # Compute the sums of the rows row_sums = sum_rows(roi) # High score --> Zebra stripes score = np.count_nonzero(row_sums) if sum(row_sums) < 100000: scores.append(angle) k = display_data(roi, row_sums, buffer) if k == 27: break # Increment angle and try again angle += .5 cv2.destroyAllWindows() # Create images for display purposes display = src.copy() # Create an image that contains bins. bins_image = np.zeros_like(display) for angle in scores: # Rotate the image and draw a line on it display = rotate(display, angle) cv2.line(display, (0,int(h/2)), (w,int(h/2)), 255, 1) display = rotate(display, -angle) # Rotate the bins image bins_image = rotate(bins_image, angle) # Draw a line on a temporary image temp = np.zeros_like(bins_image) cv2.line(temp, (0,int(h/2)), (w,int(h/2)), 50, 1) # 'Fill' up the bins bins_image += temp bins_image = rotate(bins_image, -angle) # Find the most filled bin for col in range(bins_image.shape[0]-1): column = bins_image[:, col:col+1] if np.amax(column) == np.amax(bins_image): x = col for col in range(bins_image.shape[0]-1): column = bins_image[:, col:col+1] if np.amax(column) == np.amax(bins_image): y = col # Draw circles showing the most filled bin cv2.circle(display, (x,y), 560, 255, 5) # Plot with Matplotlib import matplotlib.pyplot as plt import matplotlib.image as mpimg f, axarr = plt.subplots(1,3, sharex=True) axarr[0].imshow(src) axarr[1].imshow(display) axarr[2].imshow(bins_image) axarr[0].set_title('Source Image') axarr[1].set_title('Output') axarr[2].set_title('Bins Image') axarr[0].axis('off') axarr[1].axis('off') axarr[2].axis('off') plt.show() cv2.waitKey() cv2.destroyAllWindows() -
smeschke revised this gist
May 24, 2019 . No changes.There are no files selected for viewing
-
smeschke revised this gist
May 24, 2019 . 1 changed file with 68 additions and 57 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -13,20 +13,22 @@ # Finds the shards of paper in an image def get_shards(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Gray gray = cv2.blur(gray, (2,2)) canny = cv2.Canny(gray, 30, 150) # Canny cv2.imshow('canny', canny) # Find contours contours, _ = cv2.findContours(canny,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # Draw contours on canny (this connects the contours) cv2.drawContours(canny, contours, -1, 255, 6) # Get mask for floodfill h, w = canny.shape[:2] mask = np.zeros((h+2, w+2), np.uint8) # Floodfill from point (0, 0) cv2.floodFill(canny, mask, (0,0), 123); cv2.imshow('ff', canny) cv2.imwrite('/home/stephen/Desktop/with.png', canny) # Get an image that is only the gray floodfilled area hsv = cv2.cvtColor(canny, cv2.COLOR_GRAY2BGR) lower, upper = np.array([122,122,122]), np.array([124,124,124]) # Threshold the HSV image to get only blue colors mask = cv2.inRange(hsv, lower, upper) @@ -44,12 +46,20 @@ def get_shards(img): if area > 987 and area < img.shape[0]*img.shape[1]-9000: cv2.drawContours(res, [contour], 0, (255,255,255), cv2.FILLED) unconnectedContours.append(contour) # Return the unconnected contours image and list of contours cv2.imshow('res', res) print(len(unconnectedContours), largest_contour(contours)) cv2.waitKey() return res, unconnectedContours[0] # Distance between two points def distance(a,b): return math.sqrt((a[0]-b[0])**2 + (a[1]-b[1])**2) # Returns a single contour def largest_contour(contours): c = max(contours, key=cv2.contourArea) return c[0] # Draw a contour, but doesn't connect contour[0] with contour[-1] def draw_contour(img, contour, color, thick): for idx in range(len(contour)-1): @@ -79,21 +89,7 @@ def lines_mask(img): pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a))) cv2.line(mask, pt1, pt2, 255, 18, cv2.LINE_AA) #cv2.imshow('mask', mask) return mask # Takes an image and a contour and returns the torn edge def find_torn_edge(img, cnt, img_lines): @@ -114,38 +110,6 @@ def find_torn_edge(img, cnt, img_lines): cv2.line(temp_human, a, b, 255, 14) return torn_edge, temp_human, temp # Rotate a contour def rotate_contour(contour, edge_mask, wOverlay, hOverlay, rotation): hw = 2* max(wOverlay, hOverlay) @@ -201,14 +165,59 @@ def draw_background(best_match, img1, img2, img1_mask, img2_mask, offset): bg = bg[min(y_vals): max(y_vals), min(x_vals): max(x_vals)] bg = cv2.resize(bg, (987,987)) return bg # Read in images img1 = cv2.imread('/home/stephen/Desktop/paper3.jpg') img2 = cv2.imread('/home/stephen/Desktop/paper4.jpg') img_w, img_h = 780,1040 img1 = cv2.resize(img1, (img_w, img_h)) img2 = cv2.resize(img2, (img_w, img_h)) # Get shards of paper res1, cnt1 = get_shards(img1) res2, cnt2 = get_shards(img2) # Find the lines in the image img1_lines = lines_mask(res1) img2_lines = lines_mask(res2) # Find the torn edges torn_edge1, temp_human1, edge_mask1 = find_torn_edge(img1, cnt1, img1_lines) cv2.imshow('img1', temp_human1) cv2.waitKey() torn_edge2, temp_human2, edge_mask2 = find_torn_edge(img2, cnt2, img2_lines) #cv2.imshow('img1', temp_human2) #cv2.waitKey() # Plot import matplotlib.pyplot as plt import matplotlib.image as mpimg f, axarr = plt.subplots(2,4, sharex=True) axarr[0,0].imshow(img1) axarr[1,0].imshow(img2) outline1 = cv2.drawContours(img1.copy(), [cnt1], 0, (0,255,0), 15) outline2 = cv2.drawContours(img2.copy(), [cnt2], 0, (0,255,0), 15) img1_mask = np.zeros((img_h, img_w), np.uint8) img2_mask = np.zeros((img_h, img_w), np.uint8) img1_mask = cv2.drawContours(img1_mask, [cnt1], 0, 255, cv2.FILLED) img2_mask = cv2.drawContours(img2_mask, [cnt2], 0, 255, cv2.FILLED) axarr[0,1].imshow(outline1) axarr[1,1].imshow(outline2) axarr[0,2].imshow(img1_lines) axarr[1,2].imshow(img2_lines) axarr[0,3].imshow(temp_human1) axarr[1,3].imshow(temp_human2) axarr[0, 0].set_title('Source Images') axarr[0, 1].set_title('Paper Edges') axarr[0, 2].set_title('Hough Lines') axarr[0, 3].set_title('Torn Edge') plt.show() vid_writer = cv2.VideoWriter('/home/stephen/Desktop/re_encode.avi',cv2.VideoWriter_fourcc('M','J','P','G'),20, (987, 987)) # Rotate the contour left, right = cnt1, cnt2 match, matches, angle, best_match = 0, [-1], 0, (0,0,0,0) graph = np.zeros((987,987,3), np.uint8) while angle < 380: # Copy the images and create temporary images img, overlay = img2.copy(), img1.copy() tempA = np.zeros((img.shape[0], img.shape[1]), np.uint8) @@ -223,7 +232,9 @@ def draw_background(best_match, img1, img2, img1_mask, img2_mask, offset): tempA = draw_contour(tempA, b, 123, 3) tempB = draw_contour(tempB, a, 123, 3) tempC = tempA + tempB cv2.imwrite('/home/stephen/Desktop/thresh.png', tempC/1.5) _, thresh = cv2.threshold(tempC, 220, 255, cv2.THRESH_BINARY_INV); thresh = 255 - thresh match = sum(sum(thresh)) matches.append(match) @@ -235,7 +246,7 @@ def draw_background(best_match, img1, img2, img1_mask, img2_mask, offset): p2 = int(angle*2.35), int(sum(sum(thresh))/75) cv2.line(graph, p1, p2, (0,255,0), 2) bg = draw_background((_, angle, dx, dy), img1, img2, img1_mask, img2_mask, 0) bg += graph img = draw_contour(bg, b, (255,0,255), 2) -
smeschke revised this gist
May 7, 2019 . 1 changed file with 254 additions and 96 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,100 +1,258 @@ import cv2, numpy as np, random, math # Find contour edges # Find the edge that is torn # use the hough line transform # create a mask image where the lines and white on a black background # check if the point is in a white or black region # Rotate the torn edges # Measure how much they overlap # The rotation with the maximum overlap will be how they should align # Align and display the images # Finds the shards of paper in an image def get_shards(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Gray #blurred = cv2.GaussianBlur(gray, (5, 5), 0) # Blur canny = cv2.Canny(gray, 30, 150) # Canny # Find contours contours, _ = cv2.findContours(canny,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # Draw contours on canny (this connects the contours) cv2.drawContours(canny, contours, -1, 255, 2) _, thresh = cv2.threshold(canny, 230, 255, cv2.THRESH_BINARY_INV); # Get mask for floodfill h, w = thresh.shape[:2] mask = np.zeros((h+2, w+2), np.uint8) # Floodfill from point (0, 0) cv2.floodFill(thresh, mask, (0,0), 123); # Get an image that is only the gray floodfilled area hsv = cv2.cvtColor(thresh, cv2.COLOR_GRAY2BGR) lower, upper = np.array([122,122,122]), np.array([124,124,124]) # Threshold the HSV image to get only blue colors mask = cv2.inRange(hsv, lower, upper) # Bitwise-AND mask and original image res = cv2.bitwise_and(hsv,hsv, mask= mask) gray = 255 - cv2.cvtColor(res, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV); contours, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) res = np.zeros_like(res) # Create a list for unconnected contours unconnectedContours = [] for contour in contours: area = cv2.contourArea(contour) # If the contour is not really small, or really big if area > 987 and area < img.shape[0]*img.shape[1]-9000: cv2.drawContours(res, [contour], 0, (255,255,255), cv2.FILLED) unconnectedContours.append(contour) # Return the unconnected contours image and list of contours return res, unconnectedContours # Distance between two points def distance(a,b): return math.sqrt((a[0]-b[0])**2 + (a[1]-b[1])**2) # Draw a contour, but doesn't connect contour[0] with contour[-1] def draw_contour(img, contour, color, thick): for idx in range(len(contour)-1): a, b = contour[idx], contour[idx+1] a,b = tuple(a[0]), tuple(b[0]) if distance(a,b) < 321: cv2.line(img, a, b, color, thick) return img # Finds the lines in an image def lines_mask(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) dst = cv2.Canny(gray, 50, 200, None, 3) # Create mask image mask = np.zeros_like(gray) # Find the lines lines = cv2.HoughLines(dst, 1, np.pi / 180, 100, None, 0, 0) # Draw the lines if lines is not None: for i in range(0, len(lines)): rho = lines[i][0][0] theta = lines[i][0][1] a = math.cos(theta) b = math.sin(theta) x0 = a * rho y0 = b * rho pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a))) pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a))) cv2.line(mask, pt1, pt2, 255, 18, cv2.LINE_AA) #cv2.imshow('mask', mask) return mask # Read in images img1 = cv2.imread('/home/stephen/Desktop/paper6.jpg') img2 = cv2.imread('/home/stephen/Desktop/paper5.jpg') img_w, img_h = 780,1040 img1 = cv2.resize(img1, (img_w, img_h)) img2 = cv2.resize(img2, (img_w, img_h)) # Find the lines in the image img1_lines = lines_mask(img1) img2_lines = lines_mask(img2) # Get shards of paper _, cnt1 = get_shards(img1) _, cnt2 = get_shards(img2) cnt1, cnt2 = cnt1[0], cnt2[0] # Takes an image and a contour and returns the torn edge def find_torn_edge(img, cnt, img_lines): # Create a temporary iamge img_h, img_w = img.shape[0], img.shape[1] temp = np.zeros((img_h, img_w), np.uint8) temp_human = np.zeros((img_h, img_w), np.uint8) cv2.drawContours(temp_human, [cnt], 0, 78, cv2.FILLED) torn_edge = [] for i in range(len(cnt)): x,y = cnt[i][0] if img_lines[y,x] == 0: torn_edge.append((x,y)) #cnt1 = np.array(torn_edge1) for i in range(len(torn_edge)-1): a = torn_edge[i] b = torn_edge[i+1] cv2.line(temp, a, b, 255, 2) cv2.line(temp_human, a, b, 255, 14) return torn_edge, temp_human, temp # Find the torn edges torn_edge1, temp_human1, edge_mask1 = find_torn_edge(img1, cnt1, img1_lines) #cv2.imshow('img1', temp_human1) #cv2.waitKey() torn_edge2, temp_human2, edge_mask2 = find_torn_edge(img2, cnt2, img2_lines) #cv2.imshow('img1', temp_human2) #cv2.waitKey() # Plot import matplotlib.pyplot as plt import matplotlib.image as mpimg f, axarr = plt.subplots(2,4, sharex=True) axarr[0,0].imshow(img1) axarr[1,0].imshow(img2) outline1 = cv2.drawContours(img1.copy(), [cnt1], 0, (0,255,0), 15) outline2 = cv2.drawContours(img2.copy(), [cnt2], 0, (0,255,0), 15) img1_mask = np.zeros((img_h, img_w), np.uint8) img2_mask = np.zeros((img_h, img_w), np.uint8) img1_mask = cv2.drawContours(img1_mask, [cnt1], 0, 255, cv2.FILLED) img2_mask = cv2.drawContours(img2_mask, [cnt2], 0, 255, cv2.FILLED) axarr[0,1].imshow(outline1) axarr[1,1].imshow(outline2) axarr[0,2].imshow(img1_lines) axarr[1,2].imshow(img2_lines) axarr[0,3].imshow(temp_human1) axarr[1,3].imshow(temp_human2) axarr[0, 0].set_title('Source Images') axarr[0, 1].set_title('Paper Edges') axarr[0, 2].set_title('Hough Lines') axarr[0, 3].set_title('Torn Edge') plt.show() # Rotate a contour def rotate_contour(contour, edge_mask, wOverlay, hOverlay, rotation): hw = 2* max(wOverlay, hOverlay) temp = np.zeros((hw,hw), np.uint8) #contour = contour + max(wOverlay, hOverlay) #temp = draw_contour(temp, contour, 255, 2) temp = edge_mask.copy() #cv2.imshow('temp', cv2.resize(temp, (456,456))) rows,cols = temp.shape M = cv2.getRotationMatrix2D((cols/2,rows/2),rotation,1) dst = cv2.warpAffine(temp,M,(cols,rows)) _, thresh = cv2.threshold(dst, 123, 255, cv2.THRESH_BINARY) contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) return contours[0] # Translate a contour so that is located above another contour def align_translate(left, right): right_center, _ = cv2.minEnclosingCircle(right) left_center, _ = cv2.minEnclosingCircle(left) dx, dy = right_center[0]-left_center[0], right_center[1]-left_center[1] for i in range(len(right)): previous = right[i][0][0] right[i][0][0] = previous - dx previous = right[i][0][1] right[i][0][1] = previous - dy return left, right, dx, dy # Draws the output image def draw_background(best_match, img1, img2, img1_mask, img2_mask, offset): print(best_match) # Create a background for each of the images bgA = np.zeros((2345,2345,3), np.uint8) bgB = np.zeros((2345,2345,3), np.uint8) # Mask images for writing img1 = cv2.bitwise_and(img1, img1, mask = img1_mask) img2 = cv2.bitwise_and(img2, img2, mask = img2_mask) # Determine A buffer size _, rotation, dx, dy = best_match rotation, dx, dy = int(rotation), int(dx), int(dy) b = int(max(abs(dx), abs(dy))) + 10 # Draw the first image on the background bgA[offset+b:offset+b+img_h, offset+b:offset+b+img_w] = img2 # Rotate the second image M = cv2.getRotationMatrix2D((img_w/2,img_h/2),rotation,1) dst = cv2.warpAffine(img1,M,(img_w,img_h)) # Translate it and paste it on the background bgB[offset+b-dy:offset+b-dy+img_h, offset+b-dx:offset+b-dx+img_w] = dst # Combine the backgrounds bg = bgA + bgB # Crop and resize x_vals = b-dx,b-dx+img_w, b,b+img_w y_vals = b, b+img_h, b-dy, b-dy+img_h bg = bg[min(y_vals): max(y_vals), min(x_vals): max(x_vals)] bg = cv2.resize(bg, (987,987)) return bg vid_writer = cv2.VideoWriter('/home/stephen/Desktop/re_encode.avi',cv2.VideoWriter_fourcc('M','J','P','G'),20, (987, 987)) # Rotate the contour left, right = cnt1, cnt2 match, matches, angle, best_match = 0, [-1], 0, (0,0,0,0) graph = np.zeros((987,987,3), np.uint8) while angle < 360: # Copy the images and create temporary images img, overlay = img2.copy(), img1.copy() tempA = np.zeros((img.shape[0], img.shape[1]), np.uint8) tempB = np.zeros((img.shape[0], img.shape[1]), np.uint8) # Rotate the contour rotatedContour = rotate_contour(torn_edge1, edge_mask1, max(img.shape), max(img.shape), angle) # Clean left contour clean_left = rotate_contour(torn_edge2, edge_mask2, max(img.shape), max(img.shape), 0) # Translate the contour a,b, dx, dy = align_translate(clean_left, rotatedContour) # Draw the contour tempA = draw_contour(tempA, b, 123, 3) tempB = draw_contour(tempB, a, 123, 3) tempC = tempA + tempB _, thresh = cv2.threshold(tempC, 220, 255, cv2.THRESH_BINARY_INV); thresh = 255 - thresh match = sum(sum(thresh)) matches.append(match) # Is this the best match? if match >= max(matches): best_match = b, angle, int(dx), int(dy) # Make the graph p1 = int(angle*2.35), 0 p2 = int(angle*2.35), int(sum(sum(thresh))/75) cv2.line(graph, p1, p2, (0,255,0), 2) bg = draw_background((_, angle, dx, dy), img1, img2, img1_mask, img2_mask, 123) bg += graph img = draw_contour(bg, b, (255,0,255), 2) img = draw_contour(bg, a, (255,255,0), 2) img = draw_contour(bg, best_match[0], (0,255,255), 4) cv2.imshow('bg', bg) vid_writer.write(bg) k=cv2.waitKey(1) if k == 27: break angle += 1 cv2.destroyAllWindows() # Show user the best match bg = draw_background(best_match, img1, img2, img1_mask, img2_mask, 0) cv2.imshow('img', bg) cv2.imwrite('/home/stephen/Desktop/paperReconstruction.png', bg) cv2.waitKey() cv2.destroyAllWindows() -
smeschke created this gist
Apr 18, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,100 @@ import cv2 import numpy as np out = cv2.VideoWriter('/home/stephen/Desktop/smooth_pose.avi',cv2.VideoWriter_fourcc('M','J','P','G'), 60, (640,640)) src = 255 - cv2.imread('/home/stephen/Desktop/scan.jpg',0) scores = [] def rotate(img, angle): rows,cols = img.shape M = cv2.getRotationMatrix2D((cols/2,rows/2),angle,1) dst = cv2.warpAffine(img,M,(cols,rows)) return dst def sum_rows(img): # Create a list to store the row sums row_sums = [] # Iterate through the rows for r in range(img.shape[0]-1): # Sum the row row_sum = sum(sum(img[r:r+1,:])) # Add the sum to the list row_sums.append(row_sum) # Normalize range to (0,255) row_sums = (row_sums/max(row_sums)) * 255 # Return return row_sums def display_data(roi, row_sums, buffer): # Create background to draw transform on bg = np.zeros((buffer*2, buffer*2), np.uint8) # Iterate through the rows and draw on the background for row in range(roi.shape[0]-1): row_sum = row_sums[row] bg[row:row+1, :] = row_sum left_side = int(buffer/3) bg[:, left_side:] = roi[:,left_side:] cv2.imshow('bg1', bg) k = cv2.waitKey(1) out.write(cv2.cvtColor(cv2.resize(bg, (640,640)), cv2.COLOR_GRAY2BGR)) return k # Rotate the image around in a circle angle = 0 while angle <= 360: # Rotate the source image img = rotate(src, angle) # Crop the center 1/3rd of the image (roi is filled with text) h,w = img.shape buffer = min(h, w) - int(min(h,w)/1.5) roi = img[int(h/2-buffer):int(h/2+buffer), int(w/2-buffer):int(w/2+buffer)] # Create background to draw transform on bg = np.zeros((buffer*2, buffer*2), np.uint8) # Compute the sums of the rows row_sums = sum_rows(roi) # High score --> Zebra stripes score = np.count_nonzero(row_sums) scores.append(score) # Image has best rotation if score <= min(scores): # Save the rotatied image print('found optimal rotation') best_rotation = img.copy() k = display_data(roi, row_sums, buffer) if k == 27: break # Increment angle and try again angle += .5 cv2.destroyAllWindows() def area_to_top_of_text(img): # Create a background to draw on bg = np.zeros_like(img) # Iterate through the rows for position in range(w-1): # Find the top value in the column column = np.array(img[:,position:position+1]) top = np.argmax(column) # Fill in the area from the top of the page to top of the text a = position, 0 b = position, top cv2.line(img, a, b, 123, 1) cv2.line(bg, a, b, 255, 1) # Show and return cv2.imshow('img', img) cv2.waitKey(0) return img, bg # Find the area from the top of page to top of image _, bg = area_to_top_of_text(best_rotation.copy()) right_side_up = sum(sum(bg)) # Flip image and try again best_rotation_flipped = rotate(best_rotation, 180) _, bg = area_to_top_of_text(best_rotation_flipped.copy()) upside_down = sum(sum(bg)) # Check which area is larger if right_side_up < upside_down: aligned_image = best_rotation else: aligned_image = best_rotation_flipped # Save aligned image cv2.imwrite('/home/stephen/Desktop/best_rotation.png', 255-aligned_image) cv2.destroyAllWindows()