Как реализовать имбинаризацию в OpenCV
Я разработал скрипт в Matlab, который анализирует гравированный текст на цветном краже. Я использую ряд морфологических методов, чтобы извлечь текст и прочитать его с помощью OCR. Мне нужно реализовать это на Raspberry Pi, поэтому я решил перенести мой код Matlab в OpenCV (в python). Я пытался перенести некоторые методы, и они работают аналогично, но как мне реализовать imreconstruct и imbinarize (как показано ниже) в OpenCV? (здесь уместно различать передний план и задний план).
Может быть, я должен попробовать добавить grabCut
или же getStructuringElement
или же morphologyEx
или же dilate
? Я пробовал их в различных комбинациях, но не нашел идеального решения.
Я напишу весь сценарий для обоих, если кто-нибудь может дать мне советы о том, как вообще улучшить это извлечение и точность процесса распознавания, я был бы очень признателен.
На основе значений бина серого изображения. Я изменяю некоторые параметры в этих функциях:
Matlab:
se = strel('disk', 300);
img = imtophat(img, se);
maker = imerode(img, strel('line',100,0)); %for whiter ones
maker = imerode(img, strel('line',85,0)); %for medium
maker = imerode(img, strel('line',5,0));
imgClear = imreconstruct(maker, img);
imgBlur = imgaussfilt(imgClear,1); %less blur for whiter frames
BW = imbinarize(imgBlur,'adaptive','ForegroundPolarity','Bright',...
'Sensitivity',0.7); %process for medium
BW = imbinarize(imgBlur, 'adaptive', 'ForegroundPolarity',...
'Dark', 'Sensitivity', 0.4); % process for black and white
res = ocr(BW, 'CharacterSet', '0123456789', 'TextLayout', 'Block');
res.Text;
OpenCV
kernel = numpy.ones((5,5),numpy.uint8)
blur = cv2.GaussianBlur(img,(5,5),0)
erosion = cv2.erode(blur,kernel,iterations = 1)
opening = cv2.morphologyEx(erosion, cv2.MORPH_OPEN, kernel)
#bremove = cv2.grabCut(opening,mask,rect,bgdModelmode==GC_INIT_WITH_MASK)
#th3 = cv2.adaptiveThreshold(opening,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU,11,2)
ret, thresh= cv2.threshold(opening,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
ocr = pytesseract.image_to_string(Image.open('image2.png'),config='stdout -c tessedit_char_whitelist=0123456789')
Вот входное изображение:
4 ответа
Я разработал код для получения положительного результата на основе вашего образца текста с гравировкой.
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
def show(img):
plt.imshow(img, cmap="gray")
plt.show()
# load the input image
img = cv2.imread('./imagesStackru/engraved_text.jpg',0);
show(img)
ret, mask = cv2.threshold(img, 60, 120, cv2.THRESH_BINARY) # turn 60, 120 for the best OCR results
kernel = np.ones((5,3),np.uint8)
mask = cv2.erode(mask,kernel,iterations = 1)
show(mask)
# I used a version of OpenCV with Tesseract, you may use your pytesseract and set the modes as:
# OCR Enginer Mode (OEM) = 3 (defualt = 3)
# Page Segmentation mode (PSmode) = 11 (defualt = 3)
tesser = cv2.text.OCRTesseract_create('C:/Program Files/Tesseract 4.0.0/tessdata/','eng','0123456789',11,3)
retval = tesser.run(mask, 0) # return string type
print 'OCR:' + retval
Обработанное изображение и OCR-вывод:
Было бы здорово, если бы вы могли отправить результаты теста с большим количеством образцов изображений.
Я удивлен тем, насколько велика разница между matlab и opencv, когда они оба используют один и тот же алгоритм. Почему ты бежишь imbinarize
дважды? Что на самом деле делает ключевое слово чувствительность (математически, за фоном). Потому что они, очевидно, на несколько шагов больше, чем просто голая ОЦУ.
import cv2
import numpy as np
import matplotlib.pyplot as plt
def show(img):
plt.imshow(img, cmap="gray")
plt.show()
img = cv2.imread("letters.jpg", cv2.IMREAD_GRAYSCALE)
kernel = np.ones((3,3), np.uint8)
blur = cv2.GaussianBlur(img,(3,3), 0)
erosion = cv2.erode(blur, kernel, iterations=3)
opening = cv2.dilate(erosion, kernel)
th3 = cv2.adaptiveThreshold(opening, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 45, 2)
show(th3)
kernel2 = cv2.getGaussianKernel(6, 2) #np.ones((6,6))
kernel2 = np.outer(kernel2, kernel2)
th3 = cv2.dilate(th3, kernel2)
th3 = cv2.erode(th3, kernel)
show(th3)
Отображаемые изображения:
После небольшой уборки:
Так что в целом не то же самое и, конечно, не так хорошо, как Matlab. Но основной принцип кажется тем же самым, просто с цифрами нужно играть.
Лучшим подходом, вероятно, было бы сделать порог по значению изображения, а затем использовать его вывод в качестве маски для адаптивного порога исходного изображения. Надеемся, что тогда результаты будут лучше, чем OpenCV и Matlab.
Попробуйте сделать это с помощью ADAPTIVE_THRESH_MEAN_C, вы можете получить действительно хорошие результаты, но вокруг будет больше мусора. Опять же, может быть, если вы можете использовать его в качестве маски для выделения текста, а затем снова выполнить трешолдинг, это может оказаться лучше. Кроме того, форма ядер эрозии и расширения будет иметь большое значение здесь.
Из вашего кода я могу видеть, что вы использовали фильтрацию tophat в своем коде Matlab в качестве первого шага. Тем не менее, я не мог увидеть то же самое в вашем коде OpenCV Python. Python имеет встроенный фильтр tophat, попробуйте применить его для получения аналогичного результата
kernel = np.ones((5,5),np.uint8)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
Кроме того, попробуйте использовать CLAHE, чтобы он лучше контрастировал с вашим изображением, а затем примените blackhat, чтобы отфильтровать мелкие детали.
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
Я получил лучшие результаты, применяя эти преобразования.
Попробовал ниже, он работает, чтобы распознать более легкий гравированный образец текста. Надеюсь, поможет.
def show(img):
plt.imshow(img, cmap="gray")
plt.show()
# load the input image
img = cv2.imread('./imagesStackru/engraved_text2.jpg',0);
show(img)
# apply CLAHE to adjust the contrast
clahe = cv2.createCLAHE(clipLimit=5.1, tileGridSize=(5,3))
cl1 = clahe.apply(img)
img = cl1.copy()
show(img)
img = cv2.GaussianBlur(img,(3,3), 1)
ret, mask = cv2.threshold(img, 125, 150, cv2.THRESH_BINARY) # turn 125, 150 for the best OCR results
kernel = np.ones((5,3),np.uint8)
mask = cv2.erode(mask,kernel,iterations = 1)
show(mask)
# I used a version of OpenCV with Tesseract, you may use your pytesseract and set the modes as:
# Page Segmentation mode (PSmode) = 11 (defualt = 3)
# OCR Enginer Mode (OEM) = 3 (defualt = 3)
tesser = cv2.text.OCRTesseract_create('C:/Program Files/Tesseract 4.0.0/tessdata/','eng','0123456789',11,3)
retval = tesser.run(mask, 0) # return string type
print 'OCR:' + retval