-
-
Save mweibel/bd2d6c2271e42ed97b97 to your computer and use it in GitHub Desktop.
import cv2 | |
import numpy as np | |
from matplotlib import pyplot as plt | |
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE) | |
def compute_skew(image): | |
image = cv2.bitwise_not(image) | |
height, width = image.shape | |
edges = cv2.Canny(image, 150, 200, 3, 5) | |
lines = cv2.HoughLinesP(edges, 1, cv2.cv.CV_PI/180, 100, minLineLength=width / 2.0, maxLineGap=20) | |
angle = 0.0 | |
nlines = lines.size | |
for x1, y1, x2, y2 in lines[0]: | |
angle += np.arctan2(y2 - y1, x2 - x1) | |
return angle / nlines | |
def deskew(image, angle): | |
image = cv2.bitwise_not(image) | |
non_zero_pixels = cv2.findNonZero(image) | |
center, wh, theta = cv2.minAreaRect(non_zero_pixels) | |
root_mat = cv2.getRotationMatrix2D(center, angle, 1) | |
rows, cols = image.shape | |
rotated = cv2.warpAffine(image, root_mat, (cols, rows), flags=cv2.INTER_CUBIC) | |
return cv2.getRectSubPix(rotated, (cols, rows), center) | |
deskewed_image = deskew(img.copy(), compute_skew(img)) |
For me it doesn't work so, I'had to make some features:
def compute_skew(image):
# image = cv2.bitwise_not(image)
height, width = image.shape
edges = cv2.Canny(image, 150, 200, 3, 5)
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=width / 2.0, maxLineGap=20)
angle = 0.0
number_of_line = lines.size
for x1, y1, x2, y2 in lines[0]:
if x1 != x2:
angle += np.arctan(y2 - y1 / x2 - x1)
return angle / number_of_line
def deskew(image, angle):
angle = np.math.degrees(angle)
# image = cv2.bitwise_not(image)
non_zero_pixels = cv2.findNonZero(image)
center, wh, theta = cv2.minAreaRect(non_zero_pixels)
root_mat = cv2.getRotationMatrix2D(center, angle, 1)
rows, cols = image.shape
rotated = cv2.warpAffine(image, root_mat, (cols, rows), flags=cv2.INTER_CUBIC)
return cv2.getRectSubPix(rotated, (cols, rows), center)
I tried to run your code with opencv 2.4.10 and I get some errors. Does it run only on version 3 ?
I'm using:
Numpy: 1.13.3
OpenCV: 3.3.0
Python: 3.6.4
For me, it was necessary to do some modifications in order to make it work in my environment:
import cv2
import numpy as np
img = cv2.imread('image_path', cv2.IMREAD_GRAYSCALE)
def compute_skew(image):
image = cv2.bitwise_not(image)
height, width = image.shape
# Filter removed
# edges = cv2.Canny(image, 150, 200, 3, 5)
lines = cv2.HoughLinesP(image, 1, np.pi/180, 100, minLineLength=width / 2.0, maxLineGap=20)
angle = 0.0
# lines.size gets the number of lines multiplied by 4 (number of columns)
# nlines = lines.size
# so now, I only use the number of lines
nlines = lines.size.shape[0]
# this reshape was necessary in order to convert the shape from (n_lines,1,4) to (n_lines,4)
lines = lines.reshape(lines.shape[0], 4)
# [0] removed because of the new shape
# for x1, y1, x2, y2 in lines[0]:
for x1, y1, x2, y2 in lines:
angle += np.arctan2(y2 - y1, x2 - x1)
# The function cv2.getRotationMatrix2D recieves as input the
# angle in degrees, so I converted the return
# https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html#getrotationmatrix2d
#return angle / nlines
angle /= nlines
return angle*180/np.pi
def deskew(image, angle):
image = cv2.bitwise_not(image)
non_zero_pixels = cv2.findNonZero(image)
center, wh, theta = cv2.minAreaRect(non_zero_pixels)
root_mat = cv2.getRotationMatrix2D(center, angle, 1)
rows, cols = image.shape
rotated = cv2.warpAffine(image, root_mat, (cols, rows), flags=cv2.INTER_CUBIC)
return cv2.getRectSubPix(rotated, (cols, rows), center)
deskewed_image = deskew(img.copy(), compute_skew(img))
cv2.imshow('original', img)
cv2.imshow('deskew', deskewed_image)
I hope that helps someone 😄
@avsthiago Yes, thank you! In return, I share the following JavaScript version (specifically for opencv4nodejs
function deskew( mat, angle, reqid = '0', drawGrid = false ) {
angle = angle || computeSkew( mat.cvtColor(cv.COLOR_BGR2GRAY).bitwiseNot() );
let nGray = mat.cvtColor(cv.COLOR_BGR2GRAY),
mGray = nGray.bilateralFilter( 10, 60, 60 ),
mEdge = mGray.canny(0 , 1, 5),
contours = mEdge.findContours(cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE),
rotatedRect = contours.sort( (c0, c1) => c1.area - c0.area )[0].minAreaRect(),
initCAngle = rotatedRect.angle,
contourAngle = rotatedRect.size.width < rotatedRect.size.height ? initCAngle + 90 : initCAngle;
return mat.warpAffine( cv.getRotationMatrix2D( new cv.Point( mat.cols / 2, mat.rows / 2 ), contourAngle ) );
}
function computeSkew( mat ) { // mat is expected to already be grayscale and inverted
let [ height, width ] = mat.sizes,
lines = mat.houghLinesP(1, Math.PI/180, 100, minLineLength = width / 2.0, maxLineGap = 20),
angle = lines.reduce((ac, line, index) => {
return ac + Math.atan2( line.w - line.x, line.z - line.y )
}, 0);
return angle / lines.length * 180 / Math.PI;
}
@avsthiago i got an error in 'nlines = lines.size.shape[0]', it says 'int' object has no attribute 'shape'
me too i got an error in 'nlines = lines.size.shape[0]', it says 'int' object has no attribute 'shape'
me too i got an error in 'nlines = lines.size.shape[0]', it says 'int' object has no attribute 'shape'
For those that are getting the error in 'nlines = lines.size.shape[0]', it says 'int' object has no attribute 'shape'.
You can resolve this issue by changing it to 'nlines = lines.shape[0]'
cols, rows = image.shape
should be:
rows, cols = image.shape
Also:
np.arctan2(x2 - x1, y2 - y1)
should be:
np.arctan2(y2 - y1, x2 - x1)