Извлечение текста OpenCV

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

// calculate the local variances of the grayscale image Mat t_mean, t_mean_2; Mat grayF; outImg_gray.convertTo(grayF, CV_32F); int winSize = 35; blur(grayF, t_mean, cv::Size(winSize,winSize)); blur(grayF.mul(grayF), t_mean_2, cv::Size(winSize,winSize)); Mat varMat = t_mean_2 - t_mean.mul(t_mean); varMat.convertTo(varMat, CV_8U); // threshold the high variance regions Mat varMatRegions = varMat > 100; 

Когда вы получите изображение, подобное этому:

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

Затем, когда я показываю varMatRegions я получаю это изображение:

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

Как вы можете видеть, это несколько сочетает левый блок текста с заголовком карты, для большинства карт этот метод отлично работает, но на более сложных карточках это может вызвать проблемы.

Причина, по которой плохо контактировать, заключается в том, что она ограничивает рамку контура почти всей картой.

Может ли кто-нибудь предложить другой способ, я могу найти текст, чтобы обеспечить правильное обнаружение текста?

200 очков тому, кто может найти текст на карте выше этих двух.

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

Вы можете обнаружить текст, найдя тесные элементы (вдохновленные LPD):

 #include "opencv2/opencv.hpp" std::vector detectLetters(cv::Mat img) { std::vector boundRect; cv::Mat img_gray, img_sobel, img_threshold, element; cvtColor(img, img_gray, CV_BGR2GRAY); cv::Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT); cv::threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY); element = getStructuringElement(cv::MORPH_RECT, cv::Size(17, 3) ); cv::morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element); //Does the trick std::vector< std::vector< cv::Point> > contours; cv::findContours(img_threshold, contours, 0, 1); std::vector > contours_poly( contours.size() ); for( int i = 0; i < contours.size(); i++ ) if (contours[i].size()>100) { cv::approxPolyDP( cv::Mat(contours[i]), contours_poly[i], 3, true ); cv::Rect appRect( boundingRect( cv::Mat(contours_poly[i]) )); if (appRect.width>appRect.height) boundRect.push_back(appRect); } return boundRect; } 

Применение:

 int main(int argc,char** argv) { //Read cv::Mat img1=cv::imread("side_1.jpg"); cv::Mat img2=cv::imread("side_2.jpg"); //Detect std::vector letterBBoxes1=detectLetters(img1); std::vector letterBBoxes2=detectLetters(img2); //Display for(int i=0; i< letterBBoxes1.size(); i++) cv::rectangle(img1,letterBBoxes1[i],cv::Scalar(0,255,0),3,8,0); cv::imwrite( "imgOut1.jpg", img1); for(int i=0; i< letterBBoxes2.size(); i++) cv::rectangle(img2,letterBBoxes2[i],cv::Scalar(0,255,0),3,8,0); cv::imwrite( "imgOut2.jpg", img2); return 0; } 

Результаты:

а. element = getStructuringElement (cv :: MORPH_RECT, cv :: Size (17, 3)); imgOut1imgOut2

б. element = getStructuringElement (cv :: MORPH_RECT, cv :: Размер (30, 30)); imgOut1imgOut2

Результаты аналогичны для упомянутого другого изображения.

Я использовал метод, основанный на gradleиенте, в приведенной ниже программе. Добавлены результирующие изображения. Обратите внимание, что я использую уменьшенную версию изображения для обработки.

версия для c ++

 The MIT License (MIT) Copyright (c) 2014 Dhanushka Dangampola Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "stdafx.h" #include  #include  #include  #include  using namespace cv; using namespace std; #define INPUT_FILE "1.jpg" #define OUTPUT_FOLDER_PATH string("") int _tmain(int argc, _TCHAR* argv[]) { Mat large = imread(INPUT_FILE); Mat rgb; // downsample and use it for processing pyrDown(large, rgb); Mat small; cvtColor(rgb, small, CV_BGR2GRAY); // morphological gradient Mat grad; Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3)); morphologyEx(small, grad, MORPH_GRADIENT, morphKernel); // binarize Mat bw; threshold(grad, bw, 0.0, 255.0, THRESH_BINARY | THRESH_OTSU); // connect horizontally oriented regions Mat connected; morphKernel = getStructuringElement(MORPH_RECT, Size(9, 1)); morphologyEx(bw, connected, MORPH_CLOSE, morphKernel); // find contours Mat mask = Mat::zeros(bw.size(), CV_8UC1); vector> contours; vector hierarchy; findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); // filter contours for(int idx = 0; idx >= 0; idx = hierarchy[idx][0]) { Rect rect = boundingRect(contours[idx]); Mat maskROI(mask, rect); maskROI = Scalar(0, 0, 0); // fill the contour drawContours(mask, contours, idx, Scalar(255, 255, 255), CV_FILLED); // ratio of non-zero pixels in the filled region double r = (double)countNonZero(maskROI)/(rect.width*rect.height); if (r > .45 /* assume at least 45% of the area is filled if it contains text */ && (rect.height > 8 && rect.width > 8) /* constraints on region size */ /* these two conditions alone are not very robust. better to use something like the number of significant peaks in a horizontal projection as a third condition */ ) { rectangle(rgb, rect, Scalar(0, 255, 0), 2); } } imwrite(OUTPUT_FOLDER_PATH + string("rgb.jpg"), rgb); return 0; } 

версия для python

 The MIT License (MIT) Copyright (c) 2017 Dhanushka Dangampola Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import cv2 import numpy as np large = cv2.imread('1.jpg') rgb = cv2.pyrDown(large) small = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) grad = cv2.morphologyEx(small, cv2.MORPH_GRADIENT, kernel) _, bw = cv2.threshold(grad, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1)) connected = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, kernel) # using RETR_EXTERNAL instead of RETR_CCOMP contours, hierarchy = cv2.findContours(connected.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) mask = np.zeros(bw.shape, dtype=np.uint8) for idx in range(len(contours)): x, y, w, h = cv2.boundingRect(contours[idx]) mask[y:y+h, x:x+w] = 0 cv2.drawContours(mask, contours, idx, (255, 255, 255), -1) r = float(cv2.countNonZero(mask[y:y+h, x:x+w])) / (w * h) if r > 0.45 and w > 8 and h > 8: cv2.rectangle(rgb, (x, y), (x+w-1, y+h-1), (0, 255, 0), 2) cv2.imshow('rects', rgb) 

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

Вот альтернативный подход, который я использовал для обнаружения текстовых блоков:

  1. Преобразование изображения в оттенки серого
  2. Прикладной порог (простой двоичный порог, с выбранным значением 150 в качестве порогового значения)
  3. Примененная дилатация для сгущения линий на изображении, что приводит к более компактным объектам и fragmentам с меньшим количеством пробелов. Используется большое значение для количества итераций, поэтому дилатация очень тяжелая (13 итераций, а также выбраны для оптимальных результатов).
  4. Определенные контуры объектов в приведенном изображении с использованием opencv findContours .
  5. Нарисовал ограничивающий прямоугольник (прямоугольник), описывающий каждый контурный объект – каждый из них создает блок текста.
  6. Опционально отбрасываемые области, которые вряд ли будут объектом, который вы ищете (например, текстовые блоки), учитывая их размер, поскольку вышеприведенный алгоритм также может найти пересекающиеся или вложенные объекты (например, всю верхнюю область для первой карты), некоторые из которых могут быть неинтересный для ваших целей.

Ниже приведен код, написанный на питоне с pyopencv, его легко переносить на C ++.

 import cv2 image = cv2.imread("card.png") gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # grayscale _,thresh = cv2.threshold(gray,150,255,cv2.THRESH_BINARY_INV) # threshold kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3)) dilated = cv2.dilate(thresh,kernel,iterations = 13) # dilate _, contours, hierarchy = cv2.findContours(dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) # get contours # for each contour found, draw a rectangle around it on original image for contour in contours: # get rectangle bounding contour [x,y,w,h] = cv2.boundingRect(contour) # discard areas that are too large if h>300 and w>300: continue # discard areas that are too small if h<40 or w<40: continue # draw rectangle around contour on original image cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,255),2) # write original image with added contours to disk cv2.imwrite("contoured.jpg", image) 

Исходное изображение - первое изображение в вашем посте.

После предварительной обработки (оттенки серого, порог и расширение - так после шага 3) изображение выглядело так:

Разбавленное изображение

Ниже приведен образ («contoured.jpg» в последней строке); конечные ограничивающие поля для объектов на изображении выглядят следующим образом:

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

Вы можете видеть, что текстовый блок слева определяется как отдельный блок, ограниченный его окружением.

Используя тот же скрипт с теми же параметрами (кроме типа порога, который был изменен для второго изображения, как описано ниже), вот результаты для двух других карт:

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

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

Настройка параметров

Параметры (пороговое значение, параметры дилатации) были оптимизированы для этого изображения и эта задача (поиск текстовых блоков) и может быть скорректирована, если необходимо, для других изображений карт или других типов объектов.

Для порогового значения (шаг 2) я использовал черный порог. Для изображений, где текст светлее фона, например, второе изображение в вашем посте, должен использоваться белый порог, поэтому замените тип cv2.THRESH_BINARY ). Для второго изображения я также использовал немного более высокое значение для порога (180). Изменение параметров порогового значения и количества итераций для дилатации приведет к разной степени чувствительности при разграничении объектов на изображении.

Поиск других типов объектов:

Например, уменьшение дилатации до 5 итераций в первом изображении дает нам более точное разделение объектов на изображении, грубо нахождение всех слов на изображении (а не текстовых блоков):

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

Зная грубый размер слова, здесь я отбросил области, которые были слишком маленькими (шириной или высотой ниже 20 пикселей) или слишком большими (более 100 пикселей ширины или высоты), чтобы игнорировать объекты, которые вряд ли будут словами, чтобы получить результаты в вышеупомянутое изображение.

Подход @ dhanushka показал самое promise, но я хотел поиграть в Python, поэтому пошел вперед и перевел его для удовольствия:

 import cv2 import numpy as np from cv2 import boundingRect, countNonZero, cvtColor, drawContours, findContours, getStructuringElement, imread, morphologyEx, pyrDown, rectangle, threshold large = imread(image_path) # downsample and use it for processing rgb = pyrDown(large) # apply grayscale small = cvtColor(rgb, cv2.COLOR_BGR2GRAY) # morphological gradient morph_kernel = getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) grad = morphologyEx(small, cv2.MORPH_GRADIENT, morph_kernel) # binarize _, bw = threshold(src=grad, thresh=0, maxval=255, type=cv2.THRESH_BINARY+cv2.THRESH_OTSU) morph_kernel = getStructuringElement(cv2.MORPH_RECT, (9, 1)) # connect horizontally oriented regions connected = morphologyEx(bw, cv2.MORPH_CLOSE, morph_kernel) mask = np.zeros(bw.shape, np.uint8) # find contours im2, contours, hierarchy = findContours(connected, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) # filter contours for idx in range(0, len(hierarchy[0])): rect = x, y, rect_width, rect_height = boundingRect(contours[idx]) # fill the contour mask = drawContours(mask, contours, idx, (255, 255, 2555), cv2.FILLED) # ratio of non-zero pixels in the filled region r = float(countNonZero(mask)) / (rect_width * rect_height) if r > 0.45 and rect_height > 8 and rect_width > 8: rgb = rectangle(rgb, (x, y+rect_height), (x+rect_width, y), (0,255,0),3) 

Теперь, чтобы отобразить изображение:

 from PIL import Image Image.fromarray(rgb).show() 

Не самый Pythonic скриптов, но я старался как можно ближе напоминать оригинальный код на C ++ для читателей.

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

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

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

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

Вы можете попробовать этот метод , разработанный Chucai Yi и Yingli Tian.

Они также используют программное обеспечение (основанное на Opencv-1.0, и оно должно работать под платформой Windows), которое вы можете использовать (хотя исходный код не доступен). Он будет генерировать все текстовые ограничивающие прямоугольники (показаны в цветовых тенях) на изображении. Обратившись к своим образцам, вы получите следующие результаты:

Примечание. Чтобы сделать результат более надежным, вы можете объединить соседние ящики вместе.


Обновление. Если ваша конечная цель – распознать тексты на изображении, вы можете дополнительно проверить gttext , который является бесплатным программным обеспечением OCR и инструментом Ground Truthing для цветных изображений с текстом. Исходный код также доступен.

При этом вы можете получить распознанные тексты, такие как:

Выше кода JAVA версия: Спасибо @William

 public static List detectLetters(Mat img){ List boundRect=new ArrayList<>(); Mat img_gray =new Mat(), img_sobel=new Mat(), img_threshold=new Mat(), element=new Mat(); Imgproc.cvtColor(img, img_gray, Imgproc.COLOR_RGB2GRAY); Imgproc.Sobel(img_gray, img_sobel, CvType.CV_8U, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT); //at src, Mat dst, double thresh, double maxval, int type Imgproc.threshold(img_sobel, img_threshold, 0, 255, 8); element=Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(15,5)); Imgproc.morphologyEx(img_threshold, img_threshold, Imgproc.MORPH_CLOSE, element); List contours = new ArrayList(); Mat hierarchy = new Mat(); Imgproc.findContours(img_threshold, contours,hierarchy, 0, 1); List contours_poly = new ArrayList(contours.size()); for( int i = 0; i < contours.size(); i++ ){ MatOfPoint2f mMOP2f1=new MatOfPoint2f(); MatOfPoint2f mMOP2f2=new MatOfPoint2f(); contours.get(i).convertTo(mMOP2f1, CvType.CV_32FC2); Imgproc.approxPolyDP(mMOP2f1, mMOP2f2, 2, true); mMOP2f2.convertTo(contours.get(i), CvType.CV_32S); Rect appRect = Imgproc.boundingRect(contours.get(i)); if (appRect.width>appRect.height) { boundRect.add(appRect); } } return boundRect; } 

И используйте этот код на практике:

  System.loadLibrary(Core.NATIVE_LIBRARY_NAME); Mat img1=Imgcodecs.imread("abc.png"); List letterBBoxes1=Utils.detectLetters(img1); for(int i=0; i< letterBBoxes1.size(); i++) Imgproc.rectangle(img1,letterBBoxes1.get(i).br(), letterBBoxes1.get(i).tl(),new Scalar(0,255,0),3,8,0); Imgcodecs.imwrite("abc1.png", img1); 

Реализация Python для решения @ dhanushka:

 def process_rgb(rgb): hasText = 0 gray = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY); morphKernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)) grad = cv2.morphologyEx(gray, cv2.MORPH_GRADIENT, morphKernel) # binarize _, bw = cv2.threshold(grad, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # connect horizontally oriented regions morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1)) connected = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, morphKernel) # find contours mask = np.zeros(bw.shape[:2], dtype="uint8"); _,contours, hierarchy = cv2.findContours(connected, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) # filter contours idx = 0 while idx >= 0: x,y,w,h = cv2.boundingRect(contours[idx]); # fill the contour cv2.drawContours(mask, contours, idx, (255, 255, 255), cv2.FILLED); # ratio of non-zero pixels in the filled region r = cv2.contourArea(contours[idx])/(w*h) if(r > 0.45 and h > 5 and w > 5 and w > h): cv2.rectangle(rgb, (x,y), (x+w,y+h), (0, 255, 0), 2) hasText = 1 idx = hierarchy[0][idx][0] return hasText, rgb 
  • Как печатать цвет в консоли с помощью System.out.println?
  • Как добавить разрыв строки в Android TextView?
  • Обосновать последнюю строку div?
  • Тайм-аут в async / wait
  • Чтобы нарисовать подчеркивание под TextView в Android
  • Как я могу определить кодировку / кодовую страницу текстового файла
  • Подчеркивание текста в UIButton
  • Подпиксельный сглаженный текст в элементе canvasа HTML5
  • Вертикальная (повернутая) метка в Android
  • андроидный эллипсовый многострочный текст
  • Масштабировать текст в режиме просмотра?
  • Давайте будем гением компьютера.