OCR-Python-Opencv一种基于坐标投影的图片旋转矫正方法

        由于最近公司业务需要使用OCR功能,所以我不得不研究Opencv这个图像库,进行OCR功能前期的图像处理工作,二值化、灰度化什么的都非常简单这里就不过多讲述,下面记录我自己受到启发自己实现的一种基于坐标投影的图片矫正算法,其实思路非常简单,大神勿喷,再次做一个小分享也算是我自己对该方面知识的一个总结和整理。

ps:该算法适合旋转角度不大,在-35~35度的旋转角矫正,具备一定抗干扰能力(当然也不是万能的,这个算法可以当做其它矫正算法的补充)

首先来看一下效果

左边是原图,右边是矫正后的图

这张图我为了验证其抗干扰能力,我刻意加了一下乱七八糟的东西干扰,效果看起来视乎还是不错的

这张视乎还有一点点倾斜,看起来视乎还不是很正,不过相对于原来的字来说已经正很多了。

原理

其实这个原理非常简单,就是把一张图经过“灰度化”“二值化”“自动阈值”后的图像从-35°到35度进行旋转遍历,每张旋转后的图像投影到x或者y坐标上(我这里是使用了投影到y坐标上),然后统计其在该坐标上的非0像素行数,非零像素行数最少的旋转角度就是该图片需要旋转的角度。下面是一张灰常直截了当的指示图,就是取其在Y坐标上像素的投影最少行数的角度就是其旋转角度。

弊端:

这个方式可以矫正大部分情况下的图片,但是对于一下全包围的图片可能无法进行矫正,并非万能的,仅供参考,例如下面的图就矫正不了,因为无论怎么旋转其投影都是一样的。

代码: 

好了废话了这么多直接贴代码咯,有兴趣的研究研究一起学习,欢迎下面评论。下面是一整个.py文件,拷贝过去就可以了,其中要的库报错的自行安装。

 基于投影的倾斜角计算.py

```

import cv2

import numpyas np

import math

import imutils

def strt():

image = cv2.imread('images/ttt.jpg', 0)

x, y = image.shape[0:2]

image = cv2.resize(image, (int(y /3), (int(x /3))))

normalImage = image

# 灰度图片

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 二值化

    cv2.imshow("hui du hua:", gray)# 展示图片

    edges = cv2.Canny(gray, 50, 120)

cv2.imshow("edges ", edges)

minLineLength =1

    maxLineGap =12

    binary = cv2.adaptiveThreshold(~gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 35, 0)

## 图片旋转

def rotate_bound(image, angle):

# # 获取宽高

# (h, w) = image.shape[:2]

# (cX, cY) = (w // 2, h // 2)

#

# # 提取旋转矩阵 sin cos

# M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)

    return imutils.rotate(image, -angle)# 18

"""

计算图片的旋转角度

"""

def getImageRotate(image):

imageHeight, imageWidth= image.shape[0:2]

print(imageWidth, ", ", imageHeight)

swapImage = image.copy()

templateImageWidth =0

    templateImageHeight =0

    toWidth =500

    if imageWidth > toWidthand imageWidth > imageHeight:

templateImageWidth = toWidth

templateImageHeight = toWidth / imageWidth * imageHeight

elif imageHeight > toWidthand imageHeight > imageWidth:

templateImageHeight = toWidth

templateImageWidth = toWidth / imageHeight * imageWidth

# 使用Numpy创建一张全黑纸

    lastImageWidth = templateImageWidth

if templateImageWidth < templateImageHeight:

lastImageWidth = templateImageHeight

else:

lastImageWidth = templateImageWidth

lastImageWidth =int(math.sqrt(lastImageWidth * lastImageWidth *2))

templateImage = np.zeros((lastImageWidth, lastImageWidth, 3), np.uint8)

# 使用黑色填充图片区域

    templateImage.fill(0)

# cv2.imshow("templateImage", templateImage)

    print(templateImageWidth, ", ", templateImageHeight)

swapImage = cv2.resize(swapImage, (int(templateImageWidth), int(templateImageHeight)))

# cv2.imshow("swapImage", swapImage)

    grayImage = cv2.cvtColor(swapImage, cv2.COLOR_BGR2GRAY)

# cv2.imshow("grayImage", grayImage)

# gaussianBlurImage = cv2.GaussianBlur(grayImage, (3, 3), 3)

    binaryImage = cv2.adaptiveThreshold(grayImage, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, 35)

# ret, thresh1 = cv2.threshold(binaryImage, 127, 255, cv2.THRESH_BINARY)  # 大于阈值为白

    swapBinnaryImage = ~binaryImage

# cv2.imshow("swapBinnaryImage", binaryImage)

    width, height = templateImage.shape[0:2]

center = (height //2, width //2)

print(width, height)

mask =255 * np.ones(swapBinnaryImage.shape, swapBinnaryImage.dtype)

checkBaseImage = cv2.seamlessClone(swapBinnaryImage, templateImage, mask, center, cv2.NORMAL_CLONE)

cv2.imshow("checkBaseImage", checkBaseImage)

minRotate =0

    minCount = -100

    maxPixSum = -100

    for rotatein range(-35, 35):

rotateImage = rotate_bound(checkBaseImage, rotate)

rotateImageWidth =len(rotateImage)

xPixList = []

pixSum =0

      for iin range(rotateImageWidth):

lineCount =0

          pixSum += cv2.sumElems(rotateImage[i])[0]

lineCount += cv2.countNonZero(rotateImage[i])

if lineCount >0:

xPixList.append(lineCount)

# if pixSum == -100:

#    maxPixSum = pixSum

#    minRotate = rotate

# if pixSum > maxPixSum:

#    maxPixSum = pixSum

#    minRotate = rotate

      if minCount == -100:

minCount =len(xPixList)

minRotate = rotate

# print(len(xPixList), ", ", minCount)

      if len(xPixList) < minCount:

minCount =len(xPixList)

minRotate = rotate

# print(minRotate)

    print("over: rotate = ", minRotate)

print("maxPixSum = ", maxPixSum)

return minRotate

if __name__=="__main__":

imagePath ="images/lei.jpg"

    image = cv2.imread(imagePath)

cv2.imshow("normalImage", image)

rotateAngle = getImageRotate(image)

print("lastAngle = ", rotateAngle)

image = rotate_bound(image, rotateAngle)

cv2.imshow("rotateImage", image)

cv2.waitKey(0)

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351