Я работаю с отсканированными документами (удостоверение личности, водительские права, ...).Проблема, с которой я столкнулся, применяя к ним некоторую предварительную обработку, заключается в том, что документы занимают лишь небольшую область изображения, а все остальное - пустое / зашумленное пространство.По этой причине я хотел разработать код Python, который автоматически обрезает нежелательную область и сохраняет только зону, в которой находится документ ( без предварительного определения разрешения для каждого документа ).Ну, это возможно с использованием findContours()
из OpenCV.Однако большинство документов (особенно старых) имеют нечеткий контур, а их конечности недостаточно четкие, чтобы их можно было обнаружить.Кроме того, шум в пустом пространстве также может быть обнаружен как контуры. Таким образом, контуры не будут работать во всех случаях.
Идея, которая пришла мне в голову:
- Считайте изображение и конвертируйтев оттенках серого.
- Примените функцию
bitwise_not()
из OpenCV, чтобы отделить фон от вспениваемого грунта. - Примените адаптивное среднее пороговое значение, чтобы устранить как можно больше шума (и, в конечном итоге, отбелитьфон).
На этом уровне у меня фон почти белый, а документ черный, но с белыми пробелами.
Итак, я применил эрозию, чтобы заполнить пробелы в части документа. Прочитайте каждую строку изображения и, если 20% его содержит черный, сохраните его, если он белый, удалите его.проделайте то же самое с каждым столбцом изображения. Обрежьте изображение в соответствии с минимальным и максимальным индексом черных линий и столбцов.
Вот мой код с некоторымикомментарии:
import cv2
import numpy as np
def crop(filename):
#Read the image
img = cv2.imread(filename)
#Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#Separate the background from the foreground
bit = cv2.bitwise_not(gray)
#Apply adaptive mean thresholding
amtImage = cv2.adaptiveThreshold(bit, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 35, 15)
#Apply erosion to fill the gaps
kernel = np.ones((15,15),np.uint8)
erosion = cv2.erode(amtImage,kernel,iterations = 2)
#Take the height and width of the image
(height, width) = img.shape[0:2]
#Ignore the limits/extremities of the document (sometimes are black, so they distract the algorithm)
image = erosion[50:height - 50, 50: width - 50]
(nheight, nwidth) = image.shape[0:2]
#Create a list to save the indexes of lines containing more than 20% of black.
index = []
for x in range (0, nheight):
line = []
for y in range(0, nwidth):
line2 = []
if (image[x, y] < 150):
line.append(image[x, y])
if (len(line) / nwidth > 0.2):
index.append(x)
#Create a list to save the indexes of columns containing more than 15% of black.
index2 = []
for a in range(0, nwidth):
line2 = []
for b in range(0, nheight):
if image[b, a] < 150:
line2.append(image[b, a])
if (len(line2) / nheight > 0.15):
index2.append(a)
#Crop the original image according to the max and min of black lines and columns.
img = img[min(index):max(index) + min(250, (height - max(index))* 10 // 11) , max(0, min(index2)): max(index2) + min(250, (width - max(index2)) * 10 // 11)]
#Save the image
cv2.imwrite('res_' + filename, img)
Вот пример: Я использовал изображение из Интернета, чтобы избежать проблем с конфиденциальностью
Здесь следует отметить, что качество изображения намного лучше (Пустое пространство не содержит шума), чем примеры, над которыми я работаю.
ВХОД: 1920x1080

ВЫХОД: 801x623
Я тестировал этот код с различными документами, и он хорошо работает.Проблема в том, что обработка одного документа занимает много времени (из-за циклов и чтения каждого пикселя изображения дважды: один раз со строками, а второй со столбцами).
Можно ли внести некоторые изменения воптимизировать код и сократить время обработки?
Любые предложения более чем приветствуются.
Спасибо.
РЕДАКТИРОВАТЬ:
Я забыл упомянуть, что я уже отправил тот же вопросв Проверка кода Stack Exchange , но я не получил ответа.Поэтому я пометил вопрос и попросил модераторов перенести его в StakOverflow.И так как я не получил ответ от модераторов, я решил опубликовать его здесь, потому что я думаю, что это тоже по теме здесь.Как только я получу ответ на одном из веб-сайтов, я удалю свой вопрос на другом веб-сайте, чтобы избежать избыточности.