Nano Hash - криптовалюты, майнинг, программирование

Локальное усиление контраста для распознавания цифр с помощью cv2/pytesseract

Я хочу использовать pytesseract для чтения цифр с изображений. Изображения выглядят следующим образом:

введите здесь описание изображения

введите здесь описание изображения

Цифры расставлены точками, и чтобы использовать pytesseract, мне нужны черные соединенные цифры на белом фоне. Для этого я подумал об использовании размытия и расширения в качестве методов предварительной обработки. Как видите, изображения похожи, но в некоторых аспектах они совершенно разные. Например, точки на первом изображении темнее фона, а на втором — белее. Это означает, что на первом изображении я могу использовать erode, чтобы получить черные соединенные линии, а на втором изображении я могу использовать расширение, чтобы получить белые соединенные линии, а затем инвертировать цвета. Это приводит к следующим результатам:

введите здесь описание изображения

введите здесь описание изображения

Используя соответствующий порог, первое изображение можно легко прочитать с помощью pytesseract. Второй образ, кто угодно, более хитрый. Проблема в том, что, например, части 4 темнее, чем фон вокруг трех. Так что простой порог не сработает. Мне нужно что-то вроде локального порога или локального усиления контраста. У кого-нибудь есть идея здесь?

Редактировать:

OTSU, средний порог и гауссов порог приводят к следующим результатам:

введите здесь описание изображения


  • Вы уже пробовали порог OTSU? 11.01.2021
  • спасибо за идею, я только что попробовал, и это не работает. Я также пробовал порог Гаусса и средний порог, но они не работают должным образом. 11.01.2021

Ответы:


1

Ваши изображения имеют довольно низкое разрешение, но вы можете попробовать метод под названием Gain Division. Идея состоит в том, что вы пытаетесь построить модель фона, а затем взвешиваете каждый входной пиксель по этой модели. Выходное усиление должно быть относительно постоянным в течение большей части изображения.

После выполнения разделения усиления вы можете попытаться улучшить изображение, применив фильтр области и морфологию. Я попробовал только ваше первое изображение, потому что оно наименее худшее.

Вот шаги, чтобы получить изображение с разделением по усилению:

  1. Примените мягкий фильтр среднее размытие, чтобы избавиться от высокочастотного шума.
  2. Получите модель фона через локальный максимум. Примените очень сильную операцию close с большим structuring element (я использую прямоугольное ядро ​​размером 15).
  3. Выполните настройку усиления, разделив 255 между каждым локальным максимальным пикселем. Взвешивайте это значение с каждым пикселем входного изображения.
  4. Вы должны получить красивое изображение, где фоновое освещение в значительной степени нормализовано, threshold это изображение, чтобы получить бинарную маску символов.

Теперь вы можете улучшить качество изображения, выполнив следующие дополнительные действия:

  1. Threshold через Otsu, но добавьте небольшую предвзятость. (К сожалению, это ручной шаг в зависимости от ввода).

  2. Примените фильтр области, чтобы отфильтровать мелкие пятна шума.

Давайте посмотрим код:

import numpy as np
import cv2

# image path
path = "C:/opencvImages/"
fileName = "iA904.png"

# Reading an image in default mode:
inputImage = cv2.imread(path+fileName)

# Remove small noise via median:
filterSize = 5
imageMedian = cv2.medianBlur(inputImage, filterSize)

# Get local maximum:
kernelSize = 15
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
localMax = cv2.morphologyEx(imageMedian, cv2.MORPH_CLOSE, maxKernel, None, None, 1, cv2.BORDER_REFLECT101)

# Perform gain division
gainDivision = np.where(localMax == 0, 0, (inputImage/localMax))

# Clip the values to [0,255]
gainDivision = np.clip((255 * gainDivision), 0, 255)

# Convert the mat type from float to uint8:
gainDivision = gainDivision.astype("uint8") 

# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(gainDivision, cv2.COLOR_BGR2GRAY)

Вот что дает вам деление усиления:

Обратите внимание, что освещение более сбалансировано. Теперь давайте немного улучшим контраст:

# Contrast Enhancement:
grayscaleImage = np.uint8(cv2.normalize(grayscaleImage, grayscaleImage, 0, 255, cv2.NORM_MINMAX))

Вы получаете это, что создает немного больше контраста между передним планом и фоном:

Теперь давайте попробуем установить порог для этого изображения, чтобы получить красивую бинарную маску. Как я и предложил, попробуйте пороговую обработку Оцу, но добавьте (или уберите) немного смещения к результату. Этот шаг, как уже упоминалось, зависит от качества вашего ввода:

# Threshold via Otsu + bias adjustment:
threshValue, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

threshValue = 0.9 * threshValue
_, binaryImage = cv2.threshold(grayscaleImage, threshValue, 255, cv2.THRESH_BINARY)

В итоге вы получите эту бинарную маску:

Инвертируйте это и отфильтруйте маленькие капли. Я установил пороговое значение area в 10 пикселей:

# Invert image:
binaryImage = 255 - binaryImage

# Perform an area filter on the binary blobs:
componentsNumber, labeledImage, componentStats, componentCentroids = \
cv2.connectedComponentsWithStats(binaryImage, connectivity=4)

# Set the minimum pixels for the area filter:
minArea = 10

# Get the indices/labels of the remaining components based on the area stat
# (skip the background component at index 0)
remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]

# Filter the labeled pixels based on the remaining labels,
# assign pixel intensity to 255 (uint8) for the remaining pixels
filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype("uint8")

И это окончательная бинарная маска:

Если вы планируете отправить это изображение в OCR, вы можете сначала применить некоторую морфологию. Может быть, closing, чтобы попытаться соединить точки, из которых состоят персонажи. Также не забудьте обучить классификатор OCR шрифту, который близок к тому, что вы на самом деле пытаетесь распознать. Это (перевернутая) маска после операции размером 3 rectangular closing с 3 итерациями:

Редактировать:

Чтобы получить последнее изображение, обработайте отфильтрованный вывод следующим образом:

# Set kernel (structuring element) size:
kernelSize = 3

# Set operation iterations:
opIterations = 3

# Get the structuring element:
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))

# Perform closing:
closingImage = cv2.morphologyEx(filteredImage, cv2.MORPH_CLOSE, maxKernel, None, None, opIterations, cv2.BORDER_REFLECT101)

# Invert image to obtain black numbers on white background:
closingImage = 255 - closingImage
11.01.2021
  • потрясающий ответ! Спасибо большое, обязательно попробую все, что вы посоветовали :) 12.01.2021
  • @spadel Рад, что смог помочь, друг мой! 12.01.2021
  • Я почему-то не могу воспроизвести последний шаг (операцию закрытия). Я пробовал следующее: kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3)) closing = cv2.morphologyEx(filteredImage, cv2.MORPH_CLOSE, kernel) Но это не имеет никакого эффекта. Я попытался применить отфильтрованное двоичное изображение и его инверсию. 12.01.2021
  • @spadel Проверьте мое редактирование, я добавил код для получения последнего изображения. 12.01.2021
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..