Trích xuất văn bản OpenCV


148

Tôi đang cố gắng tìm các hộp văn bản giới hạn trong một hình ảnh và hiện đang sử dụng phương pháp này:

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

Khi được cung cấp một hình ảnh như thế này:

nhập mô tả hình ảnh ở đây

Sau đó, khi tôi hiển thị, varMatRegionstôi nhận được hình ảnh này:

nhập mô tả hình ảnh ở đây

Như bạn có thể thấy nó phần nào kết hợp khối văn bản bên trái với tiêu đề của thẻ, đối với hầu hết các thẻ, phương pháp này hoạt động rất tốt nhưng trên các thẻ bận hơn, nó có thể gây ra sự cố.

Lý do rất tệ cho những đường viền đó để kết nối là vì nó làm cho hộp giới hạn của đường viền gần như chiếm toàn bộ thẻ.

Bất cứ ai có thể đề nghị một cách khác tôi có thể tìm thấy văn bản để đảm bảo phát hiện đúng văn bản?

200 điểm cho bất cứ ai có thể tìm thấy văn bản trong thẻ ở trên hai.

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây


1
Cách dễ nhất tôi thấy ở đây là tăng độ tương phản trước khi lấy các vùng ...
Paweł Stawarz

3
Câu hỏi hay. Cảm ơn bạn đã đăng nó và lưu trữ tiền thưởng để đảm bảo trả lời thú vị như vậy.
Geoff

Mới lập trình. Những thứ tương tự có thể được thực hiện cho văn bản trong các tập lệnh khác ngoài tiếng Anh như tiếng Phạn không?
Vamshi Krishna

Câu trả lời:


127

Bạn có thể phát hiện văn bản bằng cách tìm các yếu tố cạnh sát (lấy cảm hứng từ 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;
}

Sử dụng:

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

Các kết quả:

a. phần tử = getSturationuringEuity (cv :: MORPH_RECT, cv :: Size (17, 3)); imgOut1 imgOut2

b. phần tử = getSturationuringEuity (cv :: MORPH_RECT, cv :: Size (30, 30)); imgOut1 imgOut2

Kết quả tương tự cho các hình ảnh khác được đề cập.


6
Phát hiện biển số xe.
LovaBill

2
Đối với một số thẻ, hộp giới hạn không bao gồm tất cả các văn bản, chẳng hạn như một nửa chữ cái bị cắt. Chẳng hạn như thẻ này: i.imgur.com/tX3XrwH.jpg Làm cách nào tôi có thể mở rộng mọi chiều cao và chiều rộng của khung giới hạn n? Cảm ơn cho giải pháp nó hoạt động tuyệt vời!
Clip

4
Nói cv::Rect a;. Phóng to bởi n : a.x-=n/2;a.y-=n/2;a.width+=n;a.height+=n;.
LovaBill

2
Xin chào, làm thế nào để tôi đạt được kết quả tương tự với python cv2?
dnth


128

Tôi đã sử dụng một phương pháp dựa trên độ dốc trong chương trình dưới đây. Đã thêm hình ảnh kết quả. Xin lưu ý rằng tôi đang sử dụng phiên bản thu nhỏ của hình ảnh để xử lý.

phiên bản 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;
}

phiên bản trăn

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)

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây


3
Tôi chỉ có một cái nhìn về cách tiếp cận của anh ấy. Sự khác biệt chính tôi thấy là anh ấy đang sử dụng bộ lọc Sobel trong khi tôi đang sử dụng bộ lọc gradient hình thái. Tôi nghĩ rằng bộ lọc hình thái và downsampling làm phẳng phần lớn các cạnh không quá mạnh. Sobel có thể thu được nhiều tiếng ồn hơn.
dhanushka

1
@ascenator Khi bạn kết hợp OTSU với loại ngưỡng, nó sử dụng ngưỡng của Otsu thay vì giá trị ngưỡng được chỉ định. Xem ở đây .
dhanushka

1
@VishnuJayanand Bạn chỉ cần áp dụng một tỷ lệ cho rect. Có một pyrdown, nhân x, y, width, heightvới rect4.
dhanushka 16/03/18

1
Bạn có thể vui lòng cung cấp cho chúng tôi điều kiện thứ ba: số lượng đỉnh đáng kể trong hình chiếu ngang hoặc ít nhất là một số đạo trình.
ISlimani

2
@DforTye Lấy hình chiếu ngang của đường viền đầy (cv :: giảm), sau đó ngưỡng nó (giả sử sử dụng chiều cao trung bình hoặc trung bình). Nếu bạn hình dung kết quả này, nó sẽ trông giống như một mã vạch. Tôi nghĩ, vào thời điểm đó, tôi đã nghĩ đến việc đếm số lượng thanh và áp đặt một ngưỡng cho nó. Bây giờ tôi nghĩ, nếu khu vực đủ sạch, nó cũng có thể hữu ích nếu chúng ta có thể cung cấp nó cho OCR và có được mức độ tin cậy cho mỗi ký tự được phát hiện để chắc chắn rằng khu vực đó có chứa văn bản.
dhanushka

51

Đây là một cách tiếp cận khác mà tôi đã sử dụng để phát hiện các khối văn bản:

  1. Chuyển đổi hình ảnh sang thang độ xám
  2. Ngưỡng áp dụng ( ngưỡng nhị phân đơn giản, với giá trị được chọn cẩn thận là 150 làm giá trị ngưỡng)
  3. Áp dụng sự giãn nở để làm dày các đường trong ảnh, dẫn đến các vật thể nhỏ gọn hơn và các mảnh không gian trắng ít hơn. Được sử dụng một giá trị cao cho số lần lặp, do đó độ giãn rất nặng (13 lần lặp, cũng được lựa chọn cẩn thận để có kết quả tối ưu).
  4. Đường viền được xác định của các đối tượng trong hình ảnh kết quả bằng cách sử dụng hàm opencv findContours .
  5. Đã vẽ một hộp giới hạn (hình chữ nhật) bao quanh từng đối tượng đường viền - mỗi đối tượng tạo khung cho một khối văn bản.
  6. Các khu vực bị loại bỏ tùy ý không có khả năng là đối tượng bạn đang tìm kiếm (ví dụ: khối văn bản) có kích thước của chúng, vì thuật toán ở trên cũng có thể tìm thấy các đối tượng giao nhau hoặc lồng nhau (như toàn bộ khu vực trên cùng cho thẻ đầu tiên) một số trong đó có thể là không quan tâm cho mục đích của bạn.

Dưới đây là mã được viết bằng python với pyopencv, nên dễ dàng chuyển sang 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) 

Hình ảnh gốc là hình ảnh đầu tiên trong bài viết của bạn.

Sau khi tiền xử lý (thang độ xám, ngưỡng và giãn - vì vậy sau bước 3) hình ảnh trông như thế này:

Hình ảnh giãn

Dưới đây là hình ảnh kết quả ("contoured.jpg" trong dòng cuối cùng); các hộp giới hạn cuối cùng cho các đối tượng trong ảnh trông như thế này:

nhập mô tả hình ảnh ở đây

Bạn có thể thấy khối văn bản bên trái được phát hiện dưới dạng một khối riêng biệt, được phân tách khỏi môi trường xung quanh.

Sử dụng cùng một tập lệnh với cùng tham số (ngoại trừ loại ngưỡng được thay đổi cho hình ảnh thứ hai như được mô tả bên dưới), đây là kết quả cho 2 thẻ còn lại:

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Điều chỉnh các tham số

Các tham số (giá trị ngưỡng, tham số giãn nở) đã được tối ưu hóa cho hình ảnh này và tác vụ này (tìm khối văn bản) và có thể được điều chỉnh, nếu cần, cho các hình ảnh thẻ khác hoặc các loại đối tượng khác được tìm thấy.

Đối với ngưỡng (bước 2), tôi đã sử dụng ngưỡng màu đen. Đối với hình ảnh trong đó văn bản nhẹ hơn nền, chẳng hạn như hình ảnh thứ hai trong bài đăng của bạn, nên sử dụng ngưỡng màu trắng, vì vậy hãy thay thế loại ngưỡng bằng cv2.THRESH_BINARY). Đối với hình ảnh thứ hai, tôi cũng sử dụng giá trị cao hơn một chút cho ngưỡng (180). Thay đổi các tham số cho giá trị ngưỡng và số lần lặp để giãn nở sẽ dẫn đến mức độ nhạy khác nhau trong việc phân định các đối tượng trong ảnh.

Tìm các loại đối tượng khác:

Ví dụ, việc giảm độ giãn xuống 5 lần lặp trong hình ảnh đầu tiên giúp chúng ta phân định rõ hơn các đối tượng trong hình ảnh, gần như tìm thấy tất cả các từ trong hình ảnh (thay vì khối văn bản):

nhập mô tả hình ảnh ở đây

Biết kích thước thô của một từ, ở đây tôi đã loại bỏ các khu vực quá nhỏ (chiều rộng hoặc chiều cao dưới 20 pixel) hoặc quá lớn (chiều rộng hoặc chiều cao trên 100 pixel) để bỏ qua các đối tượng không có khả năng là từ, để có kết quả trong hình ảnh trên.


2
Bạn thật tuyệt! Tôi sẽ thử điều này vào buổi sáng.
Clip

Tôi đã thêm một bước nữa để loại bỏ các đối tượng không quan tâm; cũng đã thêm ví dụ để xác định các từ hoặc các loại đối tượng khác (ngoài các khối văn bản)
anana

Cảm ơn câu trả lời chi tiết, tuy nhiên tôi đang gặp lỗi cv2.findContours. Nó nói ValueError: too many values to unpack.
Abhijith

1
Vấn đề là hàm cv2.findContourstrả về 3 đối số và mã gốc chỉ bắt được 2.
Abhijith

@Abhijith cv2 trong phiên bản hai đã trả về hai đối số, nhưng bây giờ, trong phiên bản ba, nó trả về 3
Tomasz Giba

27

Cách tiếp cận của @ dhanushka cho thấy nhiều hứa hẹn nhất nhưng tôi muốn chơi xung quanh bằng Python nên đã tiếp tục và dịch nó cho vui:

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)

Bây giờ để hiển thị hình ảnh:

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

Không phải là Pythonic nhất của các tập lệnh nhưng tôi đã cố gắng giống với mã C ++ gốc nhất có thể để người đọc theo dõi.

Nó hoạt động gần như bản gốc. Tôi sẽ rất vui khi đọc các đề xuất về cách cải thiện / sửa lỗi để giống với kết quả ban đầu.

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây


3
Cảm ơn bạn đã cung cấp một phiên bản python. Nhiều người sẽ thấy điều này hữu ích. +1
dhanushka

sự khác biệt giữa làm đầy đường viền và vẽ nó là gì? Tôi đã tìm thấy một mã mà không có giai đoạn điền vào đây: stackoverflow.com/a/23556997/6837132
SarahData

@SarahM Tôi không biết nếu bạn hỏi về sự khác biệt chung giữa vẽ và điền (tôi nghĩ khá rõ ràng?) Hoặc API OpenCV cụ thể? Nếu sau đó sẽ thấy các tài liệu cho drawContourstrạng thái đó "Hàm vẽ đường viền đường viền trong ảnh nếu độ dày> 0 hoặc lấp đầy vùng giới hạn bởi các đường viền nếu độ dày <0." Nó đã được thực hiện để chúng ta có thể kiểm tra tỷ lệ pixel khác không để quyết định xem hộp có khả năng chứa văn bản hay không.
rtkaleta

15

Bạn có thể thử phương pháp này được phát triển bởi Chucai Yi và Yingli Tian.

Họ cũng chia sẻ một phần mềm (dựa trên Opencv-1.0 và phần mềm này sẽ chạy trên nền tảng Windows.) Mà bạn có thể sử dụng (mặc dù không có mã nguồn khả dụng). Nó sẽ tạo ra tất cả các hộp giới hạn văn bản (hiển thị trong bóng màu) trong hình ảnh. Bằng cách áp dụng cho hình ảnh mẫu của bạn, bạn sẽ nhận được kết quả sau:

Lưu ý: để làm cho kết quả mạnh mẽ hơn, bạn có thể hợp nhất các hộp liền kề lại với nhau.


Cập nhật: Nếu mục tiêu cuối cùng của bạn là nhận ra các văn bản trong hình ảnh, bạn có thể kiểm tra thêm gttext , đây là một phần mềm miễn phí OCR và công cụ Trutrial cho hình ảnh màu với văn bản. Mã nguồn cũng có sẵn.

Với điều này, bạn có thể nhận được các văn bản được công nhận như:


gttext dành cho windows Bất kỳ đề xuất nào cho người dùng Mac / Linux
Saghir A. Khatri

5

Mã trên phiên bản JAVA: Cảm ơn @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;
}

Và sử dụng mã này trong thực tế:

        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

Triển khai Python cho giải pháp của @ 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

Tại sao nên sử dụng mặt nạ?
SarahData

1
Câu trả lời trùng lặp. Sẽ hữu ích hơn nếu bạn đóng góp vào cuộc trò chuyện trên stackoverflow.com/a/43283990/6809909 .
rtkaleta

2

Đây là phiên bản C # của câu trả lời từ dhanushka bằng 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

đây là phiên bản VB.NET của câu trả lời từ dhanushka bằng EmguCV .

Một vài chức năng và cấu trúc trong EmguCV cần xem xét khác với phiên bản C # với 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"))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.