Nhận vị trí của tất cả văn bản hiện diện trong hình ảnh bằng cách sử dụng opencv


11

Tôi có hình ảnh này chứa văn bản (số và bảng chữ cái) trong đó. Tôi muốn có được vị trí của tất cả các văn bản và số hiện diện trong hình ảnh này. Ngoài ra tôi muốn trích xuất tất cả các văn bản là tốt.

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

Làm thế nào để tôi có được các kết nối cũng như tất cả các văn bản (số và bảng chữ cái) trong hình ảnh của tôi. Ví dụ: 10B, 44, 16, 38, 22B, v.v.


phiên bản tenorflow của bạn là gì? Nếu phiên bản của bạn là 2.1, hãy thử cài đặt 2.0
gellezzz

1
Ném tiền thưởng vào các câu hỏi xấu nó không phải là một thực hành tốt. Bạn đã cho thấy không có kiến ​​thức về cách thực hiện việc này nên có vẻ như bạn chỉ đang cố gắng lôi kéo các nhà phát triển mã hóa một giải pháp hoàn chỉnh để đổi lấy một vài điểm đại diện. Tôi không mong đợi để thấy câu trả lời hoàn hảo cho lý do đó, nhưng tôi tin rằng bạn có thể nhận được giải pháp tốt hơn tại các trang web tự do nếu bạn trả tiền cho mọi người cho thời gian của họ.
karlphillip

@karlphillip rất xin lỗi, nhưng tôi là người mới bắt đầu tôi cần một cái gì đó để bắt đầu, phải không? Bạn có thể giúp tôi với cái này không
Pulkit Bhatnagar

Câu trả lời:


13

Đây là một cách tiếp cận tiềm năng sử dụng các hoạt động hình thái để lọc ra các đường viền phi văn bản. Ý tưởng là:

  1. Có được hình ảnh nhị phân. Tải hình ảnh, thang độ xám, rồi ngưỡng của Otsu

  2. Loại bỏ các đường ngang và dọc. Tạo hạt nhân ngang và dọc bằng cách sử dụng cv2.getStructuringElementsau đó loại bỏ các dòng vớicv2.drawContours

  3. Xóa các đường chéo, đối tượng hình tròn và đường viền cong. Lọc sử dụng diện tích đường viền cv2.contourArea và xấp xỉ đường viền cv2.approxPolyDP để cô lập các đường viền phi văn bản

  4. Trích xuất ROI văn bản và OCR. Tìm đường viền và bộ lọc cho ROI sau đó OCR bằng Pytesseract .


Đã xóa các đường ngang được tô sáng màu xanh lá cây

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

Đã xóa các đường thẳng đứng

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

Đã xóa các loại đường viền phi văn bản (đường chéo, đối tượng tròn và đường cong)

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

Vùng văn bản được phát hiện

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

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
clean = thresh.copy()

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,30))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

cnts = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    # Remove diagonal lines
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(clean, [c], -1, 0, 3)
    # Remove circle objects
    elif area > 1000:
        cv2.drawContours(clean, [c], -1, 0, -1)
    # Remove curve stuff
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    x,y,w,h = cv2.boundingRect(c)
    if len(approx) == 4:
        cv2.rectangle(clean, (x, y), (x + w, y + h), 0, -1)

open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
opening = cv2.morphologyEx(clean, cv2.MORPH_OPEN, open_kernel, iterations=2)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,2))
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, close_kernel, iterations=4)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = cv2.contourArea(c)
    if area > 500:
        ROI = image[y:y+h, x:x+w]
        ROI = cv2.GaussianBlur(ROI, (3,3), 0)
        data = pytesseract.image_to_string(ROI, lang='eng',config='--psm 6')
        if data.isalnum():
            cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
            print(data)

cv2.imwrite('image.png', image)
cv2.imwrite('clean.png', clean)
cv2.imwrite('close.png', close)
cv2.imwrite('opening.png', opening)
cv2.waitKey()

Ý tưởng tốt để loại bỏ những dòng đầu tiên.
karlphillip

ý tưởng tồi để xóa các phần của chữ cái ...
Walter Tross

8

Được rồi, đây là một giải pháp khả thi khác. Tôi biết bạn làm việc với Python - Tôi làm việc với C ++. Tôi sẽ cung cấp cho bạn một số ý tưởng và hy vọng, nếu bạn mong muốn như vậy, bạn sẽ có thể thực hiện câu trả lời này.

Ý tưởng chính là hoàn toàn không sử dụng tiền xử lý (ít nhất là không ở giai đoạn ban đầu) và thay vào đó tập trung vào từng nhân vật mục tiêu, nhận một số thuộc tínhlọc mọi blob theo các thuộc tính này.

Tôi đang cố gắng không sử dụng tiền xử lý vì: 1) Các bộ lọc và giai đoạn hình thái có thể làm giảm chất lượng của các đốm màu và 2) các đốm màu mục tiêu của bạn dường như thể hiện một số đặc điểm mà chúng ta có thể khai thác, chủ yếu là: tỷ lệ khung hìnhdiện tích .

Hãy kiểm tra xem, tất cả các số và chữ có vẻ cao hơn rộng hơn nữa, chúng dường như thay đổi trong một giá trị khu vực nhất định. Ví dụ: bạn muốn loại bỏ các đối tượng "quá rộng" hoặc "quá lớn" .

Ý tưởng là tôi sẽ lọc mọi thứ không nằm trong các giá trị được tính toán trước. Tôi đã kiểm tra các ký tự (số và chữ) và đi kèm với các giá trị diện tích tối thiểu, tối đa và tỷ lệ khung hình tối thiểu (ở đây, tỷ lệ giữa chiều cao và chiều rộng).

Hãy làm việc với thuật toán. Bắt đầu bằng cách đọc hình ảnh và thay đổi kích thước của nó thành một nửa kích thước. Hình ảnh của bạn quá lớn Chuyển đổi sang thang độ xám và nhận hình ảnh nhị phân qua otsu, đây là mã giả:

//Read input:
inputImage = imread( "diagram.png" );

//Resize Image;
resizeScale = 0.5;

inputResized = imresize( inputImage, resizeScale );

//Convert to grayscale;
inputGray = rgb2gray( inputResized );

//Get binary image via otsu:
binaryImage = imbinarize( inputGray, "Otsu" );

Mát mẻ. Chúng tôi sẽ làm việc với hình ảnh này. Bạn cần kiểm tra từng đốm trắng và áp dụng "bộ lọc thuộc tính" . Tôi đang sử dụng các thành phần được kết nối với các số liệu thống kê để lặp lại từng máng và lấy tỷ lệ diện tích và khía cạnh của nó, trong C ++, việc này được thực hiện như sau:

//Prepare the output matrices:
cv::Mat outputLabels, stats, centroids;
int connectivity = 8;

//Run the binary image through connected components:
int numberofComponents = cv::connectedComponentsWithStats( binaryImage, outputLabels, stats, centroids, connectivity );

//Prepare a vector of colors  color the filtered blobs in black
std::vector<cv::Vec3b> colors(numberofComponents+1);
colors[0] = cv::Vec3b( 0, 0, 0 ); // Element 0 is the background, which remains black.

//loop through the detected blobs:
for( int i = 1; i <= numberofComponents; i++ ) {

    //get area:
    auto blobArea = stats.at<int>(i, cv::CC_STAT_AREA);

    //get height, width and compute aspect ratio:
    auto blobWidth = stats.at<int>(i, cv::CC_STAT_WIDTH);
    auto blobHeight = stats.at<int>(i, cv::CC_STAT_HEIGHT);
    float blobAspectRatio = (float)blobHeight/(float)blobWidth;

    //Filter your blobs

};

Bây giờ, chúng tôi sẽ áp dụng bộ lọc thuộc tính. Đây chỉ là một so sánh với các ngưỡng được tính toán trước. Tôi đã sử dụng các giá trị sau:

Minimum Area: 40  Maximum Area:400
MinimumAspectRatio:  1

Trong forvòng lặp của bạn , so sánh các thuộc tính blob hiện tại với các giá trị này. Nếu các xét nghiệm dương tính, bạn "vẽ" màu đen của blob. Tiếp tục bên trong forvòng lặp:

    //Filter your blobs

    //Test the current properties against the thresholds:
    bool areaTest =  (blobArea > maxArea)||(blobArea < minArea);
    bool aspectRatioTest = !(blobAspectRatio > minAspectRatio); //notice we are looking for TALL elements!

    //Paint the blob black:
    if( areaTest || aspectRatioTest ){
        //filtered blobs are colored in black:
        colors[i] = cv::Vec3b( 0, 0, 0 );
    }else{
        //unfiltered blobs are colored in white:
        colors[i] = cv::Vec3b( 255, 255, 255 );
    }

Sau vòng lặp, xây dựng hình ảnh được lọc:

cv::Mat filteredMat = cv::Mat::zeros( binaryImage.size(), CV_8UC3 );
for( int y = 0; y < filteredMat.rows; y++ ){
    for( int x = 0; x < filteredMat.cols; x++ )
    {
        int label = outputLabels.at<int>(y, x);
        filteredMat.at<cv::Vec3b>(y, x) = colors[label];
    }
}

Và đó là khá nhiều đó. Bạn đã lọc tất cả các yếu tố không giống với những gì bạn đang tìm kiếm. Chạy thuật toán bạn nhận được kết quả này:

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

Ngoài ra, tôi còn tìm thấy Hộp Bounding của các đốm màu để hình dung rõ hơn về kết quả:

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

Như bạn thấy, một số yếu tố bị phát hiện sai. Bạn có thể tinh chỉnh "bộ lọc thuộc tính" để xác định tốt hơn các ký tự bạn đang tìm kiếm. Một giải pháp sâu hơn, liên quan đến một chút học máy, đòi hỏi phải xây dựng một "vectơ đặc trưng lý tưởng", trích xuất các tính năng từ các đốm màu và so sánh cả hai vectơ thông qua một phép đo tương tự. Bạn cũng có thể áp dụng một số xử lý hậu kỳ để cải thiện kết quả ...

Dù sao đi nữa, vấn đề của bạn không phải là tầm thường hay dễ mở rộng, và tôi chỉ đưa ra ý tưởng cho bạn. Hy vọng, bạn sẽ có thể thực hiện giải pháp của bạn.


Bất kỳ cơ hội nào bạn cũng có thể chuyển đổi cùng một chương trình thành python
Pulkit Bhatnagar

@PulkitBhatnagar Vâng, tất nhiên. Bạn chỉ cần giữ chặt, tôi sẽ có một cổng hoàn hảo sẵn sàng trong vài phút.
eldesgraciado

?? bạn đã làm điều đó, để tôi có thể trao tiền thưởng cho bạn
Pulkit Bhatnagar

À đúng rồi. Tôi vô cùng xin lỗi, thưa ông, tôi đã gặp một số rắc rối, nhưng việc chuyển đổi đang diễn ra tốt đẹp. Chỉ cần bạn chờ đợi. Cám ơn.
eldesgraciado

không bao giờ tự hỏi rằng có thể là một châm biếm.
Pulkit Bhatnagar

4

Một phương pháp là sử dụng cửa sổ trượt (Nó đắt tiền).

Xác định kích thước của các ký tự trong hình ảnh (tất cả các ký tự có cùng kích thước như nhìn thấy trong hình ảnh) và đặt kích thước của cửa sổ. Hãy thử tesseract để phát hiện (Hình ảnh đầu vào yêu cầu xử lý trước). Nếu một cửa sổ phát hiện các ký tự liên tiếp, thì lưu trữ tọa độ của cửa sổ. Hợp nhất các tọa độ và lấy vùng trên các ký tự.


Tôi nghĩ 100bounty là cho câu trả lời
Himanshu Poddar
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.