Convert ImageMagick command to Python

92 Views Asked by At

I have the following ImageMagick command that works very well for removing grid lines from a table image:

convert test.png -background white -deskew 40% -write mpr:img \ 
\( mpr:img -morphology close rectangle:30x1 -negate \) \
\( mpr:img -morphology close rectangle:1x30 -negate \) \
-evaluate-sequence add \
result.png

I can, of course, invoke this code from my Python script.

However, I would like to be able to translate this command into something pythonic like Wand, PIL, cv2 or whatever.

Namely for portability sake but also to avoid having to save the output.

Even though Wand, for instance, has a morphology function I do not seem to be able to figure out the kernel and so on.

As the real images contain confidential information, I have added a similar mock example. I have verified that the above command produces the desired results of entirely clearing the grid lines without affecting the quality of the text.

As the real images contain confidential information, I have added a similar mock example.

1

There are 1 best solutions below

0
On BEST ANSWER

Here is a solution in Python/OpenCV.

Note I apply some (1 deg) rotation to your input for testing.

  • Read the input
  • Threshold on the gray lines
  • Get the convex hull from the thresholded image
  • Get the rotated rectangle from the convex hull
  • Create the affine matrix
  • Unrotate the input
  • Apply morphology to first copy of deskewed image and invert
  • Apply morphology to second copy of deskewed image and invert
  • Convert the 3 images to float and divide by 255 and add them together. Then multiply by 255 and convert back to uint8
  • Save added results

Input:

enter image description here

import cv2
import numpy as np

# read the input
img = cv2.imread('chart.png')
hh, ww = img.shape[:2]

# threshold on gray lines
lower=(190,190,190)
upper=(230,230,230)
thresh = cv2.inRange(img, lower, upper)

# get convex hull
points = np.column_stack(np.where(thresh.transpose() > 0))
hull = cv2.convexHull(points)

# draw convex hull vertices on input image
hull_img = img.copy()
cv2.polylines(hull_img, [hull], True, (0,0,255), 2)

# get rotated bounding box
rotrect = cv2.minAreaRect(hull)
box = cv2.boxPoints(rotrect)
box = np.intp(box)

# draw rotated rectangle on copy of img
rot_bbox = img.copy()
cv2.drawContours(rot_bbox,[box],0,(0,0,255),2)

# get angle relative to horizontal from rotated rectangle
angle = rotrect[-1]

# from https://www.pyimagesearch.com/2017/02/20/text-skew-correction-opencv-python/
# the `cv2.minAreaRect` function returns values in the
# range [-90, 0); as the rectangle rotates clockwise the
# returned angle tends to 0 -- in this special case we
# need to add 90 degrees to the angle
if angle < -45:
    angle = -(90 + angle)
 
# otherwise, just take the negative of the angle to make
# it positive
else:
    angle = -angle

print(angle,"deg")

# negate the angle for deskewing
neg_angle = -angle

# Get rotation matrix
(h, w) = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, neg_angle, scale=1.0)

# rotate the image to deskew it
deskewed = cv2.warpAffine(img, M, (ww,hh), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT, borderValue=(255,255,255))

# create morphology close images
img1 = deskewed.copy()
kernel = cv2.getStructuringElement(cv2.MORPH_RECT , (30,1))
img1 = cv2.morphologyEx(img1, cv2.MORPH_CLOSE, kernel)
img1 = np.invert(img1)

img2 = deskewed.copy()
kernel = cv2.getStructuringElement(cv2.MORPH_RECT , (1,30))
img2 = cv2.morphologyEx(img2, cv2.MORPH_CLOSE, kernel)
img2 = np.invert(img2)

# add input and morphology close images
result = 255*(deskewed.astype("float32")/255 + img1.astype("float32")/255 + img2.astype("float32")/255)
result = result.clip(0,255).astype("uint8")

# save results
cv2.imwrite("chart_threshold.jpg", thresh)
cv2.imwrite("chart_convexhull.jpg", hull_img)
cv2.imwrite("chart_rotated_rectangle.jpg", rot_bbox)
cv2.imwrite("chart_deskewed.jpg", deskewed)
cv2.imwrite("chart_result.png", deskewed)

# show results
cv2.imshow("THRESH", thresh)
cv2.imshow("CONVEX_HULL", hull_img)
cv2.imshow("ROTATED BBOX", rot_bbox)
cv2.imshow("DESKEWED", deskewed)
cv2.imshow("IMG1", img1)
cv2.imshow("IMG2", img2)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Threshold Image

enter image description here

Convex Hull Image:

enter image description here

Rotated Rectangle Image:

enter image description here

Deskewed Image:

enter image description here

Result Image:

enter image description here