Определить, если изображение перевернуто
У меня есть несколько сотен изображений (отсканированных документов), большинство из которых искажены. Я хотел де-перекосить их, используя Python.
Вот код, который я использовал:
import numpy as np
import cv2
from skimage.transform import radon
filename = 'path_to_filename'
# Load file, converting to grayscale
img = cv2.imread(filename)
I = cv2.cvtColor(img, COLOR_BGR2GRAY)
h, w = I.shape
# If the resolution is high, resize the image to reduce processing time.
if (w > 640):
I = cv2.resize(I, (640, int((h / w) * 640)))
I = I - np.mean(I) # Demean; make the brightness extend above and below zero
# Do the radon transform
sinogram = radon(I)
# Find the RMS value of each row and find "busiest" rotation,
# where the transform is lined up perfectly with the alternating dark
# text and white lines
r = np.array([np.sqrt(np.mean(np.abs(line) ** 2)) for line in sinogram.transpose()])
rotation = np.argmax(r)
print('Rotation: {:.2f} degrees'.format(90 - rotation))
# Rotate and save with the original resolution
M = cv2.getRotationMatrix2D((w/2,h/2),90 - rotation,1)
dst = cv2.warpAffine(img,M,(w,h))
cv2.imwrite('rotated.jpg', dst)
Этот код хорошо работает с большинством документов, за исключением некоторых углов: (180 и 0) и (90 и 270) часто определяются как один и тот же угол (т.е. Он не имеет значения между (180 и 0) и (90 и 270)). Поэтому я получаю много перевернутых документов.
Вот пример:
Полученное изображение совпадает с исходным.
Есть ли какие-либо предложения, чтобы определить, перевернуто ли изображение с помощью Opencv и Python?
PS: я пытался проверить ориентацию, используя данные EXIF, но это не привело ни к какому решению.
РЕДАКТИРОВАТЬ:
Можно определить ориентацию с помощью Tesseract (pytesseract для Python), но это возможно только тогда, когда изображение содержит много символов.
Для тех, кому это может понадобиться:
import cv2
import pytesseract
print(pytesseract.image_to_osd(cv2.imread(file_name)))
Если документ содержит достаточно символов, для Tesseract возможно определить ориентацию. Однако, когда на изображении мало линий, угол ориентации, предложенный Тессерактом, обычно неверен. Так что это не может быть 100% решением.
Ответы
Ответ 1
Скрипт Python3/OpenCV4 для выравнивания отсканированных документов.
Поверните документ и суммируйте строки. Когда документ имеет 0 и 180 градусов поворота, на изображении будет много черных пикселей:
Используйте метод учета результатов. Оценка каждого изображения для его сходства с рисунком зебры. Изображение с лучшим результатом имеет правильное вращение. Изображение, на которое вы ссылались, было отключено на 0,5 градуса. Я опустил некоторые функции для удобства чтения, полный код можно найти здесь.
# Rotate the image around in a circle
angle = 0
while angle <= 360:
# Rotate the source image
img = rotate(src, angle)
# Crop the center 1/3rd of the image (roi is filled with text)
h,w = img.shape
buffer = min(h, w) - int(min(h,w)/1.15)
roi = img[int(h/2-buffer):int(h/2+buffer), int(w/2-buffer):int(w/2+buffer)]
# Create background to draw transform on
bg = np.zeros((buffer*2, buffer*2), np.uint8)
# Compute the sums of the rows
row_sums = sum_rows(roi)
# High score --> Zebra stripes
score = np.count_nonzero(row_sums)
scores.append(score)
# Image has best rotation
if score <= min(scores):
# Save the rotatied image
print('found optimal rotation')
best_rotation = img.copy()
k = display_data(roi, row_sums, buffer)
if k == 27: break
# Increment angle and try again
angle += .75
cv2.destroyAllWindows()
Как определить, что документ перевернут? Заполните область от верха документа до первого не черного пикселя в изображении. Измерьте площадь желтым цветом. Изображение, которое имеет наименьшую площадь, будет изображением с правой стороны вверх:
# Find the area from the top of page to top of image
_, bg = area_to_top_of_text(best_rotation.copy())
right_side_up = sum(sum(bg))
# Flip image and try again
best_rotation_flipped = rotate(best_rotation, 180)
_, bg = area_to_top_of_text(best_rotation_flipped.copy())
upside_down = sum(sum(bg))
# Check which area is larger
if right_side_up < upside_down: aligned_image = best_rotation
else: aligned_image = best_rotation_flipped
# Save aligned image
cv2.imwrite('/home/stephen/Desktop/best_rotation.png', 255-aligned_image)
cv2.destroyAllWindows()
Ответ 2
Предполагая, что вы уже выполнили коррекцию угла на изображении, вы можете попробовать следующее, чтобы выяснить, перевернуто ли оно:
- Спроецируйте исправленное изображение на ось Y, чтобы получить "пик" для каждой линии. Важно: на самом деле почти всегда есть два подпика!
- Сгладьте эту проекцию, свернув гауссиан, чтобы избавиться от тонкой структуры, шума и т.д.
- Для каждого пика проверьте, находится ли более сильный суб-пик сверху или снизу.
- Рассчитайте долю пиков, у которых есть подпики на нижней стороне. Это ваша скалярная величина, которая дает вам уверенность, что изображение ориентировано правильно.
Нахождение пика на шаге 3 осуществляется путем нахождения участков со значениями выше среднего. Подпики затем находят через argmax.
Здесь фигура для иллюстрации подхода; Несколько строк вашего примера изображения
- Синий: оригинальная проекция
- Оранжевый: сглаженная проекция
- Горизонтальная линия: среднее значение сглаженной проекции для всего изображения.
вот некоторый код, который делает это:
import cv2
import numpy as np
# load image, convert to grayscale, threshold it at 127 and invert.
page = cv2.imread('Page.jpg')
page = cv2.cvtColor(page, cv2.COLOR_BGR2GRAY)
page = cv2.threshold(page, 127, 255, cv2.THRESH_BINARY_INV)[1]
# project the page to the side and smooth it with a gaussian
projection = np.sum(page, 1)
gaussian_filter = np.exp(-(np.arange(-3, 3, 0.1)**2))
gaussian_filter /= np.sum(gaussian_filter)
smooth = np.convolve(projection, gaussian_filter)
# find the pixel values where we expect lines to start and end
mask = smooth > np.average(smooth)
edges = np.convolve(mask, [1, -1])
line_starts = np.where(edges == 1)[0]
line_endings = np.where(edges == -1)[0]
# count lines with peaks on the lower side
lower_peaks = 0
for start, end in zip(line_starts, line_endings):
line = smooth[start:end]
if np.argmax(line) < len(line)/2:
lower_peaks += 1
print(lower_peaks / len(line_starts))
это печатает 0.125 для данного изображения, так что это не правильно ориентировано и должно быть перевернуто.
Обратите внимание, что этот подход может плохо работать, если на изображении присутствуют изображения или что-то не организованное (например, математика или рисунки). Еще одной проблемой будет слишком мало строк, что приведет к плохой статистике.
Также разные шрифты могут привести к разным дистрибутивам. Вы можете попробовать это на нескольких изображениях и посмотреть, работает ли подход. У меня недостаточно данных.
Ответ 3
Вы можете использовать модуль Alyn. Чтобы установить это:
pip install alyn
Затем, чтобы использовать его для создания изображений (взятых с домашней страницы):
from alyn import Deskew
d = Deskew(
input_file='path_to_file',
display_image='preview the image on screen',
output_file='path_for_deskewed image',
r_angle='offest_angle_in_degrees_to_control_orientation')'
d.run()
Обратите внимание, что Alyn
предназначена только для написания текстов.