Text extrahieren OpenCV


147

Ich versuche, die Begrenzungsrahmen von Text in einem Bild zu finden, und verwende derzeit diesen Ansatz:

// 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;

Bei einem Bild wie diesem:

Geben Sie hier die Bildbeschreibung ein

Wenn ich dann zeige, varMatRegionsbekomme ich dieses Bild:

Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen können, wird der linke Textblock etwas mit der Kopfzeile der Karte kombiniert. Bei den meisten Karten funktioniert diese Methode hervorragend, bei geschäftigeren Karten kann sie jedoch Probleme verursachen.

Der Grund für die Verbindung dieser Konturen ist, dass der Begrenzungsrahmen der Kontur fast die gesamte Karte einnimmt.

Kann jemand einen anderen Weg vorschlagen, wie ich den Text finden kann, um eine ordnungsgemäße Erkennung des Textes zu gewährleisten?

200 Punkte für jeden, der den Text auf der Karte über diesen beiden finden kann.

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein


1
Der einfachste Weg, den ich hier sehe, besteht darin, den Kontrast zu erhöhen, bevor ich die Regionen
erhalte

3
Coole Frage. Vielen Dank, dass Sie es veröffentlicht und das Kopfgeld gehostet haben, um so interessante Antworten zu erhalten.
Geoff

Neu in der Programmierung. Kann das Gleiche für Text in anderen Skripten als Englisch wie Sanskrit getan werden?
Vamshi Krishna

Antworten:


127

Sie können Text erkennen, indem Sie Elemente mit engen Kanten finden (inspiriert von einer LPD):

#include "opencv2/opencv.hpp"

std::vector<cv::Rect> detectLetters(cv::Mat img)
{
    std::vector<cv::Rect> 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<std::vector<cv::Point> > 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;
}

Verwendung:

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<cv::Rect> letterBBoxes1=detectLetters(img1);
    std::vector<cv::Rect> 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;
}

Ergebnisse:

ein. element = getStructuringElement (cv :: MORPH_RECT, cv :: Size (17, 3)); imgOut1 imgOut2

b. element = getStructuringElement (cv :: MORPH_RECT, cv :: Size (30, 30)); imgOut1 imgOut2

Die Ergebnisse für das andere erwähnte Bild sind ähnlich.


6
Kennzeichenmelder.
LovaBill

2
Bei einigen Karten enthält der Begrenzungsrahmen nicht den gesamten Text, z. B. wenn ein halber Buchstabe abgeschnitten wird. Wie diese Karte: i.imgur.com/tX3XrwH.jpg Wie kann ich die Höhe und Breite jedes Begrenzungsrahmens um erweitern n? Danke für die Lösung, es funktioniert super!
Clip

4
Sagen Sie cv::Rect a;. Vergrößert um n : a.x-=n/2;a.y-=n/2;a.width+=n;a.height+=n;.
LovaBill

2
Hallo, wie erreiche ich mit Python cv2 das gleiche Ergebnis?
11.


128

Ich habe im folgenden Programm eine gradientenbasierte Methode verwendet. Die resultierenden Bilder wurden hinzugefügt. Bitte beachten Sie, dass ich für die Verarbeitung eine verkleinerte Version des Bildes verwende.

c ++ Version

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 <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

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<vector<Point>> contours;
    vector<Vec4i> 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-Version

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)
#For opencv 3+ comment the previous line and uncomment the following line
#_, 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)

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein


3
Ich habe mir nur seinen Ansatz angesehen. Der Hauptunterschied, den ich sehe, ist, dass er einen Sobel-Filter verwendet, während ich einen morphologischen Gradientenfilter verwende. Ich denke, der morphologische Filter und das Downsampling glätten einen Großteil der nicht so starken Kanten. Sobel könnte mehr Lärm aufnehmen.
Dhanushka

1
@ascenator Wenn Sie OTSU mit dem Schwellenwerttyp kombinieren, wird der Otsu-Schwellenwert anstelle des angegebenen Schwellenwerts verwendet. Siehe hier .
Dhanushka

1
@VishnuJayanand Sie müssen nur eine Skalierung auf die anwenden rect. Es gibt eine pyrdown, also multiplizieren Sie x, y, width, heightdie rectmit 4.
Dhanushka

1
Könnten Sie uns bitte die dritte Bedingung mitteilen: die Anzahl der signifikanten Spitzen in einer horizontalen Projektion oder zumindest einen Vorsprung.
ISlimani

2
@DforTye Nehmen Sie die horizontale Projektion der gefüllten Kontur (cv :: reduzieren) und setzen Sie dann einen Schwellenwert (z. B. mit mittlerer oder mittlerer Höhe). Wenn Sie dieses Ergebnis visualisieren, sieht es wie ein Barcode aus. Ich denke, zu der Zeit dachte ich daran, die Anzahl der Balken zu zählen und ihr eine Schwelle aufzuerlegen. Wenn die Region sauber genug ist, kann es auch hilfreich sein, sie einer OCR zuzuführen und für jedes erkannte Zeichen ein Konfidenzniveau zu ermitteln, um sicherzustellen, dass die Region Text enthält.
Dhanushka

51

Hier ist ein alternativer Ansatz, mit dem ich die Textblöcke erkannt habe:

  1. Konvertierte das Bild in Graustufen
  2. Angewandter Schwellenwert (einfacher binärer Schwellenwert mit einem handverlesenen Wert von 150 als Schwellenwert)
  3. Anwenden der Erweiterung , um Linien im Bild zu verdicken, was zu kompakteren Objekten und weniger Leerraumfragmenten führt. Verwendet einen hohen Wert für die Anzahl der Iterationen, daher ist die Dilatation sehr stark (13 Iterationen, ebenfalls handverlesen, um optimale Ergebnisse zu erzielen).
  4. Identifizierte Konturen von Objekten im resultierenden Bild mithilfe der Funktion opencv findContours .
  5. Zeichnete einen Begrenzungsrahmen (Rechteck), der jedes konturierte Objekt umschreibt - jeder von ihnen umrahmt einen Textblock.
  6. Optional verworfene Bereiche, die aufgrund ihrer Größe wahrscheinlich nicht das gesuchte Objekt sind (z. B. Textblöcke), da der obige Algorithmus auch sich überschneidende oder verschachtelte Objekte (wie den gesamten oberen Bereich für die erste Karte) finden kann, von denen einige sein könnten uninteressant für Ihre Zwecke.

Unten ist der Code, der in Python mit pyopencv geschrieben wurde. Es sollte einfach sein, ihn nach C ++ zu portieren.

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) 

Das Originalbild ist das erste Bild in Ihrem Beitrag.

Nach der Vorverarbeitung (Graustufen, Schwellenwert und Dilatation - also nach Schritt 3) sah das Bild folgendermaßen aus:

Erweitertes Bild

Unten ist das resultierende Bild ("contoured.jpg" in der letzten Zeile); Die endgültigen Begrenzungsrahmen für die Objekte im Bild sehen folgendermaßen aus:

Geben Sie hier die Bildbeschreibung ein

Sie können sehen, dass der Textblock auf der linken Seite als separater Block erkannt wird, der von seiner Umgebung abgegrenzt ist.

Wenn Sie dasselbe Skript mit denselben Parametern verwenden (mit Ausnahme des Schwellenwerttyps, der für das zweite Bild wie unten beschrieben geändert wurde), sind hier die Ergebnisse für die anderen 2 Karten:

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Parameter einstellen

Die Parameter (Schwellenwert, Dilatationsparameter) wurden für dieses Bild und diese Aufgabe (Auffinden von Textblöcken) optimiert und können bei Bedarf für andere Kartenbilder oder andere zu findende Objekttypen angepasst werden.

Für den Schwellenwert (Schritt 2) habe ich einen schwarzen Schwellenwert verwendet. Für Bilder, bei denen der Text heller als der Hintergrund ist, z. B. das zweite Bild in Ihrem Beitrag, sollte ein weißer Schwellenwert verwendet werden. Ersetzen Sie daher den Schwellenwerttyp durch cv2.THRESH_BINARY). Für das zweite Bild habe ich auch einen etwas höheren Wert für den Schwellenwert (180) verwendet. Das Variieren der Parameter für den Schwellenwert und die Anzahl der Iterationen für die Dilatation führt zu unterschiedlichen Empfindlichkeitsgraden bei der Abgrenzung von Objekten im Bild.

Andere Objekttypen finden:

Wenn Sie beispielsweise die Dilatation im ersten Bild auf 5 Iterationen verringern, werden die Objekte im Bild feiner abgegrenzt, und es werden ungefähr alle Wörter im Bild gefunden (anstatt Textblöcke):

Geben Sie hier die Bildbeschreibung ein

Da ich die grobe Größe eines Wortes kannte, habe ich hier Bereiche verworfen, die zu klein (unter 20 Pixel Breite oder Höhe) oder zu groß (über 100 Pixel Breite oder Höhe) waren, um Objekte zu ignorieren, bei denen es sich wahrscheinlich nicht um Wörter handelt, um die Ergebnisse zu erhalten das obige Bild.


2
Du bist unglaublich! Ich werde es morgen früh versuchen.
Clip

Ich habe einen weiteren Schritt hinzugefügt, um uninteressante Objekte zu verwerfen. fügte auch Beispiel für die Identifizierung von Wörtern oder anderen Arten von Objekten (als Textblöcke) hinzu
anana

Vielen Dank für die ausführliche Antwort, ich erhalte jedoch eine Fehlermeldung cv2.findContours. Es heißt ValueError: too many values to unpack.
Abhijith

1
Das Problem ist, dass die Funktion cv2.findContours3 Argumente zurückgibt und der ursprüngliche Code nur 2 erfasst.
Abhijith

@Abhijith cv2 in Version zwei gab zwei Argumente zurück, aber jetzt, in Version drei, gibt es 3 zurück
Tomasz Giba

27

@ dhanushkas Ansatz zeigte sich am vielversprechendsten, aber ich wollte in Python herumspielen, also ging ich voran und übersetzte es zum Spaß:

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)

Nun, um das Bild anzuzeigen:

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

Nicht das pythonischste Skript, aber ich habe versucht, dem ursprünglichen C ++ - Code so nahe wie möglich zu kommen, damit die Leser ihm folgen können.

Es funktioniert fast so gut wie das Original. Gerne lese ich Vorschläge, wie es verbessert / behoben werden kann, um den ursprünglichen Ergebnissen vollständig zu ähneln.

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein


3
Vielen Dank für die Bereitstellung einer Python-Version. Viele Leute werden dies nützlich finden. +1
Dhanushka

Was ist der Unterschied zwischen dem Füllen und Zeichnen der Kontur? Ich habe hier einen Code ohne Füllphase
SarahData

@SarahM Ich weiß nicht, ob Sie nach dem generischen Unterschied zwischen Zeichnen und Füllen (ziemlich offensichtlich, denke ich?) Oder speziell nach der OpenCV-API fragen? Wenn letzteres der Fall ist, lesen Sie die DokumentedrawContours , für diesen Status. "Die Funktion zeichnet Konturumrisse im Bild, wenn die Dicke> 0 ist, oder füllt den durch die Konturen begrenzten Bereich, wenn die Dicke <0 ist." Dies geschieht, damit wir das Verhältnis von Pixeln ungleich Null überprüfen können, um zu entscheiden, ob das Feld wahrscheinlich Text enthält.
rtkaleta

15

Sie können diese Methode ausprobieren , die von Chucai Yi und Yingli Tian entwickelt wurde.

Sie teilen auch eine Software (die auf Opencv-1.0 basiert und unter Windows-Plattform ausgeführt werden sollte), die Sie verwenden können (obwohl kein Quellcode verfügbar ist). Es werden alle Textbegrenzungsrahmen (in Farbschatten dargestellt) im Bild generiert. Wenn Sie sich auf Ihre Beispielbilder anwenden, erhalten Sie die folgenden Ergebnisse:

Hinweis: Um das Ergebnis robuster zu machen, können Sie benachbarte Felder weiter zusammenführen.


Update: Wenn Ihr oberstes Ziel darin besteht, die Texte im Bild zu erkennen, können Sie gttext weiter überprüfen , eine OCR-freie Software und ein Ground Truthing-Tool für Farbbilder mit Text. Quellcode ist ebenfalls verfügbar.

Damit erhalten Sie erkannte Texte wie:


gttext ist für Windows. Jeder Vorschlag für Mac / Linux-Benutzer
Saghir A. Khatri

5

Über Code JAVA-Version: Danke @William

public static List<Rect> detectLetters(Mat img){    
    List<Rect> 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<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    Mat hierarchy = new Mat();
    Imgproc.findContours(img_threshold, contours,hierarchy, 0, 1);

    List<MatOfPoint> contours_poly = new ArrayList<MatOfPoint>(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;
}

Und verwenden Sie diesen Code in der Praxis:

        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat img1=Imgcodecs.imread("abc.png");
        List<Rect> 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);

2

Python-Implementierung für die Lösung von @ dhanushka:

def process_rgb(rgb):
    hasText = False
    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 = True
        idx = hierarchy[0][idx][0]
    return hasText, rgb

Warum die Maske benutzt?
SarahData

1
Doppelte Antwort. Es wäre nützlicher gewesen, wenn Sie zu der Konversation auf stackoverflow.com/a/43283990/6809909 beigetragen hätten .
Rtkaleta

2

Dies ist eine C # -Version der Antwort von Dhanushka mit OpenCVSharp

        Mat large = new Mat(INPUT_FILE);
        Mat rgb = new Mat(), small = new Mat(), grad = new Mat(), bw = new Mat(), connected = new Mat();

        // downsample and use it for processing
        Cv2.PyrDown(large, rgb);
        Cv2.CvtColor(rgb, small, ColorConversionCodes.BGR2GRAY);

        // morphological gradient
        var morphKernel = Cv2.GetStructuringElement(MorphShapes.Ellipse, new OpenCvSharp.Size(3, 3));
        Cv2.MorphologyEx(small, grad, MorphTypes.Gradient, morphKernel);

        // binarize
        Cv2.Threshold(grad, bw, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);

        // connect horizontally oriented regions
        morphKernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(9, 1));
        Cv2.MorphologyEx(bw, connected, MorphTypes.Close, morphKernel);

        // find contours
        var mask = new Mat(Mat.Zeros(bw.Size(), MatType.CV_8UC1), Range.All);
        Cv2.FindContours(connected, out OpenCvSharp.Point[][] contours, out HierarchyIndex[] hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple, new OpenCvSharp.Point(0, 0));

        // filter contours
        var idx = 0;
        foreach (var hierarchyItem in hierarchy)
        {
            idx = hierarchyItem.Next;
            if (idx < 0)
                break;
            OpenCvSharp.Rect rect = Cv2.BoundingRect(contours[idx]);
            var maskROI = new Mat(mask, rect);
            maskROI.SetTo(new Scalar(0, 0, 0));

            // fill the contour
            Cv2.DrawContours(mask, contours, idx, Scalar.White, -1);

            // ratio of non-zero pixels in the filled region
            double r = (double)Cv2.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 */
            )
            {
                Cv2.Rectangle(rgb, rect, new Scalar(0, 255, 0), 2);
            }
        }

        rgb.SaveImage(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rgb.jpg"));

0

Dies ist eine VB.NET-Version der Antwort von Dhanushka mit EmguCV .

Einige Funktionen und Strukturen in EmguCV müssen anders berücksichtigt werden als die C # -Version mit OpenCVSharp

Imports Emgu.CV
Imports Emgu.CV.Structure
Imports Emgu.CV.CvEnum
Imports Emgu.CV.Util

        Dim input_file As String = "C:\your_input_image.png"
        Dim large As Mat = New Mat(input_file)
        Dim rgb As New Mat
        Dim small As New Mat
        Dim grad As New Mat
        Dim bw As New Mat
        Dim connected As New Mat
        Dim morphanchor As New Point(0, 0)

        '//downsample and use it for processing
        CvInvoke.PyrDown(large, rgb)
        CvInvoke.CvtColor(rgb, small, ColorConversion.Bgr2Gray)

        '//morphological gradient
        Dim morphKernel As Mat = CvInvoke.GetStructuringElement(ElementShape.Ellipse, New Size(3, 3), morphanchor)
        CvInvoke.MorphologyEx(small, grad, MorphOp.Gradient, morphKernel, New Point(0, 0), 1, BorderType.Isolated, New MCvScalar(0))

        '// binarize
        CvInvoke.Threshold(grad, bw, 0, 255, ThresholdType.Binary Or ThresholdType.Otsu)

        '// connect horizontally oriented regions
        morphKernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, New Size(9, 1), morphanchor)
        CvInvoke.MorphologyEx(bw, connected, MorphOp.Close, morphKernel, morphanchor, 1, BorderType.Isolated, New MCvScalar(0))

        '// find contours
        Dim mask As Mat = Mat.Zeros(bw.Size.Height, bw.Size.Width, DepthType.Cv8U, 1)  '' MatType.CV_8UC1
        Dim contours As New VectorOfVectorOfPoint
        Dim hierarchy As New Mat

        CvInvoke.FindContours(connected, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple, Nothing)

        '// filter contours
        Dim idx As Integer
        Dim rect As Rectangle
        Dim maskROI As Mat
        Dim r As Double
        For Each hierarchyItem In hierarchy.GetData
            rect = CvInvoke.BoundingRectangle(contours(idx))
            maskROI = New Mat(mask, rect)
            maskROI.SetTo(New MCvScalar(0, 0, 0))

            '// fill the contour
            CvInvoke.DrawContours(mask, contours, idx, New MCvScalar(255), -1)

            '// ratio of non-zero pixels in the filled region
            r = CvInvoke.CountNonZero(maskROI) / (rect.Width * rect.Height)

            '/* assume at least 45% of the area Is filled if it contains text */
            '/* 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 */
            If r > 0.45 AndAlso rect.Height > 8 AndAlso rect.Width > 8 Then
                'draw green rectangle
                CvInvoke.Rectangle(rgb, rect, New MCvScalar(0, 255, 0), 2)
            End If
            idx += 1
        Next
        rgb.Save(IO.Path.Combine(Application.StartupPath, "rgb.jpg"))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.