Is there a way (using something like OpenCV) to detect text skew and correct it by rotating the image? Pretty much like this?
Rotating an image seems easy enough if you know the angle, but for the images I'm processing, I wont...it will need to be detected somehow.
Given an image containing a rotated block of text at an unknown angle, we need to correct the text skew by: Detecting the block of text in the image. Computing the angle of the rotated text. Rotating the image to correct for the skew.
The skew detection script takes image file as input, then performs the following steps: The deskew script uses the skew angle determined using skew detection script to remove the skew from the image. To calculate the skew angle for a given image file, use -i option followed by the path to file:
There can be various approaches to determine skew angle, but we’ll stick to the simple one — take the largest text block and use its angle. # Apply dilate to merge text into meaningful lines/paragraphs. # Use larger kernel on X axis to merge characters into single line, cancelling out any spaces. # Determine the angle.
After obtaining a binary image, the idea is rotate the image at various angles and generate a histogram of pixels in each iteration. To determine the skew angle, we compare the maximum difference between peaks and using this skew angle, rotate the image to correct the skew
I would provide javacv for your reference.
package com.test13;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgcodecs.Imgcodecs;
public class EdgeDetection {
static{ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }
public static void main( String[] args ) throws Exception{
Mat src = Imgcodecs.imread("src//data//inclined_text.jpg");
Mat src_gray = new Mat();
Imgproc.cvtColor(src, src_gray, Imgproc.COLOR_BGR2GRAY);
Imgcodecs.imwrite("src//data//inclined_text_src_gray.jpg", src_gray);
Mat output = new Mat();
Core.bitwise_not(src_gray, output);
Imgcodecs.imwrite("src//data//inclined_text_output.jpg", output);
Mat points = Mat.zeros(output.size(),output.type());
Core.findNonZero(output, points);
MatOfPoint mpoints = new MatOfPoint(points);
MatOfPoint2f points2f = new MatOfPoint2f(mpoints.toArray());
RotatedRect box = Imgproc.minAreaRect(points2f);
Mat src_squares = src.clone();
Mat rot_mat = Imgproc.getRotationMatrix2D(box.center, box.angle, 1);
Mat rotated = new Mat();
Imgproc.warpAffine(src_squares, rotated, rot_mat, src_squares.size(), Imgproc.INTER_CUBIC);
Imgcodecs.imwrite("src//data//inclined_text_squares_rotated.jpg",rotated);
}
}
Based on your above comment, here is the code based on the tutorial here, working fine for the above image,
Source
Rotated
Mat src=imread("text.png",0);
Mat thr,dst;
threshold(src,thr,200,255,THRESH_BINARY_INV);
imshow("thr",thr);
std::vector<cv::Point> points;
cv::Mat_<uchar>::iterator it = thr.begin<uchar>();
cv::Mat_<uchar>::iterator end = thr.end<uchar>();
for (; it != end; ++it)
if (*it)
points.push_back(it.pos());
cv::RotatedRect box = cv::minAreaRect(cv::Mat(points));
cv::Mat rot_mat = cv::getRotationMatrix2D(box.center, box.angle, 1);
//cv::Mat rotated(src.size(),src.type(),Scalar(255,255,255));
Mat rotated;
cv::warpAffine(src, rotated, rot_mat, src.size(), cv::INTER_CUBIC);
imshow("rotated",rotated);
Edit:
Also see the answer here , might be helpful.
Here's an implementation of the Projection Profile Method algorithm for skew angle estimation. Various angle points are projected into an accumulator array where the skew angle can be defined as the angle of projection within a search interval that maximizes alignment. The idea is to rotate the image at various angles and generate a histogram of pixels for each iteration. To determine the skew angle, we compare the maximum difference between peaks and using this skew angle, rotate the image to correct the skew.
Input
Result
Skew angle: -5
import cv2
import numpy as np
from scipy.ndimage import interpolation as inter
def correct_skew(image, delta=1, limit=5):
def determine_score(arr, angle):
data = inter.rotate(arr, angle, reshape=False, order=0)
histogram = np.sum(data, axis=1, dtype=float)
score = np.sum((histogram[1:] - histogram[:-1]) ** 2, dtype=float)
return histogram, score
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
scores = []
angles = np.arange(-limit, limit + delta, delta)
for angle in angles:
histogram, score = determine_score(thresh, angle)
scores.append(score)
best_angle = angles[scores.index(max(scores))]
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, best_angle, 1.0)
corrected = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, \
borderMode=cv2.BORDER_REPLICATE)
return best_angle, corrected
if __name__ == '__main__':
image = cv2.imread('1.png')
angle, corrected = correct_skew(image)
print('Skew angle:', angle)
cv2.imshow('corrected', corrected)
cv2.waitKey()
Note: You may have to adjust the delta
or limit
values depending on the image. The delta
value controls iteration step, it will iterate up until the limit
which controls the maximum angle. This method is straightforward by iteratively checking each angle + delta
and currently only works to correct skew in the range of +/- 5 degrees. If you need to correct at a larger angle, adjust the limit
value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With