Skip to content

Instantly share code, notes, and snippets.

@arccoder
Last active July 4, 2023 14:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arccoder/7b98761478aace53899cd22448b80c71 to your computer and use it in GitHub Desktop.
Save arccoder/7b98761478aace53899cd22448b80c71 to your computer and use it in GitHub Desktop.
Straighten a page using OpenCV
def cyclic_intersection_pts(pts):
"""
Sorts 4 points in clockwise direction with the first point been closest to 0,0
Assumption:
There are exactly 4 points in the input and
from a rectangle which is not very distorted
"""
if pts.shape[0] != 4:
return None
# Calculate the center
center = np.mean(pts, axis=0)
# Sort the points in clockwise
cyclic_pts = [
# Top-left
pts[np.where(np.logical_and(pts[:, 0] < center[0], pts[:, 1] < center[1]))[0][0], :],
# Top-right
pts[np.where(np.logical_and(pts[:, 0] > center[0], pts[:, 1] < center[1]))[0][0], :],
# Bottom-Right
pts[np.where(np.logical_and(pts[:, 0] > center[0], pts[:, 1] > center[1]))[0][0], :],
# Bottom-Left
pts[np.where(np.logical_and(pts[:, 0] < center[0], pts[:, 1] > center[1]))[0][0], :]
]
return np.array(cyclic_pts)
# Detect lines using hough transform
polar_lines = cv2.HoughLines(edges, 1, np.pi / 180, 150)
drawHoughLines(color, polar_lines, 'output/houghlines.png')
# Detect the intersection points
# https://gist.github.com/arccoder/9a73e0b2d8be1a8fd42d6026d3a7a1e1
import opencv_hough_lines as lq
intersect_pts = lq.hough_lines_intersection(polar_lines, gray.shape)
# Sort the points in cyclic order
intersect_pts = cyclic_intersection_pts(intersect_pts)
# Draw intersection points and save
out = color.copy()
for pts in intersect_pts:
cv2.rectangle(out, (pts[0] - 1, pts[1] - 1), (pts[0] + 1, pts[1] + 1), (0, 0, 255), 2)
cv2.imwrite('output/intersect_points.png', out)
def drawHoughLines(image, lines, output):
out = image.copy()
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 10000 * (-b))
y1 = int(y0 + 10000 * (a))
x2 = int(x0 - 10000 * (-b))
y2 = int(y0 - 10000 * (a))
cv2.line(out, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imwrite(output, out)
# Find contours
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Fit a rotated rect
rotatedRect = cv2.minAreaRect(contours[0])
# Get rotated rect dimensions
(x, y), (width, height), angle = rotatedRect
# Get the 4 corners of the rotated rect
rotatedRectPts = cv2.boxPoints(rotatedRect)
rotatedRectPts = np.int0(rotatedRectPts)
# Draw the rotated rect on the image
out = color.copy()
cv2.drawContours(out, [rotatedRectPts], 0, (0, 255, 0), 2)
cv2.imwrite('output/minRect.png', out)
# Read input
color = cv2.imread('input/image.jpg', cv2.IMREAD_COLOR)
color = cv2.resize(color, (0, 0), fx=0.15, fy=0.15)
# RGB to gray
gray = cv2.cvtColor(color, cv2.COLOR_BGR2GRAY)
cv2.imwrite('output/gray.png', gray)
# cv2.imwrite('output/thresh.png', thresh)
# Edge detection
edges = cv2.Canny(gray, 100, 200, apertureSize=3)
# Save the edge detected image
cv2.imwrite('output/edges.png', edges)
# List the output points in the same order as input
# Top-left, top-right, bottom-right, bottom-left
dstPts = [[0, 0], [width, 0], [width, height], [0, height]]
# Get the transform
m = cv2.getPerspectiveTransform(np.float32(intersect_pts), np.float32(dstPts))
# Transform the image
out = cv2.warpPerspective(color, m, (int(width), int(height)))
# Save the output
cv2.imwrite('output/page.png', out)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment