2015-04-28 42 views
15

mọi lúc và sau đó mẹ tôi phải chuyển qua các loại ảnh này để trích xuất số từ hình ảnh và đổi tên thành số đó. enter image description here enter image description here enter image description hereTrích xuất số bò từ hình ảnh

Tôi đang cố gắng sử dụng OpenCV, Python, Tesseract để có được quá trình thực hiện. Tôi thực sự bị mất cố gắng trích xuất phần hình ảnh bằng các con số. Làm thế nào tôi có thể làm điều này? Bất kỳ lời đề nghị tôi thực sự mới ở OpenCV.

Tôi cố gắng trích xuất bảng hình chữ nhật màu trắng sử dụng các ngưỡng và đường nét, nhưng không có kết quả vì RGB tôi chọn để đập không phải lúc nào cũng hoạt động và tôi không biết cách chọn đường bao.

CHỈNH SỬA:

Nhìn vào giấy này http://yoni.wexlers.org/papers/2010TextDetection.pdf. Trông prominisn

+3

Bạn đã xem là sử dụng [Mechanical Turk] (https : //requester.mturk.com/create/projects/new)? – syrion

+2

Có, nhưng một phần của tôi muốn tham gia vào thử thách này :) – serpiente

Trả lời

3

Sử dụng PIL (Thư viện hình ảnh Python), bạn có thể dễ dàng tải hình ảnh và xử lý chúng. Sử dụng grayscale conversion, bạn có thể chuyển đổi RGB thành thang độ xám, dễ dàng hơn để phát hiện các mức. Nếu bạn muốn ngưỡng ảnh (để phát hiện các bảng trắng), có point() function cho phép bạn ánh xạ màu.

Mặt khác, bạn có thể viết một chương trình đơn giản, cho phép bạn

  • chọn, và hiển thị một hình ảnh
  • đánh dấu khu vực nơi mà các hội đồng là
  • vụ hình ảnh
  • áp dụng tesseract hoặc bất cứ điều gì,
  • lưu hình ảnh với số được phát hiện

Điều đó sẽ tạo thuận lợi cho quá trình này rất nhiều! Việc viết này nên tương đối dễ dàng khi sử dụng TkInter, PyGTK, PyQt hoặc một số bộ công cụ cửa sổ khác.

EDIT: Tôi cần một chương trình tương tự để phân loại hình ảnh tại đây - mặc dù không phải OCRing chúng. Vì vậy, cuối cùng tôi đã quyết định đây là một thời điểm tốt như bất kỳ và thực hiện một thử đầu tiên (với OCR!). Tạo bản sao lưu các hình ảnh của bạn trước khi dùng thử! tay nhanh:

  • trên cùng bên trái: Chọn thư mục làm việc, danh sách hình ảnh sẽ xuất hiện nếu bất kỳ hình ảnh trong thư mục.
  • Chọn hình ảnh. Chọn khu vực hình ảnh có số. Tọa độ sẽ xuất hiện ở góc dưới bên trái, và chương trình sẽ gọi Tesseract.
  • Chỉnh sửa - nếu cần - số OCRd trong hộp thoại.
  • Nhấp vào Ok để chấp nhận - hình ảnh sẽ được đổi tên.

Đây là chương trình pre-alpha:

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
# 
# test_pil.py 
# 
# Copyright 2015 John Coppens <[email protected]> 
# 
# This program is free software; you can redistribute it and/or modify 
# it under the terms of the GNU General Public License as published by 
# the Free Software Foundation; either version 2 of the License, or 
# (at your option) any later version. 
# 
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
# GNU General Public License for more details. 
# 
# You should have received a copy of the GNU General Public License 
# along with this program; if not, write to the Free Software 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
# MA 02110-1301, USA. 
# 
# 

import pygtk 
import gtk 
import glob 
import os.path as osp 
from os import rename 
import re 
import subprocess as sp 

temp_image = "/tmp/test_pil.png" 
image_re = """\.(?:jpe?g|png|gif)$""" 

class RecognizeDigits(): 
    def __init__(self): 
     pass 

    def process(self, img, x0, y0, x1, y1): 
     """ Receive the gtk.Image, and the limits of the selected area (in 
      window coordinates!) 
      Call Tesseract on the area, and give the possibility to edit the 
      result. 
      Returns None if NO is pressed, and the OCR'd (and edited) text if OK 
     """ 
     pb = img.get_pixbuf().subpixbuf(x0, y0, x1-x0, y1-y0) 
     pb.save(temp_image, "png") 

     out = sp.check_output(("tesseract", temp_image, "stdout", "-psm 7", "digits")) 
     out = out.replace(" ", "").strip() 

     dlg = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION, 
           flags = gtk.DIALOG_MODAL, 
           buttons = gtk.BUTTONS_YES_NO, 
           message_format = "The number read is:") 
     entry = gtk.Entry() 
     entry.set_text(out) 
     dlg.get_message_area().pack_start(entry) 
     entry.show() 
     response = dlg.run() 
     nr = entry.get_text() 

     dlg.destroy() 

     if response == gtk.RESPONSE_YES: 
      return nr 
     else: 
      return None 

class FileSelector(gtk.VBox): 
    """ Provides a folder selector (at the top) and a list of files in the 
     selected folder. On selecting a file, the FileSelector calls the 
     function provided to the constructor (image_viewer) 
    """ 
    def __init__(self, image_viewer): 
     gtk.VBox.__init__(self) 
     self.image_viewer = image_viewer 

     fc = gtk.FileChooserButton('Select a folder') 
     fc.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) 
     fc.connect("selection-changed", self.on_file_set) 
     self.pack_start(fc, expand = False, fill = True) 

     self.tstore = gtk.ListStore(str) 
     self.tview = gtk.TreeView(self.tstore) 
     self.tsel = self.tview.get_selection() 
     self.tsel.connect("changed", self.on_selection_changed) 
     renderer = gtk.CellRendererText() 
     col = gtk.TreeViewColumn(None, renderer, text = 0) 
     self.tview.append_column(col) 

     scrw = gtk.ScrolledWindow() 
     scrw.add(self.tview) 
     self.pack_start(scrw, expand = True, fill = True) 

    def on_file_set(self, fcb): 
     self.tstore.clear() 
     self.imgdir = fcb.get_filename() 
     for f in glob.glob(self.imgdir + "/*"): 
      if re.search(image_re, f): 
       self.tstore.append([osp.basename(f)]) 

    def on_selection_changed(self, sel): 
     model, itr = sel.get_selected() 
     if itr != None: 
      base = model.get(itr, 0) 
      fname = self.imgdir + "/" + base[0] 
      self.image_viewer(fname) 

class Status(gtk.Table): 
    """ Small status window which shows the coordinates for of the area 
     selected in the image 
    """ 
    def __init__(self): 
     gtk.Table.__init__(self) 

     self.attach(gtk.Label("X"), 1, 2, 0, 1, yoptions = gtk.FILL) 
     self.attach(gtk.Label("Y"), 2, 3, 0, 1, yoptions = gtk.FILL) 
     self.attach(gtk.Label("Top left:"), 0, 1, 1, 2, yoptions = gtk.FILL) 
     self.attach(gtk.Label("Bottom right:"), 0, 1, 2, 3, yoptions = gtk.FILL) 

     self.entries = {} 
     for coord in (("x0", 1, 2, 1, 2), ("y0", 2, 3, 1, 2), 
         ("x1", 1, 2, 2, 3), ("y1", 2, 3, 2, 3)): 
      self.entries[coord[0]] = gtk.Entry() 
      self.entries[coord[0]].set_width_chars(6) 
      self.attach(self.entries[coord[0]], 
            coord[1], coord[2], coord[3], coord[4], 
            yoptions = gtk.FILL) 

    def set_top_left(self, x0, y0): 
     self.x0 = x0 
     self.y0 = y0 
     self.entries["x0"].set_text(str(x0)) 
     self.entries["y0"].set_text(str(y0)) 

    def set_bottom_right(self, x1, y1): 
     self.x1 = x1 
     self.y1 = y1 
     self.entries["x1"].set_text(str(x1)) 
     self.entries["y1"].set_text(str(y1)) 

class ImageViewer(gtk.ScrolledWindow): 
    """ Provides a scrollwindow to move the image around. It also detects 
     button press and release events (left button), will call status 
     to update the coordinates, and will call task on button release 
    """ 
    def __init__(self, status, task = None): 
     gtk.ScrolledWindow.__init__(self) 

     self.task = task 
     self.status = status 
     self.drawing = False 
     self.prev_rect = None 

     self.viewport = gtk.Viewport() 
     self.viewport.connect("button-press-event", self.on_button_pressed) 
     self.viewport.connect("button-release-event", self.on_button_released) 
     self.viewport.set_events(gtk.gdk.BUTTON_PRESS_MASK | \ 
           gtk.gdk.BUTTON_RELEASE_MASK) 

     self.img = gtk.Image() 
     self.viewport.add(self.img) 
     self.add(self.viewport) 

    def set_image(self, fname): 
     self.imagename = fname 
     self.img.set_from_file(fname) 

    def on_button_pressed(self, viewport, event): 
     if event.button == 1:  # Left button: Select rectangle start 
      #self.x0, self.y0 = self.translate_coordinates(self.img, int(event.x), int(event.y)) 
      self.x0, self.y0 = int(event.x), int(event.y) 
      self.status.set_top_left(self.x0, self.y0) 
      self.drawing = True 

    def on_button_released(self, viewport, event): 
     if event.button == 1:  # Right button: Select rectangle end 
      #self.x1, self.y1 = self.translate_coordinates(self.img, int(event.x), int(event.y)) 
      self.x1, self.y1 = int(event.x), int(event.y) 
      self.status.set_bottom_right(self.x1, self.y1) 
      if self.task != None: 
       res = self.task().process(self.img, self.x0, self.y0, self.x1, self.y1) 

       if res == None: return 

       newname = osp.split(self.imagename)[0] + '/' + res + ".jpeg" 
       rename(self.imagename, newname) 
       print "Renaming ", self.imagename, newname 

class MainWindow(gtk.Window): 
    def __init__(self): 
     gtk.Window.__init__(self) 
     self.connect("delete-event", self.on_delete_event) 
     self.set_size_request(600, 300) 

     grid = gtk.Table() 

     # Image selector 
     files = FileSelector(self.update_image) 
     grid.attach(files, 0, 1, 0, 1, 
          yoptions = gtk.FILL | gtk.EXPAND, xoptions = gtk.FILL) 

     # Some status information 
     self.status = Status() 
     grid.attach(self.status, 0, 1, 1, 2, 
           yoptions = gtk.FILL, xoptions = gtk.FILL) 

     # The image viewer 
     self.viewer = ImageViewer(self.status, RecognizeDigits) 
     grid.attach(self.viewer, 1, 2, 0, 2) 
     self.add(grid) 

     self.show_all() 

    def update_image(self, fname): 
     self.viewer.set_image(fname) 

    def on_delete_event(self, wdg, data): 
     gtk.main_quit() 

    def run(self): 
     gtk.mainloop() 

def main(): 
    mw = MainWindow() 
    mw.run() 
    return 0 

if __name__ == '__main__': 
    main() 
+0

Cảm ơn bạn jcoppens! Sẽ làm như thế này Tìm kiếm các số bằng algo là ngoài tầm với của tôi – serpiente

+0

Một bước cơ bản hơn nữa - có thể trước khi nhận dạng hình ảnh bằng tesseract - có thể hiển thị hình ảnh và nhập số. Sau đó, chương trình có thể thực hiện đổi tên hình ảnh. Sau đó, bạn có thể chèn cuộc gọi để tesseract sau. – jcoppens

+0

Đây là một ý tưởng hay ... nếu bạn không thể tự động hóa hoàn toàn quy trình, ít nhất bạn có thể tạo ra một công cụ để thực hiện quy trình thủ công và làm cho nó ** nhanh hơn ** đối với người đang xử lý. –

2

Tôi đã được nhìn này một chút và suy nghĩ làm thế nào tôi có thể giải quyết nó. Tôi thích phần mềm ImageMagick miễn phí có sẵn cho OSX, được cài đặt trên hầu hết các bản phân phối Linux và có sẵn cho Windows.

Phản ứng đầu tiên của tôi là thử thiết bị dò cạnh Sobel với hình ảnh, sau đó ngưỡng đó và loại bỏ nhiễu và các ngoại lệ bằng bộ lọc trung bình.

tôi có thể làm tất cả những gì với một lệnh duy nhất tại dòng lệnh như thế này:

convert c1.jpg               \ 
    -define convolve:scale='50%!' -bias 50% -morphology Convolve Sobel \ 
    -solarize 50% -level 50,0% -auto-level -threshold 50% -median 3 result.jpg 

nơi c1.jpg là bò đầu tiên của bạn, và tương tự như vậy cho những con bò khác.

tôi kết thúc với điều này:

enter image description here

enter image description here

enter image description here

đó là một điểm khởi đầu khá hợp lý để làm việc ra nơi mà con số này trong hình ảnh. Tôi đang nghĩ về việc chia hình ảnh thành các ô tiếp theo, hoặc các kỹ thuật khác, và sau đó tôi sẽ xem xét các ô/khu vực có màu trắng nhất. Bằng cách đó tôi sẽ bắt đầu để có được một xử lý về nơi tôi nên được chỉ tesseract để xem ... tuy nhiên, nó là trước khi đi ngủ - rõ ràng. Có thể ai đó thông minh như @rayryeng sẽ xem qua đêm :-)

+0

Một số đề xuất nếu đi theo đường dẫn này: có các hình thức phát hiện theo cả hai hướng. xem http://www.imagemagick.org/Usage/transform/#edge.Tuy nhiên, việc nhận dạng mẫu chung cho các ký tự hoạt động tốt hơn nếu 'điền' không bị mất. Nó cung cấp cho thuật toán thêm thông tin để làm việc. Làm tương quan giữa các chữ số được lưu trữ và các chữ số được quét sẽ hoạt động tốt. Một con đường thú vị sẽ là phát hiện cạnh để phát hiện các bảng so với hình chữ nhật, sau đó OCR trên hình ảnh gốc, bị cắt. – jcoppens

-1

Giải pháp tốt nhất cho những vấn đề như vậy hầu như luôn là một vấn đề mang tính hệ thống.

Điều mà ngày nay có thể được phát hiện một cách rẻ tiền, nhanh chóng và đáng tin cậy trong một hình ảnh là khuôn mặt người. Vì vậy, tôi đề nghị bạn thay đổi một chút giao thức để chụp ảnh. Thay vì cắt mặt của người đang cầm bảng, hãy để họ giữ nó ngay dưới cằm rồi chạy Viola-Jones. QED

Bạn có thể làm điều đó hoặc bạn phải làm việc với các hình ảnh như chúng (như trong trường hợp có một cơ sở dữ liệu kế thừa lớn để làm việc thông qua)? Nếu có, hãy làm những gì tôi đề nghị và bạn sẽ ổn thôi. Nếu không, tôi có thể đề xuất một giải pháp IP thuần túy nhưng nó sẽ phức tạp hơn.

+0

thậm chí bạn đã đọc câu hỏi chưa? – berak

+0

@berak Tất nhiên là tôi đã làm. Đây là những gì OP đã viết: "Tôi đã cố gắng trích xuất bảng hình chữ nhật màu trắng bằng cách sử dụng các ngưỡng và đường nét". Xem - anh ta dường như có thể sử dụng các công cụ tiêu chuẩn để lấy các chữ số khi anh ta có ROI của họ, nhưng anh ấy không thể tự động phát hiện ROI (đúng vậy, đó là vấn đề khó khăn hơn nhiều). Vì vậy, tôi đã đề xuất với anh ta một giải pháp cho phần này (vấn đề quan trọng) của vấn đề của anh ấy. Bạn có hài lòng khi tôi đã đọc câu hỏi và tôi biết tôi đang nói về điều gì không? Nếu có, bạn có thể muốn đảo ngược downvote của bạn ... :) –

+0

ok, một phần đọc sai nó. Tuy nhiên, bạn không thể kiểm soát nội dung của các bức ảnh đã chụp – berak

6

Tôi đã được gặp một cái nhìn lúc này, và đã có một vài nguồn cảm hứng trên đường đi ....

  1. Tesseract có thể chấp nhận các từ điển tùy chỉnh, và nếu bạn đào thêm một chút, dường như từ v3.0, nó chấp nhận tham số dòng lệnh digits để làm cho nó nhận ra chữ số chỉ - dường như là một ý tưởng hữu ích cho nhu cầu của bạn.

  2. Có thể không cần thiết phải tìm các bảng có các chữ số - có thể dễ dàng chạy Tesseract nhiều lần với các lát hình ảnh khác nhau và để nó tự thử như vậy .

Vì vậy, tôi quyết định xử lý trước hình ảnh bằng cách thay đổi mọi thứ trong khoảng 25% từ màu đen thành màu đen tinh khiết và mọi thứ khác thành màu trắng tinh khiết. Điều đó mang lại những hình ảnh trước xử lý như thế này:

enter image description here

enter image description here

enter image description here

Tiếp theo, tôi tạo ra một loạt các hình ảnh và vượt qua chúng, cùng một lúc để Tesseract.Tôi quyết định giả định rằng các chữ số có thể nằm trong khoảng từ 40% đến 10% chiều cao của hình ảnh, vì vậy tôi đã tạo một vòng lặp trên các dải 40, 30, 20 và 10% chiều cao của hình ảnh. Tôi sau đó trượt dải xuống hình ảnh từ trên xuống dưới trong 20 bước đi qua mỗi dải để Tesseract, cho đến khi dải cơ bản là trên dưới cùng của hình ảnh.

Dưới đây là các dải 40% - mỗi khung hình của phim hoạt hình được chuyển cho Tesseract:

enter image description here

Dưới đây là 20% dải - mỗi khung hình của phim hoạt hình được chuyển cho Tesseract:

enter image description here

Có dải, tôi thay đổi kích thước của chúng cho điểm ngọt của Tesseract và làm sạch chúng khỏi tiếng ồn. Sau đó, tôi chuyển chúng vào Tesseract và đánh giá chất lượng công nhận, vì vậy mewhat crudely, bằng cách đếm số chữ số nó được tìm thấy. Cuối cùng, tôi sắp xếp đầu ra theo số chữ số - có lẽ nhiều chữ số hơn có thể là tốt hơn ...

Có một số cạnh và bit thô ráp mà bạn có thể bắt đầu, nhưng đó là sự khởi đầu!

#!/bin/bash 
image=${1-c1.jpg} 

# Make everything that is nearly black go fully black, everything else goes white. Median for noise 
# convert -delay 500 c1.jpg c2.jpg c3.jpg -normalize -fuzz 25% -fill black -opaque black -fuzz 0 -fill white +opaque black -median 9 out.gif 
    convert "${image}" -normalize \ 
     -fuzz 25% -fill black -opaque black \ 
     -fuzz 0 -fill white +opaque black \ 
     -median 9 tmp_$$.png 

# Get height of image - h 
h=$(identify -format "%h" "${image}") 

# Generate strips that are 40%, 30%, 20% and 10% of image height 
for pc in 40 30 20 10; do 
    # Calculate height of this strip in pixels - sh 
    ((sh=(h*pc)/100)) 
    # Calculate offset from top of picture to top of bottom strip - omax 
    ((omax=h-sh)) 
    # Calculate step size, there will be 20 steps 
    ((step=omax/20)) 

    # Cut strips sh pixels high from the picture starting at top and working down in 20 steps 
    for ((off=0;off<$omax;off+=$step)) do 
     t=$(printf "%05d" $off) 
     # Extract strip and resize to 80 pixels tall for tesseract 
     convert tmp_$$.png -crop x${sh}+0+${off}  \ 
      -resize x80 -median 3 -median 3 -median 3 \ 
      -threshold 90% +repage slice_${pc}_${t}.png 

     # Run slice through tesseract, seeking only digits 
     tesseract slice_${pc}_${t}.png temp digits quiet 

     # Now try and assess quality of output :-) ... by counting number of digits 
     digits=$(tr -cd "[0-9]" < temp.txt) 
     ndigits=${#digits} 
     [ $ndigits -gt 0 ] && [ $ndigits -lt 6 ] && echo $ndigits:$digits 
    done 
done | sort -n 

Output cho Bò 618 (số đầu tiên là số lượng chữ số tìm thấy)

2:11 
2:11 
3:573 
5:33613 <--- not bad 

Output cho Bò 2755 (số đầu tiên là số chữ số được tìm thấy)

2:51 
3:071 
3:191 
3:517 
4:2155 <--- pretty close 
4:2755 <--- nailed that puppy :-) 
4:2755 <--- nailed that puppy :-) 
4:5212 
5:12755 <--- pretty close 

Đầu ra cho bò 3174 (số đầu tiên là số r của các chữ số được tìm thấy)

3:554 
3:734 
5:12732 
5:31741 <--- pretty close 

Câu hỏi hay - cảm ơn!

0

Tôi thực sự thích vấn đề này nhưng tôi không quen với OpenCV và Python, vì vậy tôi trình bày một giải pháp một phần trong Matlab. Ý tưởng là phần quan trọng và mã chỉ để tham khảo. Tôi nghĩ rằng việc sử dụng xử lý hình ảnh của tôi có thể được tăng cường với ý tưởng cửa sổ của nhãn hiệu có thể cung cấp cho bạn kết quả thuận lợi.

Những hình ảnh này có rất nhiều thảm thực vật và thực vật thường có nhiều màu xanh và đỏ. Tôi chỉ xử lý kênh xanh lam loại bỏ rất nhiều thảm thực vật và vẫn để lại những dấu hiệu màu trắng dễ nhận biết. Sau đó tôi sử dụng phương pháp otsus để làm nổi bật một cái gì đó như thế này trong OpenCV cv::threshold(im_gray, img_bw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); nhưng sau đó tôi lấy 1,5 lần ngưỡng đã cho. do đó, ngưỡng vẫn là hình ảnh cụ thể bởi otsu, nhưng cũng rất có chọn lọc. Tại thời điểm này, bạn có một hình ảnh khá tốt.

Sau đó, chúng tôi chỉ làm sạch hình ảnh với một số xói mòn và giãn nở. Cũng lưu ý rằng yếu tố giãn nở của tôi hơi lớn hơn một phần xói mòn của tôi. Sau đó bạn có hình ảnh với số lượng khá sạch sẽ. Có lẽ tesserect thậm chí có thể chỉ xử lý hình ảnh vào thời điểm này, hoặc bạn có thể thử nó với cửa sổ.

Tôi biết OpenCV có các chức năng tương tự, nhưng như tôi đã nói tôi đã làm những gì tôi đã quen thuộc. Tôi hy vọng nó sẽ giúp bạn.đây là kết quả của tôi:

processed images

và mã

%382 is 255*1.5 so basically we are taking the auto threshold and raising it by 
%50 percent. graythresh is performing otsu thresholding 
bim = im(:,:,3) > graythresh(im(:,:,3))*382; 
bim1 = im1(:,:,3) > graythresh(im1(:,:,3))*382; 
bim2 = im2(:,:,3) > graythresh(im2(:,:,3))*382; 

%se and se2 are what opencv would call getStructuringElement using a 
%MORPH_ELLIPSE 
se = strel('disk',3); 
eim = imerode(bim,se); 
eim1 = imerode(bim1,se); 
eim2 = imerode(bim2,se); 

%se and se2 are what opencv would call getStructuringElement using a 
%MORPH_ELLIPSE 
se2 = strel('disk',5); 
dim = imdilate(eim,se2); 
dim1 = imdilate(eim1,se2); 
dim2 = imdilate(eim2,se2); 

subplot(3,3,1);imshow(bim);title('blue thresholded'); 
subplot(3,3,2);imshow(bim1);title(''); 
subplot(3,3,3);imshow(bim2);title(''); 

subplot(3,3,4);imshow(eim);title('after errosion'); 
subplot(3,3,5);imshow(eim1);title(''); 
subplot(3,3,6);imshow(eim2);title(''); 

subplot(3,3,7);imshow(dim);title('after dilation'); 
subplot(3,3,8);imshow(dim1);title(''); 
subplot(3,3,9);imshow(dim2);title(''); 
0

tôi đưa ra một giải pháp khá đơn giản với sự giúp đỡ của OpenCV.

  1. Thay đổi kích thước hình ảnh để prune outlier bởi đường nét (dễ dàng hơn để đo lường khu vực)

    std::string const img_name = "cow_00"; 
    Mat input = imread("../forum_quest/data/" + img_name + ".jpg"); 
    cout<<input.size()<<endl; 
    if(input.empty()){ 
        cerr<<"cannot open image\n"; 
        return; 
    } 
    if(input.cols > 1000){ 
        cv::resize(input, input, {1000, (int)(1000.0/input.cols * input.rows)}, 0.25, 0.25); 
    } 
    
  2. Crop khu vực trên 1/3

    //Assume the text always lie on top 1/3 of the image 
    Mat crop_region; 
    input(Rect(0, 0, input.cols, input.rows/3)).copyTo(crop_region); 
    
  3. Extract foreground

    cv::Mat fore_ground_extract(cv::Mat const &input) 
    { 
        vector<Mat> bgr; 
        split(input, bgr); 
    
        //process on blue channel as andrew suggest, because it is 
        //easier to get rid of vegetation 
        Mat text_region = bgr[0]; 
        medianBlur(text_region, text_region, 5); 
        threshold(text_region, text_region, 0, 255, cv::THRESH_OTSU); 
    
        //further remove small noise, unwanted border 
        Mat const erode_kernel = getStructuringElement(MORPH_ELLIPSE, {11, 11}); 
        erode(text_region, text_region, erode_kernel); 
        Mat const dilate_kernel = getStructuringElement(MORPH_ELLIPSE, {7, 7}); 
        dilate(text_region, text_region, dilate_kernel); 
    
        //change the text from black to white, easier to extract as contours 
        bitwise_not(text_region, text_region); 
    
        return text_region; 
    } 
    

enter image description here

enter image description here

enter image description here

  1. Extract đường viền, bạn có thể sử dụng ERFilter để trích xuất văn bản nếu tính chính xác của đường nét thấp

    std::vector<std::vector<cv::Point>> get_text_contours(cv::Mat const &input) 
    { 
        //Find the contours of candidate text, remove outlier with 
        //some contour properties 
        //Try ERFilter of opencv if accuracy of this solution is low 
        vector<cpoints> contours; 
        findContours(input, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); 
        auto outlier = [](cpoints const &cp) 
        { 
         auto const rect = cv::boundingRect(cp); 
    
         return rect.width > rect.height && (rect.area() < 900 || rect.area() >= 10000); 
        }; 
        auto it = std::remove_if(std::begin(contours), std::end(contours), outlier); 
        contours.erase(it, std::end(contours)); 
    
        std::sort(std::begin(contours), std::end(contours), [](cpoints const &lhs, cpoints const &rhs) 
        { 
         return cv::boundingRect(lhs).x < cv::boundingRect(rhs).x; 
        }); 
    
        return contours; 
    } 
    
  2. Tạo trình phân loại ký tự và lo op qua ứng cử viên văn bản

    string const vocabulary = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // must have the same order as the classifier output classes 
        Ptr<text::OCRHMMDecoder::ClassifierCallback> ocr = text::loadOCRHMMClassifierCNN("OCRBeamSearch_CNN_model_data.xml.gz"); 
        vector<int> out_classes; 
        vector<double> out_confidences; 
        for(size_t i = 0; i < text_contours.size(); ++i){ 
         Scalar const color = Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255)); 
         drawContours(text_mask, text_contours, static_cast<int>(i), color, 2); 
         auto const text_loc = boundingRect(text_contours[i]); 
         //crop_region can gain highest accuracy since it is trained on scene image 
         rectangle(crop_region, text_loc, color, 2); 
         ocr->eval(crop_region(text_loc), out_classes, out_confidences); 
         cout << "OCR output = \"" << vocabulary[out_classes[0]] 
           << "\" with confidence " 
           << out_confidences[0] << std::endl; 
         putText(crop_region, string(1, vocabulary[out_classes[0]]), Point(text_loc.x, text_loc.y - 5), 
          FONT_HERSHEY_SIMPLEX, 2, Scalar(255, 0, 0), 2); 
    
         imshow("text_mask", text_mask); 
         imshow("crop_region", crop_region(text_loc)); 
         waitKey(); 
        } 
    

Kết quả:

enter image description here enter image description here enter image description here

mã nguồn hoàn chỉnh là diễn ra tại github

Các vấn đề liên quan