Mengekstrak teks OpenCV

148

Saya mencoba menemukan kotak teks pembatas dalam gambar dan saat ini saya menggunakan pendekatan ini:

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

Saat diberi gambar seperti ini:

masukkan deskripsi gambar di sini

Lalu ketika saya menunjukkan varMatRegionssaya mendapatkan gambar ini:

masukkan deskripsi gambar di sini

Seperti yang Anda lihat, ia agak mengkombinasikan blok teks kiri dengan header kartu, untuk sebagian besar kartu metode ini berfungsi dengan baik tetapi pada kartu yang lebih sibuk dapat menyebabkan masalah.

Alasan buruk bagi kontur untuk terhubung adalah karena kontur kotak hampir mengambil seluruh kartu.

Adakah yang bisa menyarankan cara lain saya bisa menemukan teks untuk memastikan deteksi teks yang tepat?

200 poin kepada siapa pun yang dapat menemukan teks dalam kartu di atas keduanya.

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Klip
sumber
1
Cara termudah yang saya lihat di sini adalah meningkatkan kontras sebelum mendapatkan daerah ...
Paweł Stawarz
3
Pertanyaan keren Terima kasih telah memposting dan menerima hadiah untuk memastikan balasan yang menarik.
Geoff
Baru dalam pemrograman. Bisakah hal yang sama dilakukan untuk teks dalam skrip selain bahasa Inggris seperti Sanskerta?
Vamshi Krishna

Jawaban:

127

Anda dapat mendeteksi teks dengan menemukan elemen tepi dekat (terinspirasi dari 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;
}

Pemakaian:

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

Hasil:

Sebuah. element = getStructureElement (cv :: MORPH_RECT, cv :: Size (17, 3)); imgOut1 imgOut2

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

Hasil serupa untuk gambar lain yang disebutkan.

LovaBill
sumber
6
Detektor Plat Lisensi.
LovaBill
2
Untuk beberapa kartu kotak pembatas tidak menyertakan semua teks, seperti setengah huruf terpotong. Seperti kartu ini: i.imgur.com/tX3XrwH.jpg Bagaimana saya bisa memperpanjang setiap kotak pembatas dengan tinggi dan lebar n? Terima kasih atas solusinya, ini bekerja dengan sangat baik!
Klip
4
Katakan cv::Rect a;. Diperbesar dengan n: a.x-=n/2;a.y-=n/2;a.width+=n;a.height+=n;.
LovaBill
2
Hai, bagaimana saya mencapai hasil yang sama dengan python cv2?
dnth
3
Buku . Kode .
LovaBill
128

Saya menggunakan metode berbasis gradien dalam program di bawah ini. Menambahkan gambar yang dihasilkan. Harap dicatat bahwa saya menggunakan versi gambar yang diperkecil untuk diproses.

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

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

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

dhanushka
sumber
3
Saya baru saja melihat pendekatannya. Perbedaan utama yang saya lihat adalah bahwa dia menggunakan filter Sobel sedangkan saya menggunakan filter gradien morfologis. Saya pikir filter morfologis dan downsampling meratakan banyak dari tepi yang tidak terlalu kuat. Sobel mungkin menerima lebih banyak suara.
dhanushka
1
@ascenator Saat Anda menggabungkan OTSU dengan tipe ambang, ia menggunakan ambang Otsu alih-alih nilai ambang yang ditentukan. Lihat di sini .
dhanushka
1
@ WisnuJayanand Anda hanya perlu menerapkan penskalaan ke rect. Ada satu pyrdown, jadi gandakan x, y, width, heightdari rect4.
dhanushka
1
Bisakah Anda memberi kami syarat ketiga: jumlah puncak signifikan dalam proyeksi horizontal atau setidaknya beberapa petunjuk.
ISlimani
2
@DforTye Ambil proyeksi horizontal dari kontur yang diisi (cv :: kurangi), lalu ambangkan (katakanlah, menggunakan mean atau median tinggi). Jika Anda memvisualisasikan hasil ini, itu akan terlihat seperti barcode. Saya pikir, pada saat itu, saya berpikir untuk menghitung jumlah batang, dan memaksakan ambang batas pada itu. Sekarang saya pikir, jika wilayah tersebut cukup bersih, mungkin juga membantu jika kita bisa memberi makan ke OCR dan mendapatkan tingkat kepercayaan untuk setiap karakter yang terdeteksi untuk memastikan bahwa wilayah tersebut berisi teks.
dhanushka
51

Berikut adalah pendekatan alternatif yang saya gunakan untuk mendeteksi blok teks:

  1. Konversi gambar ke skala abu-abu
  2. Ambang terapan ( ambang biner sederhana, dengan nilai yang dipilih sendiri 150 sebagai nilai ambang)
  3. Terapan dilatasi untuk menebal garis dalam gambar, mengarah ke objek yang lebih kompak dan fragmen ruang kurang putih. Digunakan nilai tinggi untuk jumlah iterasi, jadi dilatasi sangat berat (13 iterasi, juga dipilih sendiri untuk hasil yang optimal).
  4. Mengidentifikasi kontur objek dalam gambar yang dihasilkan menggunakan fungsi findContours opencv .
  5. Drew sebuah kotak pembatas (persegi panjang) circumscribing setiap objek berkontur - masing-masing frame sebuah blok teks.
  6. Area yang dibuang secara opsional yang tidak mungkin menjadi objek yang Anda cari (mis. Blok teks) dengan ukurannya, karena algoritma di atas juga dapat menemukan objek yang bersinggungan atau bersarang (seperti seluruh area teratas untuk kartu pertama) beberapa di antaranya dapat berupa tidak menarik untuk tujuan Anda.

Di bawah ini adalah kode yang ditulis dalam python dengan pyopencv, seharusnya mudah untuk port ke 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) 

Gambar asli adalah gambar pertama di posting Anda.

Setelah preprocessing (skala abu-abu, ambang dan dilatasi - jadi setelah langkah 3) gambar tampak seperti ini:

Gambar melebar

Di bawah ini adalah gambar yang dihasilkan ("contoured.jpg" di baris terakhir); kotak pembatas akhir untuk objek dalam gambar terlihat seperti ini:

masukkan deskripsi gambar di sini

Anda dapat melihat blok teks di sebelah kiri terdeteksi sebagai blok terpisah, dibatasi dari lingkungannya.

Menggunakan skrip yang sama dengan parameter yang sama (kecuali untuk tipe thresholding yang diubah untuk gambar kedua seperti dijelaskan di bawah), berikut adalah hasil untuk 2 kartu lainnya:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Menyetel parameter

Parameter (nilai ambang, parameter pelebaran) dioptimalkan untuk gambar ini dan tugas ini (menemukan blok teks) dan dapat disesuaikan, jika perlu, untuk gambar kartu lain atau jenis objek lain yang dapat ditemukan.

Untuk thresholding (langkah 2), saya menggunakan threshold hitam. Untuk gambar di mana teks lebih ringan dari latar belakang, seperti gambar kedua di posting Anda, ambang putih harus digunakan, jadi ganti dengan tipe penyimpanan dengan cv2.THRESH_BINARY). Untuk gambar kedua saya juga menggunakan nilai yang sedikit lebih tinggi untuk ambang (180). Memvariasikan parameter untuk nilai ambang batas dan jumlah iterasi untuk pelebaran akan menghasilkan berbagai tingkat sensitivitas dalam membatasi objek dalam gambar.

Menemukan jenis objek lain:

Misalnya, mengurangi pelebaran hingga 5 iterasi pada gambar pertama memberi kita batasan objek yang lebih baik dalam gambar, dengan kasar menemukan semua kata dalam gambar (daripada blok teks):

masukkan deskripsi gambar di sini

Mengetahui ukuran kata yang kasar, di sini saya membuang area yang terlalu kecil (di bawah 20 piksel lebar atau tinggi) atau terlalu besar (di atas 100 piksel lebar atau tinggi) untuk mengabaikan objek yang tidak mungkin berupa kata, untuk mendapatkan hasil dalam gambar di atas.

anana
sumber
2
Kamu luar biasa! Saya akan mencoba ini di pagi hari.
Klip
Saya menambahkan langkah lain untuk membuang objek yang tidak menarik; juga menambahkan contoh untuk mengidentifikasi kata-kata atau jenis objek lainnya (selain blok teks)
anana
Terima kasih atas jawaban terinci, namun saya mendapatkan kesalahan di cv2.findContours. Dikatakan ValueError: too many values to unpack.
Abhijith
1
Masalahnya adalah fungsi cv2.findContoursmengembalikan 3 argumen, dan kode asli hanya menangkap 2.
Abhijith
@Abhijith cv2 dalam versi dua mengembalikan dua argumen, tetapi sekarang, dalam versi tiga, ia mengembalikan 3
Tomasz Giba
27

@ Pendekatan dhanushka menunjukkan janji yang paling, tetapi saya ingin bermain-main dengan Python jadi lanjutkan dan terjemahkan untuk bersenang-senang:

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)

Sekarang untuk menampilkan gambar:

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

Bukan yang paling Pythonic dari skrip tetapi saya mencoba menyerupai kode asli C ++ sedekat mungkin untuk diikuti oleh pembaca.

Ini berfungsi hampir sama seperti aslinya. Saya akan senang membaca saran bagaimana itu bisa diperbaiki / diperbaiki agar menyerupai hasil asli sepenuhnya.

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

rtkaleta
sumber
3
Terima kasih telah menyediakan versi python. Banyak orang akan menganggap ini berguna. +1
dhanushka
apa perbedaan antara mengisi kontur dan menggambarnya? Saya menemukan kode tanpa fase pengisian di sini: stackoverflow.com/a/23556997/6837132
SarahData
@SarahM Saya tidak tahu apakah Anda bertanya tentang perbedaan generik antara menggambar dan mengisi (saya kira cukup jelas?) Atau API OpenCV secara khusus? Jika yang terakhir kemudian lihat dokumen untuk drawContourskeadaan itu "Fungsi menggambar garis kontur pada gambar jika ketebalan> 0 atau mengisi area yang dibatasi oleh kontur jika ketebalan <0." Ini dilakukan agar kami dapat memeriksa rasio piksel bukan nol untuk memutuskan apakah kotak tersebut kemungkinan berisi teks.
rtkaleta
15

Anda dapat mencoba metode ini yang dikembangkan oleh Chucai Yi dan Yingli Tian.

Mereka juga berbagi perangkat lunak (yang didasarkan pada Opencv-1.0 dan harus dijalankan di bawah platform Windows.) Yang dapat Anda gunakan (meskipun tidak ada kode sumber yang tersedia). Ini akan menghasilkan semua kotak pembatas teks (ditampilkan dalam bayangan warna) pada gambar. Dengan menerapkan ke gambar sampel Anda, Anda akan mendapatkan hasil berikut:

Catatan: untuk membuat hasilnya lebih kuat, Anda dapat menggabungkan kotak yang berdekatan menjadi satu.


Pembaruan: Jika tujuan akhir Anda adalah untuk mengenali teks dalam gambar, Anda dapat memeriksa gttext , yang merupakan perangkat lunak bebas OCR dan alat Truthing Tanah untuk Gambar Berwarna dengan Teks. Kode sumber juga tersedia.

Dengan ini, Anda bisa mendapatkan teks yang dikenal seperti:

herohuyongtao
sumber
gttext adalah untuk windows. Setiap saran untuk pengguna Mac / Linux
Saghir A. Khatri
5

Above Code versi JAVA: Terima kasih @ 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;
}

Dan gunakan kode ini dalam praktiknya:

        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);
Fariz Agayev
sumber
2

Implementasi Python untuk solusi @ 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
Akash Budhia
sumber
mengapa menggunakan topeng?
SarahData
1
Jawaban rangkap. Akan lebih bermanfaat jika Anda berkontribusi pada percakapan di stackoverflow.com/a/43283990/6809909 .
rtkaleta
2

Ini adalah versi C # jawaban dari dhanushka menggunakan 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"));
Diomedes Domínguez
sumber
0

ini adalah versi VB.NET dari jawaban dari dhanushka menggunakan EmguCV .

Beberapa fungsi dan struktur di EmguCV perlu pertimbangan berbeda dari versi C # dengan 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"))
Hakan Usakli
sumber