Skip to content

Instantly share code, notes, and snippets.

@mweibel
Last active March 20, 2020 13:05
Show Gist options
  • Save mweibel/bd2d6c2271e42ed97b97 to your computer and use it in GitHub Desktop.
Save mweibel/bd2d6c2271e42ed97b97 to your computer and use it in GitHub Desktop.
Python OpenCV deskew function, based on http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/
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))
@rainabba
Copy link

rainabba commented Sep 13, 2018

@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;
}

@cresxjohn
Copy link

@avsthiago i got an error in 'nlines = lines.size.shape[0]', it says 'int' object has no attribute 'shape'

@Bech007
Copy link

Bech007 commented Apr 16, 2019

me too i got an error in 'nlines = lines.size.shape[0]', it says 'int' object has no attribute 'shape'

@ggamit1
Copy link

ggamit1 commented Dec 18, 2019

me too i got an error in 'nlines = lines.size.shape[0]', it says 'int' object has no attribute 'shape'

@danepeterpadley
Copy link

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]'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment