OpenCV metni çıkarma


151

Bir görüntüdeki sınırlayıcı metin kutularını bulmaya çalışıyorum ve şu anda bu yaklaşımı kullanıyorum:

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

Böyle bir görüntü verildiğinde:

görüntü açıklamasını buraya girin

Sonra gösterdiğimde varMatRegionsbu resmi alıyorum:

görüntü açıklamasını buraya girin

Gördüğünüz gibi, sol metin bloğunu kartın başlığıyla biraz birleştiriyor, çoğu kart için bu yöntem harika çalışıyor ancak daha yoğun kartlarda sorunlara neden olabilir.

Bu konturların bağlanmasının kötü olmasının nedeni, konturun sınırlayıcı kutusunun neredeyse tüm kartı kaplamasıdır.

Metnin doğru şekilde algılanmasını sağlamak için metni bulmam için başka bir yol önerebilecek biri var mı?

Bu ikisinin üzerindeki karttaki metni kim bulabilirse 200 puan.

görüntü açıklamasını buraya girin görüntü açıklamasını buraya girin


1
Burada gördüğüm en kolay yol, bölgeleri almadan önce kontrastı artırmak ...
Paweł Stawarz

3
Harika soru. Böylesine ilginç yanıtların alınmasını sağlamak için ödülü yayınladığınız ve ödüle ev sahipliği yaptığınız için teşekkür ederiz.
Geoff

Programlamada yeni. Aynı şeyler Sanskritçe gibi İngilizce dışındaki metinlerdeki metinler için de yapılabilir mi?
Vamshi Krishna

Yanıtlar:


131

Yakın kenar öğelerini (bir LPD'den esinlenerek) bularak metni tespit edebilirsiniz:

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

Kullanım:

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

Sonuçlar:

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

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

Sonuçlar, bahsedilen diğer görsel için benzerdir.


6
Plaka Dedektörü.
LovaBill

2
Bazı kartlar için sınırlayıcı kutu, yarım harfin kesilmesi gibi metnin tamamını kapsamaz. Bu kart gibi: i.imgur.com/tX3XrwH.jpg Sınırlayıcı kutuların her yüksekliğini ve genişliğini nasıl genişletebilirim n? Çözüm için teşekkürler, harika çalışıyor!
klibi

4
Söyle cv::Rect a;. N: ile büyütülmüş a.x-=n/2;a.y-=n/2;a.width+=n;a.height+=n;.
LovaBill

2
Merhaba, python cv2 ile aynı sonucu nasıl elde ederim?
dnth


130

Aşağıdaki programda gradyan tabanlı bir yöntem kullandım. Ortaya çıkan görüntüleri eklendi. Lütfen işlemek için görüntünün küçültülmüş bir sürümünü kullandığımı unutmayın.

c ++ sürümü

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 sürümü

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)

görüntü açıklamasını buraya girin görüntü açıklamasını buraya girin görüntü açıklamasını buraya girin


3
Yaklaşımına bir baktım. Gördüğüm ana fark, onun bir Sobel filtresi kullanması, ben ise bir morfolojik gradyan filtresi kullanıyorum. Bence morfolojik filtre ve altörnekleme, çok güçlü olmayan kenarların çoğunu düzleştiriyor. Sobel daha fazla gürültü alabilir.
dhanushka

1
@ascenator OTSU'yu eşik türü ile birleştirdiğinizde, belirtilen eşik değeri yerine Otsu'nun eşik değerini kullanır. Buraya bakın .
dhanushka

1
@VishnuJayanand Sadece rect. Bir tane var pyrdown, bu yüzden 4'ü çarpın x, y, width, height.rect
dhanushka

1
Lütfen bize üçüncü koşulu sağlayabilir misiniz: yatay bir projeksiyondaki önemli zirvelerin sayısı veya en azından biraz öncülük.
ISlimani

2
@DforTye Doldurulmuş konturun yatay izdüşümünü alın (cv :: azaltın), ardından sınırlayın (örneğin, ortalama veya medyan yüksekliği kullanarak). Bu sonucu görselleştirirseniz, bir barkod gibi görünecektir. Sanırım o zamanlar çubuk sayısını saymayı ve üzerine bir eşik koymayı düşünüyordum. Şimdi, bölge yeterince temizse, onu bir OCR'ye besleyebilirsek ve bölgenin metin içerdiğinden emin olmak için tespit edilen her karakter için bir güven seviyesi elde etmemizin de yardımcı olabileceğini düşünüyorum.
dhanushka

52

Metin bloklarını tespit etmek için kullandığım alternatif bir yaklaşım:

  1. Resmi gri tonlamaya dönüştürdü
  2. Uygulanan eşik (eşik değeri olarak elle seçilmiş 150 değeriyle basit ikili eşik)
  3. Görüntüdeki çizgileri kalınlaştırmak için genişletme uygulanarak daha kompakt nesneler ve daha az beyaz boşluk parçası elde edilir. Yineleme sayısı için yüksek bir değer kullanılır, bu nedenle genişleme çok ağırdır (13 yineleme, ayrıca en iyi sonuçlar için özel olarak seçilmiştir).
  4. Opencv findContours işlevi kullanılarak sonuçlanan görüntüdeki nesnelerin konturları belirlendi .
  5. Her bir konturlu nesneyi çevreleyen bir sınırlayıcı kutu (dikdörtgen) çizin - her biri bir metin bloğunu çerçeveler.
  6. Yukarıdaki algoritma aynı zamanda kesişen veya iç içe nesneler de bulabildiğinden (ilk kart için tüm üst alan gibi) boyutlarına göre aradığınız nesne olma olasılığı düşük olan isteğe bağlı olarak atılmış alanlar (ör. Metin blokları) amaçlarınız için ilginç değil.

Pyopencv ile python ile yazılmış kod aşağıdadır, C ++ 'ya portu kolay olmalıdır.

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) 

Orijinal görsel, gönderinizdeki ilk görseldir.

Ön işlemden sonra (gri tonlama, eşik ve genişleme - 3. adımdan sonra) görüntü şöyle görünüyordu:

Büyütülmüş görüntü

Aşağıda sonuçlanan görüntü (son satırda "contoured.jpg") bulunmaktadır; görüntüdeki nesneler için son sınırlayıcı kutular şuna benzer:

görüntü açıklamasını buraya girin

Soldaki metin bloğunun çevresinden ayrılmış ayrı bir blok olarak algılandığını görebilirsiniz.

Aynı komut dosyasını aynı parametrelerle kullanarak (aşağıda açıklandığı gibi ikinci görüntü için değiştirilen eşik türü hariç), işte diğer 2 kart için sonuçlar:

görüntü açıklamasını buraya girin

görüntü açıklamasını buraya girin

Parametrelerin ayarlanması

Parametreler (eşik değeri, genişleme parametreleri) bu görüntü ve bu görev (metin bloklarının bulunması) için optimize edilmiştir ve gerekirse diğer kart görüntüleri veya bulunacak diğer nesneler için ayarlanabilir.

Eşikleme için (2. adım), siyah bir eşik kullandım. Gönderinizdeki ikinci resim gibi metnin arka plandan daha açık olduğu resimler için beyaz bir eşik kullanılmalıdır, bu nedenle eşleme türünü) ile değiştirin cv2.THRESH_BINARY. İkinci görüntü için de eşik için biraz daha yüksek bir değer kullandım (180). Genişleme için eşik değeri için parametrelerin ve yineleme sayısının değiştirilmesi, görüntüdeki nesnelerin sınırlandırılmasında farklı hassasiyet derecelerine neden olacaktır.

Diğer nesne türlerini bulmak:

Örneğin, ilk görüntüde genişlemeyi 5 yinelemeye düşürmek, görüntüdeki nesnelerin daha ince bir şekilde sınırlandırılmasını sağlar, kabaca görüntüdeki tüm kelimeleri (metin blokları yerine) bulur :

görüntü açıklamasını buraya girin

Bir kelimenin kaba boyutunu bilerek, burada sonuçları almak için kelime olma ihtimali olmayan nesneleri yok saymak için çok küçük (20 piksel genişlik veya yüksekliğin altında) veya çok büyük (100 piksel genişlik veya yüksekliğin üzerinde) alanları attım. yukarıdaki görüntü.


2
Harikasın! Bunu sabah deneyeceğim.
klibi

İlginç olmayan nesneleri atmak için bir adım daha ekledim; ayrıca kelimeleri veya diğer nesne türlerini (metin blokları dışında) tanımlamak için bir örnek eklendi
anana

Ayrıntılı cevap için teşekkürler, ancak bir hata alıyorum cv2.findContours. Diyor ValueError: too many values to unpack.
Abhijith

1
Sorun, işlevin cv2.findContours3 bağımsız değişken döndürmesi ve orijinal kodun yalnızca 2'yi yakalamasıdır.
Abhijith

İkinci sürümdeki @Abhijith cv2 iki bağımsız değişken döndürdü, ancak şimdi sürüm üçte 3 döndürüyor
Tomasz Giba

27

@ dhanushka'nın yaklaşımı en çok vaad etti ama ben Python'da oynamak istedim, bu yüzden devam ettim ve bunu eğlence için tercüme ettim:

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)

Şimdi resmi görüntülemek için:

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

Komut dosyalarının çoğu Pythonic değil, ancak orijinal C ++ koduna okuyucuların takip etmesi için olabildiğince yakından benzemeye çalıştım.

Neredeyse orijinali kadar iyi çalışıyor. Orijinal sonuçlara tam olarak benzemek için nasıl geliştirilebileceği / düzeltilebileceği konusundaki önerileri okumaktan mutluluk duyarım.

görüntü açıklamasını buraya girin

görüntü açıklamasını buraya girin

görüntü açıklamasını buraya girin


3
Bir python sürümü sağladığınız için teşekkür ederiz. Birçok kişi bunu faydalı bulacaktır. +1
dhanushka

konturu doldurmakla çizmek arasındaki fark nedir? Burada doldurma aşaması olmayan bir kod buldum: stackoverflow.com/a/23556997/6837132
SarahData

@SarahM Çizim ve doldurma arasındaki genel fark (sanırım oldukça açık?) Veya özellikle OpenCV API'si hakkında soru soruyor musunuz bilmiyorum? İkincisi , bu durum için belgelere bakın drawContours"Kalınlık> 0 ise işlev görüntüde kontur anahatları çizer veya kalınlık <0 ise konturlarla sınırlanan alanı doldurur." Kutunun metin içerip içermediğine karar vermek için sıfır olmayan piksellerin oranını kontrol edebilmemiz için yapıldı.
rtkaleta

15

Chucai Yi ve Yingli Tian tarafından geliştirilen bu yöntemi deneyebilirsiniz .

Ayrıca kullanabileceğiniz bir yazılımı (Opencv-1.0 tabanlı ve Windows platformu altında çalışmalıdır) paylaşırlar (kaynak kodu olmasa da). Görüntüdeki tüm metin sınırlayıcı kutuları (renkli gölgelerle gösterilen) oluşturacaktır. Örnek resimlerinize başvurarak aşağıdaki sonuçları alacaksınız:

Not: Sonucu daha sağlam hale getirmek için, bitişik kutuları daha da birleştirebilirsiniz.


Güncelleme: Nihai amacınız görüntüdeki metinleri tanımaksa , OCR ücretsiz bir yazılım ve Metin İçeren Renkli Görüntüler için Zemin Düzeltme aracı olan gttext'e daha fazla göz atabilirsiniz . Kaynak kodu da mevcuttur.

Bununla, aşağıdaki gibi tanınan metinler alabilirsiniz:


gttext pencereler içindir. Mac / Linux kullanıcıları için herhangi bir öneri
Saghir A. Khatri

6

Yukarıdaki Kod JAVA sürümü: Teşekkürler @ 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;
}

Ve bu kodu pratikte kullanın:

        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

@ Dhanushka'nın çözümü için Python Uygulaması:

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

neden maskeyi kullandın?
SarahData

1
Yinelenen yanıt. Stackoverflow.com/a/43283990/6809909 adresindeki sohbete katkıda bulunsaydınız daha yararlı olabilirdi .
rtkaleta

2

Bu bir C # versiyonudur cevap dhanushka kullanmasını 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

bu, cevabın VB.NET versiyonu dhanushka kullanmasını EmguCV .

EmguCV'deki birkaç işlev ve yapı, OpenCVSharp içeren C # sürümünden farklı değerlendirmeye ihtiyaç duyar

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"))

0

Bir python uygulaması SWTloc kullanabilirsiniz .

Tam Açıklama: Ben bu kütüphanenin yazarıyım

Bunu yapmak için :-

Birinci ve İkinci Resim

Buradaki text_mode'un 'lb_df' olduğuna dikkat edin, bu da Açık Arka Plan Koyu Ön Plan anlamına gelir, yani bu görüntüdeki metin arka plandan daha koyu renkte olacaktır.

from swtloc import SWTLocalizer
from swtloc.utils import imgshowN, imgshow

swtl = SWTLocalizer()
# Stroke Width Transform
swtl.swttransform(imgpaths='img1.jpg', text_mode = 'lb_df',
                  save_results=True, save_rootpath = 'swtres/',
                  minrsw = 3, maxrsw = 20, max_angledev = np.pi/3)
imgshow(swtl.swtlabelled_pruned13C)

# Grouping
respacket=swtl.get_grouped(lookup_radii_multiplier=0.9, ht_ratio=3.0)
grouped_annot_bubble = respacket[2]
maskviz = respacket[4]
maskcomb  = respacket[5]

# Saving the results
_=cv2.imwrite('img1_processed.jpg', swtl.swtlabelled_pruned13C)
imgshowN([maskcomb, grouped_annot_bubble], savepath='grouped_img1.jpg')

görüntü açıklamasını buraya girin görüntü açıklamasını buraya girin


görüntü açıklamasını buraya girin görüntü açıklamasını buraya girin

Üçüncü Resim

Buradaki text_mode'un 'db_lf' olduğuna dikkat edin, bu da Koyu Arka Plan Işığı Ön Plan anlamına gelir, yani bu görüntüdeki metin arka plandan daha açık renkte olacaktır.

from swtloc import SWTLocalizer
from swtloc.utils import imgshowN, imgshow

swtl = SWTLocalizer()
# Stroke Width Transform
swtl.swttransform(imgpaths=imgpaths[1], text_mode = 'db_lf',
              save_results=True, save_rootpath = 'swtres/',
              minrsw = 3, maxrsw = 20, max_angledev = np.pi/3)
imgshow(swtl.swtlabelled_pruned13C)

# Grouping
respacket=swtl.get_grouped(lookup_radii_multiplier=0.9, ht_ratio=3.0)
grouped_annot_bubble = respacket[2]
maskviz = respacket[4]
maskcomb  = respacket[5]

# Saving the results
_=cv2.imwrite('img1_processed.jpg', swtl.swtlabelled_pruned13C)
imgshowN([maskcomb, grouped_annot_bubble], savepath='grouped_img1.jpg')

görüntü açıklamasını buraya girin görüntü açıklamasını buraya girin

Ayrıca, görüntülerin değişebileceğinden istenen sonuçları elde etmek için, yapılan gruplamanın o kadar doğru olmadığını da fark edeceksiniz, swtl.get_grouped()işlevdeki gruplama parametrelerini ayarlamaya çalışın .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.