Wykorzystanie YOLO lub innych technik rozpoznawania obrazów do identyfikacji całego tekstu alfanumerycznego obecnego na obrazach

12

Mam schemat wielu obrazów, z których wszystkie zawierają etykiety jako znaki alfanumeryczne zamiast samej etykiety tekstowej. Chcę, aby mój model YOLO identyfikował wszystkie zawarte w nim cyfry i znaki alfanumeryczne.

Jak mogę wytrenować mój model YOLO, aby robił to samo. Zestaw danych można znaleźć tutaj. https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

Na przykład: zobacz ramki ograniczające. Chcę, aby YOLO wykrywał, gdziekolwiek tekst jest obecny. Jednak obecnie nie jest konieczne identyfikowanie tekstu w nim zawartego.

wprowadź opis zdjęcia tutaj

To samo należy zrobić dla tego typu obrazów wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Obrazy można pobrać tutaj

Właśnie tego próbowałem za pomocą opencv, ale to nie działa dla wszystkich obrazów w zestawie danych.

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

Czy jest jakiś model lub jakaś technika opencv lub jakiś wstępnie wyszkolony model, który może zrobić to samo dla mnie? Potrzebuję tylko obwiedni wokół wszystkich znaków alfanumerycznych obecnych na obrazach. Następnie muszę określić, co w nim jest. Jednak druga część nie jest obecnie ważna.

Pulkit Bhatnagar
źródło
Czy to odpowiada na twoje pytanie? Błąd OpenCV! _Src.empty () w funkcji „cvtColor”
Amit Yadav
to nie działa na wszystkie obrazy
Pulkit Bhatnagar

Odpowiedzi:

7

Możliwym podejściem jest użycie detektora głębokiego uczenia EAST (Efficient and Accurate Scene Text) opartego na pracy Zhou i wsp. Z 2017 roku, EAST: An Efficient and Accurate Scene Text Detector . Model został pierwotnie przeszkolony do wykrywania tekstu na naturalnych scenach, ale może być możliwe zastosowanie go na obrazach diagramów. EAST jest dość solidny i potrafi wykrywać rozmazany lub odblaskowy tekst. Oto zmodyfikowana wersja implementacji EAST Adriana Rosebrocka. Zamiast stosować detektor tekstu bezpośrednio na obrazie, możemy spróbować usunąć jak najwięcej obiektów nietekstowych z obrazu przed wykryciem tekstu. Chodzi o to, aby usunąć linie poziome, linie pionowe i kontury nietekstowe (krzywe, przekątne, kształty kołowe) przed zastosowaniem detekcji. Oto wyniki niektórych zdjęć:

Wprowadź ->kontury nietekstowe do usunięcia na zielono

Wynik

Inne obrazy

Wstępnie przeszkolony frozen_east_text_detection.pbmodel niezbędny do wykrycia tekstu można znaleźć tutaj . Chociaż model przechwytuje większość tekstu, wyniki nie są w 100% dokładne i od czasu do czasu mają fałszywie dodatnie wyniki, prawdopodobnie ze względu na sposób, w jaki był trenowany na naturalnych obrazach scen. Aby uzyskać dokładniejsze wyniki, prawdopodobnie będziesz musiał trenować własny model niestandardowy. Ale jeśli chcesz przyzwoitego, gotowego do użycia rozwiązania, powinno to działać. Zobacz post na blogu Adriana OpenCV Text Detection (detektor tekstu EAST), aby uzyskać pełniejsze wyjaśnienie detektora tekstu EAST.

Kod

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()
nathancy
źródło
Bardzo kompletna odpowiedź. Ile godzin wysiłku?
karlphillip
Około godziny i kolejne 30 minut, żeby to napisać
nathancy
Do dziś jestem zaskoczony liczbą osób, które pojawiają się z bardzo podobnymi pytaniami CV w ciągu kilku dni. Wygląda prawie na to, że faceci z tej samej klasy przetwarzania obrazu szukają pomocy w ukończeniu pracy domowej lub po prostu szukają kogoś, kto odrobiłby pracę domową. To naprawdę dziwny „zbieg okoliczności”.
karlphillip
2
@karlphillip Może to pytanie wygląda znajomo, ponieważ OP opublikował je około tygodnia temu. Pragnie odpowiedzi CTRL + C, CTRL + V, która obejmuje wszystkie jego przypadki od razu po wyjęciu z pudełka, więc myślę, że możesz zobaczyć to samo pytanie ponownie za kilka tygodni!
eldesgraciado
3
@eldesgraciado Właśnie zdałem sobie sprawę, że OP opublikował podobne pytanie kilka tygodni temu. Do tej pory nawet nie zdawałem sobie sprawy, że to ta sama osoba! Zastanawiałem się także, dlaczego pytanie wyglądało bardzo znajomo
nathancy
6

Dla wygody chciałbym dodać pakiet keras_ocr . Można go łatwo zainstalować za pomocą pipa i jest oparty na detektorze tekstu CRAFT, który jest nieco nowszy niż detektor EAST, jeśli się nie mylę.

Poza wykrywaniem robi już także trochę OCR! Wyniki przedstawiono poniżej, patrz na to jako alternatywę, być może łatwiejszą do wdrożenia, niż zaakceptowana odpowiedź.wprowadź opis zdjęcia tutaj

Victor Sonck
źródło
Cześć zwycięzco, czy to działa przez co najmniej 70% moich zdjęć?
Pulkit Bhatnagar
Nie umieściłeś etykiet w swoim zestawie danych. Tak więc nie mogę ci powiedzieć, na ile% obrazów działa, jeśli nie mam sposobu, aby sprawdzić, czy zadziałało, porównując je z etykietą. Jest to jednak pakiet pip, więc powinno być wystarczająco łatwe, abyś uruchomił go w swoim zestawie danych i przekonał się sam :)
Victor Sonck
4

To, co opisujesz, wydaje się być OCR ( optyczne rozpoznawanie znaków ). Jeden silnik OCR, o którym wiem, to tesseract , chociaż jest też ten od IBM i innych.

Ponieważ YOLO początkowo było szkolone do bardzo różnych zadań, używanie go do lokalizowania tekstu prawdopodobnie wymagać będzie ponownego przeszkolenia go od zera. Można spróbować użyć istniejących pakietów (dostosowanych do twojego konkretnego ustawienia) dla prawdziwej prawdy (chociaż warto pamiętać, że model byłby co najwyżej tak dobry, jak prawdziwa prawda). Lub, być może łatwiej, wygeneruj syntetyczne dane do szkolenia (tj. Dodaj tekst w wybranych pozycjach do istniejących rysunków, a następnie trenuj, aby go zlokalizować).

Alternatywnie, jeśli wszystkie obrazy docelowe mają strukturę podobną do powyższej, można spróbować stworzyć prawdę podstawową przy użyciu klasycznej heurystyki CV, tak jak to zrobiono powyżej, aby oddzielić / segmentować symbole, a następnie klasyfikację przy użyciu CNN przeszkolonego na MNIST lub podobnym w celu ustalenia jeśli dana kropelka zawiera symbol.

W przypadku, gdy zdecydujesz się na YOLO - istnieją już implementacje w Pythonie, np. Miałem trochę doświadczenia z tym - powinno być dość proste, aby skonfigurować szkolenie z własną prawdą.

Wreszcie, jeśli użycie YOLO lub CNN nie jest celem samym w sobie, a jedynie rozwiązaniem, każda z powyższych „podstawowych prawd” może być wykorzystana bezpośrednio jako rozwiązanie, a nie do szkolenia modelu.

Mam nadzieję, że poprawnie zrozumiałem twoje pytanie

Jurij Feldman
źródło
Jeśli możesz podać kod dla tego samego, ponieważ to pytanie zawiera nagrodę
Pulkit Bhatnagar
zadaniem jest ostatecznie uzyskać tekst, ale najpierw próbuję zidentyfikować wszystkie znaki alfanumeryczne, a następnie używam OCR dla tego samego raz zidentyfikowanego
Pulkit Bhatnagar
Żadne z tego, co zaproponowałem, nie jest naprawdę gotowym rozwiązaniem, a kod algorytmu nie byłby krótki ani prosty, jak sądzę, więc zostawię go na poziomie pomysłów :-). ps dzięki za głos!
Yuri Feldman