python3 opencv 文本倾斜校正

效果图展示:

一、

二、

三、

~~

说明:其实第一组图可以用检测轮廓(findcontours)的方式获取到矩形的四个顶点,在通过透视变化完成可以矫正的,但是这种方法有一定的局限性,当图像边界信息部明显,找不到轮廓,例如:二、三两组图片,所以我采用了霍夫直线函数HoughLinesP函数进行线段检索,再根据这些线段算出夹角,利用角度的加权平均值和频率最高的思想作为旋转的最佳角度。

import cv2
import numpy as np
from math import *
from scipy.stats import mode


__author__ = "zxsuperstar"
__email__ = "[email protected]"


class ImgCorrect():
    def __init__(self, img):
        self.img = img
        self.h, self.w, self.channel = self.img.shape
        if self.w <= self.h:
            self.scale = 700 / self.w
            self.w_scale = 700
            self.h_scale = self.h * self.scale
            self.img = cv2.resize(self.img, (0, 0), fx=self.scale, fy=self.scale, interpolation=cv2.INTER_NEAREST)
        else:
            self.scale = 700 / self.h
            self.h_scale = 700
            self.w_scale = self.w * self.scale
            self.img = cv2.resize(self.img, (0, 0), fx=self.scale, fy=self.scale, interpolation=cv2.INTER_NEAREST)
        self.gray = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)


    def img_lines(self):
        ret, binary = cv2.threshold(self.gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
        # cv2.imshow("bin",binary)
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))  # 矩形结构
        binary = cv2.dilate(binary, kernel)  # 膨胀
        edges = cv2.Canny(binary, 50, 200)
        # cv2.imshow("edges", edges)
        self.lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=100, maxLineGap=20)
        print(self.lines)
        if self.lines is None:
            print("Line segment not found")
            return None

        lines1 = self.lines[:, 0, :]  # 提取为二维
        # print(lines1)
        imglines = self.img.copy()
        for x1, y1, x2, y2 in lines1[:]:
            cv2.line(imglines, (x1, y1), (x2, y2), (0, 255, 0), 3)
        return imglines

    def search_lines(self):
        lines = self.lines[:, 0, :]  # 提取为二维
        # k = [(y2 - y1) / (x2 - x1) for x1, y1, x2, y2 in lines]
        # sorted_k = sorted(lines, key=lambda x:(x[3] - x[1]) / (x[2] - x[0]))
        number_inexistence_k = 0
        sum_positive_k45 = 0
        number_positive_k45 = 0
        sum_positive_k90 = 0
        number_positive_k90 = 0
        sum_negative_k45 =0
        number_negative_k45 = 0
        sum_negative_k90 = 0
        number_negative_k90 = 0
        sum_zero_k = 0
        number_zero_k = 0
        for x in lines:
            if x[2] == x[0]:
                number_inexistence_k += 1
                continue
            print(degrees(atan((x[3] - x[1]) / (x[2] - x[0]))),"pos:",x[0],x[1],x[2],x[3],"斜率:",(x[3] - x[1]) / (x[2] - x[0]))
            if 0 < degrees(atan((x[3] - x[1]) / (x[2] - x[0]))) < 45:
                number_positive_k45 +=1
                sum_positive_k45 += degrees(atan((x[3] - x[1]) / (x[2] - x[0])))
            if 45 <= degrees(atan((x[3] - x[1]) / (x[2] - x[0]))) < 90:
                number_positive_k90 += 1
                sum_positive_k90 += degrees(atan((x[3] - x[1]) / (x[2] - x[0])))
            if -45 < degrees(atan((x[3] - x[1]) / (x[2] - x[0]))) < 0:
                number_negative_k45 += 1
                sum_negative_k45 += degrees(atan((x[3] - x[1]) / (x[2] - x[0])))
            if -90 < degrees(atan((x[3] - x[1]) / (x[2] - x[0]))) <= -45:
                number_negative_k90 += 1
                sum_negative_k90 += degrees(atan((x[3] - x[1]) / (x[2] - x[0])))
            if x[3] == x[1]:
                number_zero_k += 1

        max_number = max(number_inexistence_k,number_positive_k45, number_positive_k90, number_negative_k45, number_negative_k90,number_zero_k)
        # print(number_inexistence_k,number_positive_k45, number_positive_k90, number_negative_k45, number_negative_k90,number_zero_k)
        if max_number == number_inexistence_k:
            return 90
        if max_number == number_positive_k45:
            return sum_positive_k45 / number_positive_k45
        if max_number == number_positive_k90:
            return sum_positive_k90 / number_positive_k90
        if max_number == number_negative_k45:
            return sum_negative_k45 / number_negative_k45
        if max_number == number_negative_k90:
            return sum_negative_k90 / number_negative_k90
        if max_number == number_zero_k:
            return 0

    def rotate_image(self,degree):
        """
        正角 逆时针旋转
        :param degree:
        :return:
        """
        print("degree:", degree)
        if -45 <= degree <= 0:
            degree = degree # #负角度 顺时针
        if -90<= degree < -45:
            degree = 90 + degree #正角度 逆时针
        if 0 < degree <= 45:
            degree = degree #正角度 逆时针
        if 45 < degree <90:
            degree = degree - 90 #负角度 顺时针
        print("rotate degree:", degree)
        # degree = -45
        # # 获取旋转后4角的填充色
        filled_color = -1
        if filled_color == -1:
            filled_color = mode([self.img[0, 0], self.img[0, -1],
                                 self.img[-1, 0], self.img[-1, -1]]).mode[0]
        if np.array(filled_color).shape[0] == 2:
            if isinstance(filled_color, int):
                filled_color = (filled_color, filled_color, filled_color)
        else:
            filled_color = tuple([int(i) for i in filled_color])

        # degree = degree - 90
        height, width = self.img.shape[:2]
        heightNew = int(width * fabs(sin(radians(degree))) + height * fabs(cos(radians(degree))))  # 这个公式参考之前内容
        widthNew = int(height * fabs(sin(radians(degree))) + width * fabs(cos(radians(degree))))

        matRotation = cv2.getRotationMatrix2D((width / 2, height / 2), degree, 1) #逆时针旋转 degree

        matRotation[0, 2] += (widthNew - width) / 2  # 因为旋转之后,坐标系原点是新图像的左上角,所以需要根据原图做转化
        matRotation[1, 2] += (heightNew - height) / 2

        imgRotation = cv2.warpAffine(self.img, matRotation, (widthNew, heightNew), borderValue=filled_color)
        # imgRotation = cv2.warpAffine(self.img, matRotation, (widthNew, heightNew), borderValue=(255, 255, 255))

        # 填充四个角
        # mask = np.zeros((heightNew + 2, widthNew + 2), np.uint8)
        # mask[:] = 0
        # seed_points = [(0, 0), (0, heightNew - 1), (widthNew - 1, 0),
        #                (widthNew - 1, heightNew - 1)]
        # for i in seed_points:
        #     cv2.floodFill(imgRotation, mask, i, filled_color)

        cv2.imshow("imgRotation", imgRotation)
        # cv2.imwrite("./image/r14a.jpg", imgRotation)
        return imgRotation

if __name__ == "__main__":
    im = cv2.imread("./image/tilt1.jpg")
    imgcorrect = ImgCorrect(im)
    cv2.imshow("normalization image", imgcorrect.img)
    lines_img = imgcorrect.img_lines()
    print(type(lines_img))
    if lines_img is None:
        imgcorrect.rotate_image(0)
    # cv2.imshow("lines_img",lines_img)
    else:
        # cv2.imshow("lines_img", lines_img)
        degree = imgcorrect.search_lines()
        imgcorrect.rotate_image(degree)
    cv2.waitKey()

 
扫描二维码关注公众号,回复: 6790564 查看本文章

猜你喜欢

转载自blog.csdn.net/zx_good_night/article/details/90716329