การใช้ YOLO หรือเทคนิคการจดจำภาพอื่น ๆ เพื่อระบุข้อความตัวอักษรและตัวเลขทั้งหมดที่ปรากฏในภาพ


12

ฉันมีแผนภาพรูปภาพหลายภาพซึ่งทั้งหมดมีป้ายกำกับเป็นตัวอักษรและตัวเลขแทนที่จะเป็นเพียงป้ายข้อความเท่านั้น ฉันต้องการให้แบบจำลอง YOLO ของฉันเพื่อระบุตัวเลขและตัวอักษรและตัวเลขทั้งหมดที่มีอยู่ในนั้น

ฉันจะฝึกโมเดล YOLO ของฉันให้ทำเช่นเดียวกันได้อย่างไร ชุดข้อมูลสามารถพบได้ที่นี่ https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

ตัวอย่างเช่น: ดูกล่อง จำกัด ฉันต้องการให้ YOLO ตรวจพบทุกที่ที่มีข้อความ อย่างไรก็ตามในปัจจุบันไม่จำเป็นต้องระบุข้อความภายใน

ป้อนคำอธิบายรูปภาพที่นี่

นอกจากนี้ยังจำเป็นต้องทำสิ่งเดียวกันสำหรับรูปภาพประเภทนี้ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

สามารถดาวน์โหลดภาพได้ที่นี่

นี่คือสิ่งที่ฉันได้ลองใช้โดยใช้ opencv แต่มันใช้ไม่ได้กับภาพทั้งหมดในชุดข้อมูล

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r"C:\Users\HPO2KOR\AppData\Local\Tesseract-OCR\tesseract.exe"

image = cv2.imread(r'C:\Users\HPO2KOR\Desktop\Work\venv\Patent\PARTICULATE DETECTOR\PD4.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
clean = thresh.copy()

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)

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:
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(clean, [c], -1, 0, 3)
    elif area > 1000:
        cv2.drawContours(clean, [c], -1, 0, -1)
    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()

มีแบบจำลองหรือเทคนิค opencv หรือแบบจำลองที่ได้รับการฝึกฝนมาก่อนซึ่งสามารถทำแบบเดียวกันกับฉันได้หรือไม่? ฉันต้องการกล่องที่ล้อมรอบตัวอักษรและตัวเลขทั้งหมดที่อยู่ในภาพ หลังจากนั้นฉันต้องระบุสิ่งที่มีอยู่ในนั้น อย่างไรก็ตามส่วนที่สองไม่สำคัญในขณะนี้


สิ่งนี้ตอบคำถามของคุณหรือไม่ OpenCV! _src.empty () ในฟังก์ชั่นข้อผิดพลาด 'cvtColor'
Amit Yadav


ที่ใช้ไม่ได้กับภาพทั้งหมด
Pulkit Bhatnagar

คำตอบ:


7

วิธีการที่เป็นไปได้คือการใช้ EAST (ประสิทธิภาพและถูกต้องฉาก Text) ลึกการเรียนรู้เครื่องตรวจจับข้อความบนพื้นฐานของโจว et al, 2017 กระดาษ. EAST: มีประสิทธิภาพและถูกต้องฉากข้อความตรวจจับ เดิมรุ่นนี้ได้รับการฝึกฝนเพื่อตรวจจับข้อความในภาพทิวทัศน์ธรรมชาติ แต่อาจนำไปใช้กับภาพไดอะแกรมได้ EAST ค่อนข้างแข็งแกร่งและสามารถตรวจจับข้อความเบลอหรือแสงสะท้อนได้ นี่คือการปรับใช้ EASTของAdrian Rosebrock ที่ได้รับการแก้ไข. แทนที่จะใช้เครื่องตรวจจับข้อความบนภาพโดยตรงเราสามารถลองลบวัตถุที่ไม่ใช่ข้อความบนภาพก่อนทำการตรวจจับข้อความ แนวคิดคือการลบเส้นแนวนอน, เส้นแนวตั้งและรูปทรงที่ไม่ใช่ข้อความ (เส้นโค้ง, เส้นทแยงมุม, รูปทรงกลม) ก่อนที่จะใช้การตรวจจับ นี่คือผลลัพธ์ที่มีบางภาพของคุณ:

อินพุต->รูปทรงที่ไม่ใช่ข้อความเพื่อลบออกเป็นสีเขียว

ผลลัพธ์

ภาพอื่น ๆ

pretrained frozen_east_text_detection.pbรุ่นจำเป็นที่จะต้องดำเนินการตรวจสอบข้อความที่สามารถพบได้ที่นี่ แม้ว่าแบบจำลองจะจับข้อความส่วนใหญ่ได้ แต่ผลลัพธ์นั้นไม่ถูกต้อง 100% และอาจมีผลบวกที่ผิดพลาดเป็นครั้งคราวอาจเป็นเพราะวิธีการฝึกฝนภาพทิวทัศน์ธรรมชาติ เพื่อให้ได้ผลลัพธ์ที่แม่นยำยิ่งขึ้นคุณอาจต้องฝึกรูปแบบที่คุณกำหนดเอง แต่ถ้าคุณต้องการทางออกที่ดีทันทีสิ่งนี้น่าจะเหมาะกับคุณ ลองดูโพสต์บล็อกOpenCVของการตรวจจับข้อความ (EAST Text detector)ของ Adrian สำหรับคำอธิบายที่ครอบคลุมกว่านี้ของตัวตรวจจับข้อความ EAST

รหัส

from imutils.object_detection import non_max_suppression
import numpy as np
import cv2

def EAST_text_detector(original, image, confidence=0.25):
    # Set the new width and height and determine the changed ratio
    (h, W) = image.shape[:2]
    (newW, newH) = (640, 640)
    rW = W / float(newW)
    rH = h / float(newH)

    # Resize the image and grab the new image dimensions
    image = cv2.resize(image, (newW, newH))
    (h, W) = image.shape[:2]

    # Define the two output layer names for the EAST detector model that
    # we are interested -- the first is the output probabilities and the
    # second can be used to derive the bounding box coordinates of text
    layerNames = [
        "feature_fusion/Conv_7/Sigmoid",
        "feature_fusion/concat_3"]

    net = cv2.dnn.readNet('frozen_east_text_detection.pb')

    # Construct a blob from the image and then perform a forward pass of
    # the model to obtain the two output layer sets
    blob = cv2.dnn.blobFromImage(image, 1.0, (W, h), (123.68, 116.78, 103.94), swapRB=True, crop=False)
    net.setInput(blob)
    (scores, geometry) = net.forward(layerNames)

    # Grab the number of rows and columns from the scores volume, then
    # initialize our set of bounding box rectangles and corresponding
    # confidence scores
    (numRows, numCols) = scores.shape[2:4]
    rects = []
    confidences = []

    # Loop over the number of rows
    for y in range(0, numRows):
        # Extract the scores (probabilities), followed by the geometrical
        # data used to derive potential bounding box coordinates that
        # surround text
        scoresData = scores[0, 0, y]
        xData0 = geometry[0, 0, y]
        xData1 = geometry[0, 1, y]
        xData2 = geometry[0, 2, y]
        xData3 = geometry[0, 3, y]
        anglesData = geometry[0, 4, y]

        # Loop over the number of columns
        for x in range(0, numCols):
            # If our score does not have sufficient probability, ignore it
            if scoresData[x] < confidence:
                continue

            # Compute the offset factor as our resulting feature maps will
            # be 4x smaller than the input image
            (offsetX, offsetY) = (x * 4.0, y * 4.0)

            # Extract the rotation angle for the prediction and then
            # compute the sin and cosine
            angle = anglesData[x]
            cos = np.cos(angle)
            sin = np.sin(angle)

            # Use the geometry volume to derive the width and height of
            # the bounding box
            h = xData0[x] + xData2[x]
            w = xData1[x] + xData3[x]

            # Compute both the starting and ending (x, y)-coordinates for
            # the text prediction bounding box
            endX = int(offsetX + (cos * xData1[x]) + (sin * xData2[x]))
            endY = int(offsetY - (sin * xData1[x]) + (cos * xData2[x]))
            startX = int(endX - w)
            startY = int(endY - h)

            # Add the bounding box coordinates and probability score to
            # our respective lists
            rects.append((startX, startY, endX, endY))
            confidences.append(scoresData[x])

    # Apply non-maxima suppression to suppress weak, overlapping bounding
    # boxes
    boxes = non_max_suppression(np.array(rects), probs=confidences)

    # Loop over the bounding boxes
    for (startX, startY, endX, endY) in boxes:
        # Scale the bounding box coordinates based on the respective
        # ratios
        startX = int(startX * rW)
        startY = int(startY * rH)
        endX = int(endX * rW)
        endY = int(endY * rH)

        # Draw the bounding box on the image
        cv2.rectangle(original, (startX, startY), (endX, endY), (36, 255, 12), 2)
    return original

# Convert to grayscale and Otsu's threshold
image = cv2.imread('1.png')
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)

# Remove non-text contours (curves, diagonals, circlar shapes)
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:
    area = cv2.contourArea(c)
    if area > 1500:
        cv2.drawContours(clean, [c], -1, 0, -1)
    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)

# Bitwise-and with original image to remove contours
filtered = cv2.bitwise_and(image, image, mask=clean)
filtered[clean==0] = (255,255,255)

# Perform EAST text detection
result = EAST_text_detector(image, filtered)

cv2.imshow('filtered', filtered)
cv2.imshow('result', result)
cv2.waitKey()

คำตอบที่สมบูรณ์มาก ต้องใช้ความพยายามกี่ชั่วโมง
karlphillip

ประมาณหนึ่งชั่วโมงและอีก 30 นาทีในการเขียนมันขึ้นมา
nathancy

ถึงวันนี้ฉันยังคงประหลาดใจที่มีคนจำนวนมากที่แสดงคำถาม CV ที่คล้ายกันมากในไม่กี่วัน ดูเหมือนว่าคนที่อยู่ในคลาสการประมวลผลภาพเดียวกันกำลังมองหาความช่วยเหลือในการทำการบ้านให้เสร็จหรือแค่มองหาคนที่จะทำการบ้านให้พวกเขา มันเป็น "เรื่องบังเอิญ" ที่แปลกประหลาดจริงๆ
karlphillip

2
@karlphillip บางทีคำถามนี้ดูคุ้นเคยเพราะ OP โพสต์ไว้เมื่อประมาณหนึ่งสัปดาห์ที่ผ่านมา เขาต้องการคำตอบ CTRL + C, CTRL + V ที่ครอบคลุมทุกกรณีของเขาออกมาจากกล่องดังนั้นฉันคิดว่าคุณอาจเห็นคำถามเดียวกันนี้อีกครั้งในอีกสองสัปดาห์!
eldesgraciado

3
@eldesgraciado ฉันเพิ่งรู้ว่า OP โพสต์คำถามที่คล้ายกันไม่กี่สัปดาห์ที่ผ่านมา ไม่ได้ตระหนักว่ามันเป็นบุคคลเดียวกันจนกระทั่งตอนนี้! ฉันยังสงสัยว่าทำไมคำถามดูคุ้น ๆ
nathancy

6

เพื่อเห็นแก่ความสะดวกสบายฉันต้องการที่จะเพิ่มแพคเกจkeras_ocr สามารถติดตั้งได้อย่างง่ายดายด้วย pip และตั้งอยู่บน CRAFT text detector ซึ่งค่อนข้างใหม่กว่า EAST detector ถ้าฉันไม่ผิด

ถัดจากการตรวจจับก็ทำ OCR บางส่วนด้วยเช่นกัน! ผลลัพธ์ดังที่เห็นด้านล่างเห็นว่านี่เป็นทางเลือกบางทีอาจจะนำไปใช้ง่ายกว่าคำตอบที่ยอมรับป้อนคำอธิบายรูปภาพที่นี่


สวัสดีผู้ชนะมันใช้ได้กับภาพอย่างน้อย 70% หรือไม่?
Pulkit Bhatnagar

คุณไม่ได้รวมป้ายกำกับไว้ในชุดข้อมูลของคุณ ดังนั้นฉันจึงไม่สามารถบอกคุณได้ว่ารูปภาพทำงานได้เป็นจำนวนเท่าใดหากฉันไม่มีวิธีการตรวจสอบว่าสามารถใช้งานได้หรือไม่โดยเปรียบเทียบกับป้ายกำกับ อย่างไรก็ตามมันเป็นแพ็คเกจ pip ดังนั้นควรจะง่ายพอที่คุณจะรันบนชุดข้อมูลของคุณและดูด้วยตัวคุณเอง :)
Victor Sonck

4

สิ่งที่คุณกำลังอธิบายดูเหมือนจะเป็น OCR ( การรู้จำอักขระด้วยแสง ) หนึ่งเครื่องยนต์ OCR ที่ฉันรู้จักคือtesseractแม้ว่าจะมีอันนี้จาก IBMและอื่น ๆ

เมื่อ YOLO ได้รับการฝึกอบรมสำหรับงานที่แตกต่างกันมากการใช้งานสำหรับการแปลข้อความจะต้องมีการฝึกอบรมใหม่ตั้งแต่เริ่มต้น หนึ่งสามารถลองใช้แพคเกจที่มีอยู่ (ปรับให้เข้ากับการตั้งค่าเฉพาะของคุณ) สำหรับความจริงภาคพื้นดิน (แม้ว่ามันจะคุ้มค่าที่จะจำไว้ว่าแบบจำลองโดยทั่วไปจะเป็นเพียงที่ดีที่สุดเท่าที่ความจริงพื้นดิน) หรืออาจสร้างข้อมูลสังเคราะห์สำหรับการฝึกอบรมได้ง่ายขึ้น (เช่นเพิ่มข้อความในตำแหน่งที่คุณเลือกไปยังภาพวาดที่มีอยู่แล้วฝึกให้เป็นภาษาท้องถิ่น)

อีกทางหนึ่งหากภาพเป้าหมายทั้งหมดของคุณมีโครงสร้างคล้ายกับด้านบนคุณสามารถลองสร้างความจริงภาคพื้นดินโดยใช้การวิเคราะห์พฤติกรรมแบบคลาสสิกที่คุณได้ทำไว้ข้างต้นเพื่อแยก / แยกส่วนสัญลักษณ์ตามด้วยการจำแนกโดยใช้ CNN ถ้าหยดที่กำหนดมีสัญลักษณ์

สำหรับกรณีที่คุณเลือกใช้ YOLO - มีการนำไปใช้งานใน python เช่นฉันเคยมีประสบการณ์กับเรื่องนี้มาบ้าง- ควรจะตรงไปตรงมาพอสมควรในการตั้งค่าการฝึกอบรมด้วยความจริงพื้นฐานของคุณเอง

ในที่สุดหากใช้ YOLO หรือ CNN ไม่ใช่เป้าหมายในตัวเอง แต่เป็นเพียงวิธีการแก้ปัญหาใด ๆ "ความจริงภาคพื้นดิน" ใด ๆ ข้างต้นสามารถใช้โดยตรงเป็นวิธีแก้ปัญหาและไม่ใช่สำหรับการฝึกอบรมแบบจำลอง

หวังว่าฉันเข้าใจคำถามของคุณถูกต้อง


หากคุณสามารถให้รหัสได้เช่นเดียวกันเนื่องจากคำถามนี้มีความโปรดปราน
Pulkit Bhatnagar

ภารกิจคือการได้รับข้อความ แต่ท้ายที่สุดฉันพยายามระบุตัวอักษรและตัวเลขทั้งหมดในนั้นจากนั้นใช้ OCR สำหรับการระบุครั้งเดียวเหมือนกัน
Pulkit Bhatnagar

ไม่มีสิ่งที่ฉันเสนอเป็นทางออกนอกกรอบจริงๆและรหัสอัลกอริทึมจะไม่สั้นหรือง่ายฉันคิดว่าดังนั้นฉันจะปล่อยให้มันอยู่ในระดับความคิด :-) ps ขอบคุณสำหรับการลงคะแนน!
Yuri Feldman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.