1. Происхождение
Красавица-секретарь компании знала, что я программист, занимающийся распознаванием изображений, и специально меня нашла. Она сказала, я была в контакте со всеми отраслями, но я не контактировала с мужчинами в вашей сфере ИТ.Я хотела бы спросить, вы делаете это напрямую? Я была немного ошеломлена и растеряна... Далее она спросила, есть ли какая-то подготовительная работа перед этим? Я сказал да. Она очень счастлива, это здорово, то есть перед тем, как распознать образ, должна быть какая-то предварительная обработка изображения. Бывает, что у меня есть на руках отсканированные документы, но они не стандартизированы, можете мне помочь?
Женщина-секретарь дала мне такую картинку, сказав, что пустая область этого конфиденциального документа слишком велика.Она хотела только текстовую область и попросила меня использовать программу, чтобы отметить текстовую область.
Два, ограничивающий прямоугольник boundingRect
Это требование слишком простое.
Моя первая мысль былаvc2
внутриboundingRect
Метод, это профессиональная рамка прямоугольной формы.
Через оттенки серого, инверсию и бинаризацию я его обработал, и в итоге передал в boundingRect для распознавания, быстро сделал, эффект такой:
код показывает, как показано ниже:
def boundingRect(image_path):
# 读入图片3通道 [[[255,255,255],[255,255,255]],[[255,255,255],[255,255,255]]]
image = cv2.imread(image_path)
# 转为灰度单通道 [[255 255],[255 255]]
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 黑白颠倒
gray = cv2.bitwise_not(gray)
# 二值化
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 获取最小包裹正矩形 x-x轴位置, y-y轴位置, w-宽度, h-高度
x, y, w, h = cv2.boundingRect(thresh)
left, top, right, bottom = x, y, x+w, y+h
# 把框画在图上
cv2.rectangle(image,(left, top), (right, bottom), (0, 0, 255), 2)
# 将处理好的文件保存到当前目录
#cv2.imwrite('img2_1_rotate.jpg', image)
# 将处理好的文件弹窗展示
# cv2.imshow("output", image)
# cv2.waitKey(0)
boundingRect('img1_0_origin.jpg')
Я хочу набраться смелости, чтобы пойти к секретарше, и я планирую рассказать ей свою идею реализации. Я боялся, что не смогу внятно объяснить после встречи, поэтому заранее сделал набросок.
первый звонокcv2.imread
Прочитано на картинке, на этот раз прочитано3
Исходное изображение канала, после прочтения, если позвонитьcv2.imshow("output", image)
Покажите, это исходное цветное изображение. Данные графика такие[[[255,255,255],[255,255,255]],[[255,255,255],[255,255,255]]]
, каждый пиксель имеетRGB
три значения.
Эти данные все еще немного сложны для распознавания границ текста. Потому что компьютеру все равно, красный он или зеленый, важно, есть он или нет. Поэтому необходимо вызватьcv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Обработка графики в градациях серого, обработанные графические данные упрощаются и становятся одноканальной коллекцией пикселей.[[255 255],[255 255]]
.
В этот момент значение белой области близко к255
, значение черной области близко к0
. Мы уделяем больше внимания словам в черной области, и ее значение на самом деле0
. Этого нельзя делать. Компьютер обычно игнорирует 0, т.к.255
стоит обратить внимание (именно поэтому многие наборы для обучения распознаванию текста белые на черном). Следовательно, необходим черно-белый разворот, чтобы сместить фокус на 255.
Алгоритм черно-белой инверсии очень прост, используйте255-
может.255-255=0,255-0=255
. Черное становится белым, а белое становится черным. Однако изображение в градациях серого0~255
числа между ними будут127、128
Такие нечерно-белые пиксели. также будет несколько5、6、7
Это какие-то заусенцы или тени, допустим, это слово, но я не вижу ясно, допустим, это не так, это еще смутно есть. В жизни надо решительно проводить "разрыв", "сдаваться" и "уходить", а программа тем более должна быть.Либо слова, либо пробелы, я так настроен, и это тоже для уменьшения количества расчет.
На данный момент существует только изображение0
или255
. дай этоboundingRect
Он может вернуть нужное нам значение.
Я передал процедуру женщине-секретарю и в итоге ничего не сказал.
Она была очень занята, поблагодарила меня и сказала попробовать позже.
Я взволнованно ждал ее ответа. За весь день я не написал ни строчки кода. Я несколько раз перечитывал код программы, которую дал ей, чтобы проверить, нет ли каких-либо упущений.
Три, прямоугольник минимальной площади minAreaRect
Секретарь позвала меня идти. Я сознательно пошел в туалет первым.
Я представил, как отвечу на ее благодарность, я должен улыбнуться, что бы я ни сказал, это вежливо, как и должно быть. Нет, я должен быть высокомерным. Это все тривиальные вопросы, и они будут сделаны в течение нескольких минут. Не забудьте связаться со мной в будущем. Разве это не достаточно дружелюбно...
Я ее еще видел, она сказала, что видимо проблема с программой, малая карта распознала не то, что хотела, я посмотрел.
Выяснилось, что конфиденциальный документ был отсканирован наискось, поэтому и рамка была наискось. Это исключительный случай.
Поскольку она не менеджер по продукту, я сдержал свой гнев. Я сказал, что вернусь и посмотрю.
Я нашел другой способ, которыйminAreaRect
, который может обрамлять наименьшую площадь площади. Даже если изображение наклонено, его рамку приходится наклонять, чтобы добиться минимальной площади. С углом наклона нормально повернуть его назад, и окончательный тест прошел успешно.
Я снова начала тренироваться перед зеркалом, на этот раз я должна набраться смелости, чтобы рассказать ей, как реализовать идею, а заодно еще и сказать причину неудачи в прошлый раз.
код показывает, как показано ниже:
def minAreaRect(image_path):
# 读入图片3通道 [[[255,255,255],[255,255,255]],[[255,255,255],[255,255,255]]]
image = cv2.imread(image_path)
# 转为灰度单通道 [[255 255],[255 255]]
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 黑白颠倒
gray = cv2.bitwise_not(gray)
# 二值化
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# %% 把大于0的点的行列找出来
ys, xs = np.where(thresh > 0)
# 组成坐标[[306 37][306 38][307 38]],里面都是非零的像素
coords = np.column_stack([xs,ys])
# 获取最小矩形的信息 返回值(中心点,长宽,角度)
rect = cv2.minAreaRect(coords)
angle = rect[-1] # 最后一个参数是角度
print(rect,angle) # ((26.8, 23.0), (320.2, 393.9), 63.4)
# %% 通过换算,获取四个顶点的坐标
box = np.int0(cv2.boxPoints(rect))
print(box) # [[15 181][367 5][510 292][158 468]]
# 画框,弹窗展示
cv2.drawContours(image, [box], 0, (0, 0, 255), 2)
cv2.imshow("output", image)
cv2.waitKey(0)
return angle
Изображение читается спереди, в оттенках серого, черно-белая инверсия, бинаризация иboundingRect
Лечить так же.
Разница в том, чтобы найти минимальную площадь,minAreaRect
Требуется, чтобы вы предоставили ему все непустые координаты. Он вычисляет эти координаты по собственному алгоритму и может рисовать прямоугольную область, которая не параллельна оси XY, а просто оборачивает эти точки координат.
minAreaRect
Возвращаемое значение необходимо интерпретировать((248.26095581054688, 237.67669677734375), (278.31488037109375, 342.6839904785156), 53.530765533447266)
, который делится на три части (координаты центральной точки x, y, длина и ширина h, w, угол a).
Давайте сначала посмотрим на угол а, если угол а является положительным числом.
Неважно, если вы не хотите их запоминать, есть способ получить положение текстового поля, называемоеcv2.boxPoints(rect)
, который можно получить изminAreaRect
Возвращаемое значение напрямую преобразует координаты четырех вершин[[15 181][367 5][510 292][158 468]]
.
Что касается того, как повернуть картинку... вам просто нужно сначала записать это, пока вы можете использовать его, потому что это не суть, нет необходимости тратить энергию на все, вы всегда должны оставить некоторые сожаления в жизни, чтобы компенсировать это.
# 传入图片数据数组和需要旋转的角度
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)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# 计算图像的新边界尺寸
nW = int((h * sin) + (w * cos))
nH = h
# 调整旋转矩阵
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
return cv2.warpAffine(image, M, (nW, nH),flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
Найдите угол, а затем поверните изображение
# 调用求角度
angle = minAreaRect('img2_0_rotate.jpg')
# 旋转图片,查看效果
image = rotate_bound(cv2.imread('img2_0_rotate.jpg'), 90-angle)
cv2.imshow("output", image)
cv2.waitKey(0)
Я взял программу, чтобы найти секретаршу, толкнул дверь и увидел, что она только что повесила свое пальто на вешалку. и очень жарко.
нет! Я джентльмен и не вижу несправедливости.
Я сказал, программа изменена, можете попробовать еще раз. Сказав это, я, краснея, хлопнула дверью.
Через некоторое время она снова позвонила мне и сказала, что, похоже, проблема с программой.
Когда я снова зашла, то обнаружила, что она надела пальто, было явно не очень холодно, так зачем же она надела пальто!
Она описала мне это явление. Она сказала, смотрите, обработанные картинки все равно не то, что она хотела, а текстовые картинки, которые она хотела подправить.
Я увидел, что эта ненормальная ситуация слишком ненормальна.Во-первых, есть наклонная прямоугольная область текста, и текст в этой области наклонен. Эта штука, как бы вы ее ни обрамляли, она не повернется правильно.
Красивая секретарша застенчиво спросила: Великий инженер, это сложно?
"Трудности, хахаха, ее не существует! Я вернусь первой и исправлю ее для вас, когда у меня будет время!" Я вышел прямо из комнаты, и в тот момент, когда я закрыл дверь, я был обескуражен. Как я могу это исправить?
Четыре, преобразование линии Хафа HoughLinesP
В это время уже нельзя думать ни о каких коробках и областях, и все коробки неэффективны. Не говоря уже о том, жаркая погода или нет, они будут только беспокоить ум.
Наконец-то я нашел способ повернуть этот извращенный курсивный текст вправо.
Я использую метод под названием霍夫变换
Методы.
霍夫变换
не только признать直线
, также может распознавать любую форму, распространенными являются круг, овал.
Я не жадный, я использую его только для определения здесь прямых линий.
# 读入图片3通道 [[[255,255,255],[255,255,255]],[[255,255,255],[255,255,255]]]
image = cv2.imread(image_path)
# 转为灰度单通道 [[255 255],[255 255]]
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 处理边缘
edges = cv2.Canny(gray, 500, 200)
# 求得所有直线的集合
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=30, maxLineGap=200)
print(lines) # [[[185 153 369 337]] [[128 172 355 362]]]
Обработка изображения в градациях серого такая же, как и раньше, основная цель — сделать данные краткими и эффективными.
Вот еще одинcv2.Canny(gray, 500, 200)
Обработка краев, эффект обработки заключается в следующем.
Цель этого по-прежнему состоит в том, чтобы сделать данные краткими и эффективными.
скажите что-тоCanny(gray, 500, 200)
из3
параметры.
- первое
1
Первый параметр — это данные изображения в градациях серого, так как он может обрабатывать только灰度图像
. - первое
2
параметры大阈值
, для установки刻画边缘的力度
, чем больше значение, тем грубее край, в определенной степени край не будет прерывисто соединяться в блоки. - первое
3
параметры小阈值
, за修补断开的边缘
, значение определяет тонкость ремонта.
Благодаря обработке краев мы получаем контур изображения, который в данный момент не влияет на исходную структуру изображения. Несмотря на то что2
Каждая точка определяет линию, но линия также может иметь точки 3, 4 и 5. Если условия подходят, линия может быть определена из точек.
cv2.HoughLinesP
На основе наброска, согласно условиям, подбрасывайте и включайте картинку для рисования линий, чтобы посмотреть, сколько прямых линий можно провести. Линий определенно много, но не все из них удовлетворяют условиям, по параметрам он может найти все отрезки линий, удовлетворяющие условиям, как показано ниже.
Интерпретируем его параметрыcv2.HoughLinesP(edges, 1, np.pi/180, threshold=30, maxLineGap=200)
.
- первое
1
параметрыedges
это пиксельные данные края. - первое
2
Первый параметр — перемещение на несколько пикселей за раз при поиске строки. - первое
3
Первый параметр — на сколько градусов поворачивать каждый раз при поиске линии. - первое
4
параметрыthreshold
Минимальное количество точек пересечения можно рассматривать как прямую линию. - первое
5
параметрыmaxLineGap
Максимальное расстояние в пикселях между двумя точками, рассматриваемыми как прямая линия, слишком велико, чтобы считаться прямой линией.
После этих просмотров вышли прямые линии.
С помощью прямой линии мы можем вычислить угол прямой линии.Здесь мы используем формулу обратной тригонометрической функции.
# 计算一条直线的角度
def calculateAngle(x1,y1,x2,y2):
x1 = float(x1)
x2 = float(x2)
y1 = float(y1)
y2 = float(y2)
if x2 - x1 == 0:
result=90 # 直线是竖直的
elif y2 - y1 == 0:
result=0 # 直线是水平的
else:
# 计算斜率
k = -(y2 - y1) / (x2 - x1)
# 求反正切,再将得到的弧度转换为度
result = np.arctan(k) * 57.29577
return result
Затем вычисляем углы всех линий. Затем выберите угол с наибольшей частотой, который в основном представляет собой общий угол наклона.
# 存储所有线段的倾斜角度
angles = []
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(image, (x1, y1), (x2, y2), (0,0,255))
angle = calculateAngle(x1, y1, x2, y2)
angles.append(round(angle))
# 找到最多出现的一个
mostAngle = Counter(angles).most_common(1)[0][0]
print("mostAngle:",mostAngle)
Наконец, мы вызываем метод для отображения эффекта поворота.
mostAngle = houghImg("img3_0_cut.jpg")
# 旋转图片,查看效果
image = rotate_bound(cv2.imread('img3_0_cut.jpg'), mostAngle)
cv2.imshow("output", image)
cv2.waitKey(0)
Таким образом, мы также можем исправить этот документ.
Я поспешно подошла к прекрасной секретарше и сообщила ей радостную новость.
Она была очень счастлива и сказала, что тоже недавно изучала программу, и попросила у меня исходный код.
Итак, я указал адрес проекта на github.GitHub.com/hollywood-industry/doc_from…дал ей.
Мы оба счастливо смеялись в тот день.
5. История продолжается...
Я тоже узнал позже.
У красивой секретарши есть маленький парень, а ее парень тоже занимается программированием.
Что касается исправления документов, то это не потребности красивой секретарши, а трудности, с которыми сталкивается ее парень в своей работе.
Я подумал о многих странных вещах: секретарь действительно знала о предобработке изображений, и предоставила такую профессиональную аномальную сцену, и она попросила у меня исходный код проекта.
Все это, казавшееся неразумным в то время, кажется таким разумным сейчас.
«Большой инженер, я жду тебя в номере 609 отеля «Счастье»… Пожалуйста, приходи!» Я получил от нее сообщение.
Сделать ошибку?
- «Большой инженер» — так она меня часто называет.
- Отель Happiness находится рядом с нашим офисным зданием.
нарочно? Что она собирается делать? Я теряюсь в мыслях...