Estrazione del testo OpenCV


148

Sto cercando di trovare le caselle di delimitazione del testo in un'immagine e attualmente sto usando questo approccio:

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

Quando viene data un'immagine come questa:

inserisci qui la descrizione dell'immagine

Quindi, quando mostro, varMatRegionsottengo questa immagine:

inserisci qui la descrizione dell'immagine

Come puoi vedere, combina in qualche modo il blocco di testo sinistro con l'intestazione della carta, per la maggior parte delle carte questo metodo funziona benissimo ma su carte più occupate può causare problemi.

La ragione per cui è difficile connettere quei contorni è che fa in modo che il riquadro di delimitazione del contorno occupi quasi l'intera carta.

Qualcuno può suggerire un modo diverso in cui posso trovare il testo per garantire il corretto rilevamento del testo?

200 punti per chiunque riesca a trovare il testo nella scheda sopra questi due.

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


1
Il modo più semplice che vedo qui è aumentare il contrasto prima di ottenere le regioni ...
Paweł Stawarz,

3
Bella domanda. Grazie per averlo pubblicato e ospitato la generosità per garantire risposte così interessanti.
Geoff,

Nuovo nella programmazione. Si può fare la stessa cosa per il testo in script diversi dall'inglese come il sanscrito?
Vamshi Krishna,

Risposte:


127

È possibile rilevare il testo trovando elementi a margine ravvicinato (ispirato a un 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;
}

Uso:

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

risultati:

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

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

I risultati sono simili per l'altra immagine menzionata.


6
Rilevatore di targa.
LovaBill

2
Per alcune carte il rettangolo di selezione non racchiude tutto il testo, ad esempio mezza lettera tagliata. Come questa carta: i.imgur.com/tX3XrwH.jpg Come posso estendere di ogni larghezza e larghezza di ogni riquadro di delimitazione n? Grazie per la soluzione funziona alla grande!
Clip

4
Say cv::Rect a;. Ampliato da n: a.x-=n/2;a.y-=n/2;a.width+=n;a.height+=n;.
LovaBill,

2
Ciao, come posso ottenere lo stesso risultato con python cv2?
DNTH


128

Ho usato un metodo basato sul gradiente nel programma qui sotto. Aggiunte le immagini risultanti. Si noti che sto utilizzando una versione ridotta dell'immagine per l'elaborazione.

versione 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 <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;
}

versione di 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)
#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)

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


3
Ho appena dato un'occhiata al suo approccio. La differenza principale che vedo è che sta usando un filtro Sobel mentre sto usando un filtro gradiente morfologico. Penso che il filtro morfologico e il downsampling appiattiscano gran parte dei bordi non così forti. Sobel potrebbe raccogliere più rumore.
Dhanushka,

1
@ascenator Quando si combina OTSU con il tipo di soglia, utilizza la soglia di Otsu invece del valore di soglia specificato. Vedi qui .
Dhanushka,

1
@VishnuJayanand Devi solo applicare un ridimensionamento a rect. Ce pyrdownx, y, width, heightrect
n'è

1
Potresti fornirci la terza condizione: il numero di picchi significativi in ​​una proiezione orizzontale o almeno un po 'di piombo.
ISlimani,

2
@DforTye Prendi la proiezione orizzontale del contorno riempito (cv :: riduci), quindi soglia (ad esempio, usando l'altezza media o mediana). Se visualizzi questo risultato, sembrerà un codice a barre. Penso, a quel tempo, stavo pensando di contare il numero di barre e imporre una soglia su di esso. Ora penso che, se la regione è abbastanza pulita, può anche essere utile se possiamo inviarla a un OCR e ottenere un livello di confidenza per ciascun personaggio rilevato per essere sicuro che la regione contenga del testo.
Dhanushka,

51

Ecco un approccio alternativo che ho usato per rilevare i blocchi di testo:

  1. Convertire l'immagine in scala di grigi
  2. Soglia applicata ( binaria semplice, con un valore selezionato di 150 come valore di soglia)
  3. Dilatazione applicata per ispessire le linee nell'immagine, portando a oggetti più compatti e meno frammenti di spazio bianco. Utilizzato un valore elevato per il numero di iterazioni, quindi la dilatazione è molto pesante (13 iterazioni, anche raccolte a mano per risultati ottimali).
  4. Contorni identificati degli oggetti nell'immagine risultante utilizzando la funzione findContours di opencv .
  5. Drew un rettangolo di selezione (rettangolo) che circoscrive ogni oggetto sagomato - ciascuno di essi racchiude un blocco di testo.
  6. Facoltativamente, le aree scartate che è improbabile che siano l'oggetto che stai cercando (ad es. Blocchi di testo) date le loro dimensioni, poiché l'algoritmo sopra può anche trovare oggetti intersecanti o nidificati (come l'intera area superiore della prima carta), alcuni dei quali potrebbero essere poco interessante per i tuoi scopi.

Di seguito è riportato il codice scritto in Python con pyopencv, dovrebbe essere facile portarlo in 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) 

L'immagine originale è la prima immagine nel tuo post.

Dopo la preelaborazione (scala di grigi, soglia e dilatazione - quindi dopo il passaggio 3) l'immagine appariva così:

Immagine dilatata

Di seguito l'immagine risultante ("contoured.jpg" nell'ultima riga); le caselle di delimitazione finali per gli oggetti nell'immagine sono simili alle seguenti:

inserisci qui la descrizione dell'immagine

Puoi vedere il blocco di testo sulla sinistra che viene rilevato come un blocco separato, delimitato da ciò che lo circonda.

Utilizzando lo stesso script con gli stessi parametri (ad eccezione del tipo di soglia che è stato modificato per la seconda immagine come descritto di seguito), ecco i risultati per le altre 2 schede:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Ottimizzazione dei parametri

I parametri (valore soglia, parametri di dilatazione) sono stati ottimizzati per questa immagine e questa attività (ricerca di blocchi di testo) e possono essere regolati, se necessario, per trovare altre immagini di carte o altri tipi di oggetti.

Per il soglia (punto 2), ho usato una soglia nera. Per le immagini in cui il testo è più chiaro dello sfondo, come la seconda immagine nel tuo post, è necessario utilizzare una soglia bianca, quindi sostituire il tipo di griglia con cv2.THRESH_BINARY). Per la seconda immagine ho anche usato un valore leggermente più alto per la soglia (180). Variando i parametri per il valore di soglia e il numero di iterazioni per la dilatazione si otterranno diversi gradi di sensibilità nel delimitare gli oggetti nell'immagine.

Trovare altri tipi di oggetti:

Ad esempio, ridurre la dilatazione a 5 iterazioni nella prima immagine ci dà una delimitazione più fine degli oggetti nell'immagine, trovando approssimativamente tutte le parole nell'immagine (piuttosto che i blocchi di testo):

inserisci qui la descrizione dell'immagine

Conoscendo la dimensione approssimativa di una parola, qui ho scartato le aree che erano troppo piccole (sotto la larghezza o l'altezza di 20 pixel) o troppo grandi (sopra la larghezza o l'altezza di 100 pixel) per ignorare oggetti che è improbabile che fossero parole, per ottenere i risultati in l'immagine sopra.


2
Sei fantastico! Ci proverò al mattino.
Clip del

Ho aggiunto un altro passaggio per scartare oggetti poco interessanti; aggiunto anche un esempio per identificare parole o altri tipi di oggetti (oltre ai blocchi di testo)
anana,

Grazie per la risposta dettagliata, tuttavia sto riscontrando un errore cv2.findContours. Dice ValueError: too many values to unpack.
Abhijith,

1
Il problema è che la funzione cv2.findContoursrestituisce 3 argomenti e il codice originale ne cattura solo 2.
Abhijith

@Abhijith cv2 nella versione due ha restituito due argomenti, ma ora, nella versione tre, restituisce 3
Tomasz Giba,

27

L'approccio di @ dhanushka ha mostrato la più promessa, ma volevo giocare in Python, quindi sono andato avanti e tradotto per divertimento:

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)

Ora per visualizzare l'immagine:

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

Non la maggior parte degli script Pythonic, ma ho cercato di assomigliare il codice C ++ originale il più vicino possibile ai lettori.

Funziona quasi come l'originale. Sarò felice di leggere suggerimenti su come potrebbe essere migliorato / riparato per assomigliare completamente ai risultati originali.

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine


3
Grazie per aver fornito una versione di Python. Molte persone lo troveranno utile. +1
Dhanushka,

qual è la differenza tra riempire il contorno e disegnarlo? Ho trovato un codice senza la fase di riempimento qui: stackoverflow.com/a/23556997/6837132
SarahData

@SarahM Non so se stai chiedendo del diff generico tra disegno e riempimento (abbastanza ovvio penso?) O l'API OpenCV in particolare? Se quest'ultimo visualizza i documenti per drawContoursquello stato "La funzione disegna i contorni nell'immagine se spessore> 0 o riempie l'area delimitata dai contorni se spessore <0." È fatto in modo che possiamo controllare il rapporto di pixel diversi da zero per decidere se la casella contiene probabilmente del testo.
rtkaleta,

15

Puoi provare questo metodo sviluppato da Chucai Yi e Yingli Tian.

Condividono anche un software (basato su Opencv-1.0 e che dovrebbe essere eseguito su piattaforma Windows) che è possibile utilizzare (anche se non è disponibile alcun codice sorgente). Genererà tutte le caselle di delimitazione del testo (mostrate in ombre colorate) nell'immagine. Applicando alle immagini di esempio, otterrai i seguenti risultati:

Nota: per rendere il risultato più affidabile, è possibile unire ulteriormente le caselle adiacenti.


Aggiornamento: se il tuo obiettivo finale è riconoscere i testi nell'immagine, puoi verificare ulteriormente gttext , che è un software gratuito OCR e uno strumento di verità sul terreno per le immagini a colori con testo. Il codice sorgente è inoltre disponibile.

Con questo, puoi ottenere testi riconosciuti come:


gttext è per Windows. Qualsiasi suggerimento per utenti Mac / Linux
Saghir A. Khatri,

5

Sopra il codice Versione JAVA: grazie @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;
}

E usa questo codice in pratica:

        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

Implementazione di Python per la soluzione di @ 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

perché ha usato la maschera?
SarahData,

1
Risposta duplicata. Sarebbe stato più utile se tu avessi contribuito alla conversazione su stackoverflow.com/a/43283990/6809909 .
rtkaleta,

2

Questa è una versione C # della risposta di dhanushka usando 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

questa è una versione VB.NET della risposta di dhanushka usando EmguCV .

Alcune funzioni e strutture in EmguCV richiedono considerazioni diverse rispetto alla versione C # con 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"))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.