[PYTHON] Erstellen eines Bildbeschneidungswerkzeugs mit OpenCV, Vorsichtsmaßnahmen für die Projektionskonvertierung (nur wenig)

Einführung

Ich habe mit OpenCV ein Tool zum Zuschneiden von Bildern erstellt. Schneiden Sie das Objekt aus, projizieren Sie es, formen Sie es und speichern Sie jedes Objekt.

Es wird angenommen, dass das Objekt ein Film mit dem folgenden Bild ist. Auch wenn dies nicht der Fall ist, kann es meiner Meinung nach beim Extrahieren eines Rechtecks verwendet werden.

<img width="200", alt="sample.jpg ", src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/f532ad11-9c6d-e7df-844b-a30e6a2851ea.jpeg ">       <img width="100", alt="sample2.png ", src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/528427a2-9619-e2d1-b189-8a513cf87c64.jpeg ">

Da ich während der Erstellung auf die Projektionskonvertierung gestoßen bin, werden Inhalt und Lösung am Ende beschrieben.

Mac OS python 3.8.5

opencv-python 4.4.0.44 numpy 1.19.2 tqdm 4.50.2

python


pip install opencv-python
pip install tqdm

<img width="350", alt="射影変換後.jpeg ", src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/5b69d421-8b17-d09c-b6d4-e7843473ccb2.jpeg ">

Ich importiere tqdm, um den Umgebungsfortschrittsbalken zu verwenden. Projektionskonvertierung Dies ist ein Vorgang, bei dem das Objekt so korrigiert wird, als ob es von vorne aufgenommen wurde. Ich habe es verwendet, weil ich wollte, dass das zugeschnittene Bild im rechten Winkel ist. Referenzartikel zur Projektionskonvertierung Die Bildkorrektur ist mit der Python / OpenCV-Projektionskonvertierung einfach! | WATLAB -Python, Signalverarbeitung, AI- Versuchen Sie die Projektionsprojektionskonvertierung von Bildern mit OpenCV mit Python --Qiita

Voller Text

Ich verweise auf die folgenden Artikel, um japanische Pfade mit dem Lesen von Bildern kompatibel zu machen. Informationen zum Umgang mit Problemen beim Umgang mit Dateipfaden einschließlich Japanisch in Python OpenCV cv2.imread und cv2.imwrite --Qiita

Die Betriebsmethode ist

  1. Erstellen Sie einen Ressourcenordner in derselben Hierarchie wie das Skript
  2. Legen Sie das Bild, das Sie verarbeiten möchten, in den Ressourcenordner (mehrere möglich, JPG, JPEG oder PNG).
  3. Führen Sie aus

Das Ergebnis wird als "./Ergebnisordner / Bilddateiname / Bilddateiname_0, ..." gespeichert. Wenn im Ergebnis derselbe Ordner wie der Dateiname vorhanden ist, wird der Vorgang übersprungen.

Wenn Sie es nicht gut schneiden können, ändern Sie den Schwellenwert oder den Mindestbereich. [Bildschwellenwertverarbeitung - OpenCV-Python-Tutorials 1-Dokumentation](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_thresholding/py_thresholding. html) [Funktionen für Bereiche (Konturen) - Dokumentation zu OpenCV-Python-Tutorials 1](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_contours/py_contour_features/ py_contour_features.html)

python


import os, shutil, time
from pathlib import Path
import cv2
import numpy as np
from tqdm import tqdm

thresh_value = 240  #Grenzwert beim Binärisieren,Wenn der Pixelwert kleiner als dieser Wert ist, machen Sie ihn weiß(max:255)
minimum_area = 10000  #Verarbeiten Sie keine Objekte, die kleiner als diese sind, wenn die Kontur erfasst wird(Für den Fall, dass andere Punkte als das Zielobjekt erkannt werden)


def imread(filename, flags=cv2.IMREAD_COLOR, dtype=np.uint8):
    try:
        n = np.fromfile(filename, dtype)
        img = cv2.imdecode(n, flags)
        return img
    except Exception as e:
        print(e)
        return None


def imwrite(filename, img, params=None):
    try:
        ext = os.path.splitext(filename)[1]
        result, n = cv2.imencode(ext, img, params)

        if result:
            with open(filename, mode='w+b') as f:
                n.tofile(f)
            return True
        else:
            return False
    except Exception as e:
        print(e)
        return False


def calculate_width_height(pts, add):
    """
Breite der erkannten Form,Finden Sie die Höhe mit drei Quadraten
Wenn es zu schräg ist, ändert sich die Form, wenn die Projektion konvertiert wird.

    :parameter
    ------------
    pts: numpy.ndarray
Koordinaten von 4 Punkten der extrahierten Form, shape=(4, 1, 2)
    add: int
Korrektur, da die Koordinaten des Startpunkts je nach Form unterschiedlich sind

    :return
    ------------
    width: int
Berechnete Breite
    height: int
Berechnete Höhe
    """
    top_left_cood = pts[0 + add][0]
    bottom_left_cood = pts[1 + add][0]
    bottom_right_cood = pts[2 + add][0]

    width = np.int(np.linalg.norm(bottom_left_cood - bottom_right_cood))
    height = np.int(np.linalg.norm(top_left_cood - bottom_left_cood))

    return width, height


def img_cut():
    """
Bilder im Ressourcenordner(jpg, png)Um den Umriss des Objekts zu erhalten
Schneiden Sie das Objekt aus, projizieren Sie es und bringen Sie es nach vorne

    1.Ordner,Datei lesen
    2.Bild lesen,Binarisierung(Schwarz und weiß)wird bearbeitet
    3.Kontur bekommen
    4.Projektionskonvertierung
    5.Ausgabe
    6.Verschieben Sie die Ressourcendatei zum Ergebnis

    :return: None
    """

    # 1.Ordner,Datei lesen
    resource_folder = Path(r'./resource')
    result_folder = Path(r'./result')
    #Erstellen, wenn der Ergebnisordner nicht vorhanden ist
    if not result_folder.exists():
        result_folder.mkdir()

    img_list1 = list(resource_folder.glob('*.jpg'))  #Pfadliste der JPG-Dateien im Ordner
    img_list2 = list(resource_folder.glob('*.jpeg'))
    img_list3 = list(resource_folder.glob('*.png'))
    img_list = img_list1 + img_list2 + img_list3

    for img in img_list:
        img_name, img_suffix = img.stem, img.suffix  #Holen Sie sich den Bildnamen und die Erweiterung

        #Erstellen Sie einen Ordner mit dem Namen der Bilddatei im Ergebnisordner,Überspringen Sie die Konvertierung, wenn derselbe Ordner bereits vorhanden ist
        result_img_folder = Path(r'./result/{}'.format(img_name))
        if not result_img_folder.exists():
            result_img_folder.mkdir()
        else:
            print('{}Kann nicht konvertiert werden, da ein Ordner mit demselben Namen wie im Ergebnis vorhanden ist'.format(img_name))
            continue

        # 2.Bild lesen,Binarisierung(Schwarz und weiß)wird bearbeitet
        read_img = imread(str(img))
        gray_img = cv2.cvtColor(read_img, cv2.COLOR_BGR2GRAY)
        ret, thresh_img = cv2.threshold(gray_img, thresh_value, 255, cv2.THRESH_BINARY_INV)

        # --------------------------------------------
        #Zur binärisierten Bildbestätigung
        # cv2.namedWindow('final', cv2.WINDOW_NORMAL)
        # cv2.imshow('final', thresh_img)
        # cv2.waitKey(0)
        # cv2.destroyAllWindows()
        # --------------------------------------------

        # 3.Kontur bekommen
        # cv2.RETR_EXTERNAL:Extrahieren Sie nur die äußerste Kontur aus den erkannten Konturen->Konturen ignorieren, auch wenn sie sich innerhalb der Kontur befinden
        # cv2.CHAIN_APPROX_SIMPLE:Holen Sie sich nur 4 Ecken, nicht die Kanten der Kontur
        contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        process_cnt = []  #Liste der Konturen, die tatsächlich geschnitten werden sollen
        for cnt in contours:
            if cv2.contourArea(cnt) < minimum_area:  #Schneiden Sie keine Gegenstände, deren Konturfläche zu klein ist
                continue
            process_cnt.append(cnt)

        num = 0
        for p_cnt in tqdm(process_cnt[::-1], desc='{}'.format(img_name)):  #Aus irgendeinem Grund beginnt der Vorgang mit dem unteren Bild. Schneiden Sie ihn also in umgekehrter Reihenfolge.(Oben)Fix von
            x, y, w, h = cv2.boundingRect(p_cnt)  #Oben links von Kontur x,y-Koordinate&Breite,Holen Sie sich Höhe
            img_half_width = x + w / 2

            # cv2.arcLength:Umfangslänge der Kontur,True bedeutet, dass die Kontur geschlossen ist
            # cv2.approPolyDP:Ungefähre erkannte Form
            epsilon = 0.1 * cv2.arcLength(p_cnt, True)
            approx = cv2.approxPolyDP(p_cnt, epsilon, True)
            try:
                # 4.Projektionskonvertierung
                pts1 = np.float32(approx)
                if pts1[0][0][0] < img_half_width:  #Wenn der Startpunkt der in pts gespeicherten Koordinaten oben links liegt
                    width, height = calculate_width_height(pts1, 0)
                    pts2 = np.float32([[0, 0], [0, height], [width, height], [width, 0]])
                else:
                    width, height = calculate_width_height(pts1, 1)
                    pts2 = np.float32([[width, 0], [0, 0], [0, height], [width, height]])
            except IndexError:
                continue
            M = cv2.getPerspectiveTransform(pts1, pts2)
            dst = cv2.warpPerspective(read_img, M, (width, height))

            result_img_name = img_name + '_{}.{}'.format(num, img_suffix)
            imwrite(str(result_img_folder) + '/' + result_img_name, dst)

            num += 1
        # 6.Verschieben Sie die Ressourcendatei zum Ergebnis
        shutil.move(str(img), result_img_folder)


if __name__ == '__main__':
    img_cut()
    print('Ende der Ausführung')
    time.sleep(3)

Einzelheiten

python


# cv2.arcLength:Umfangslänge der Kontur,True bedeutet, dass die Kontur geschlossen ist
# cv2.approPolyDP:Ungefähre erkannte Form
epsilon = 0.1 * cv2.arcLength(p_cnt, True)
approx = cv2.approxPolyDP(p_cnt, epsilon, True)
try:
    # 4.Projektionskonvertierung
    pts1 = np.float32(approx)

Koordinateninformationen von 4 Ecken des Objekts werden in Punkt 1 gespeichert. ex) [[[6181. 598.]]

[[ 145. 656.]]

[[ 135. 3499.]]

[[6210. 3363.]]]

Wenn Sie diese vier Punkte an die Ecken des Bildes bringen, sieht das Bild so aus, als wäre es von vorne gesehen worden.

python


if pts1[0][0][0] < img_half_width:  #Wenn der Startpunkt der in pts gespeicherten Koordinaten oben links liegt
    width, height = calculate_width_height(pts1, 0)
    pts2 = np.float32([[0, 0], [0, height], [width, height], [width, 0]])
else:
    width, height = calculate_width_height(pts1, 1)
    pts2 = np.float32([[width, 0], [0, 0], [0, height], [width, height]])

Es bestimmt, wo der Startpunkt von pts1 liegt. Da die vier gespeicherten Koordinatenpunkte vom oberen Punkt aus gegen den Uhrzeigersinn gespeichert werden (die y-Achse ist klein), ändert sich der Startpunkt von pts1 abhängig von der Neigung des Bildes. Es wird beurteilt und so eingestellt, dass es den Koordinaten pts2 nach der Projektionskonvertierung entspricht. <img width="400", alt="pts座標.png ", src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/f6c5e9a7-d49c-45ca-191e-05efa952f14a.png ">

Die Beurteilungsmethode basiert darauf, ob die x-Koordinate des Startpunkts links oder rechts von der Bildmitte liegt.

Da ich die Form des Objekts so weit wie möglich beibehalten möchte, habe ich die Breite und Höhe anhand von drei Quadraten berechnet und ein zugeschnittenes Bild mit dieser Größe ausgegeben.

Am Ende

Ich wusste nicht, dass sich der Startpunkt von pts1 ändern würde, und zuerst gab ich ein bedeutungsloses Bild aus. Ich konnte es nicht leicht finden, selbst wenn ich im Internet suchte, und am Ende befand ich mich in einem Zustand, in dem ich es schließlich herausfand, indem ich das Bild anstarrte. Ich hoffe, es wird hilfreich sein, wenn es Menschen gibt, die ähnlich in Schwierigkeiten sind.

Referenzseite

Lernprogramm [Bildschwellenwertverarbeitung - OpenCV-Python-Tutorials 1-Dokumentation](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_thresholding/py_thresholding. html) Gliederung: Erster Schritt - Dokumentation zu OpenCV-Python-Tutorials 1 ) [Funktionen für Bereiche (Konturen) - Dokumentation zu OpenCV-Python-Tutorials 1](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_contours/py_contour_features/ py_contour_features.html)

OpenCV-Referenz (Kontur, Bildausschnitt, Projektionskonvertierung) Grundlagen der Konturerkennung aus Bildern mit OpenCV (per findContours-Funktion) | North Building Tech.com [Objekte mit contours-python, image, opencv-contour abrufen und zuschneiden](https://living-sun.com/ja/python/725302-getting-and-cropping-object-in] -images-using-contours-python-image-opencv-contour.html) Versuchen Sie die Projektionsprojektionskonvertierung von Bildern mit OpenCV mit Python --Qiita

Unterstützung für das Lesen von OpenCV-Japanpässen Informationen zum Umgang mit Problemen beim Umgang mit Dateipfaden einschließlich Japanisch in Python OpenCV cv2.imread und cv2.imwrite --Qiita

Berechnungsreferenz für drei Quadrate [Euklidische Entfernung berechnen](http://harmonizedai.com/article/%E3%83%A6%E3%83%BC%E3%82%AF%E3%83%AA%E3%83%83%E3% 83% 89% E8% B7% 9D% E9% 9B% A2% E3% 82% 92% E6% B1% 82% E3% 82% 81% E3% 82% 8B /)

Fortschrittsbalkenreferenz Fortschrittsbalken mit tqdm --Qiita anzeigen

Recommended Posts

Erstellen eines Bildbeschneidungswerkzeugs mit OpenCV, Vorsichtsmaßnahmen für die Projektionskonvertierung (nur wenig)
Bildverarbeitung mit Lambda + OpenCV (graue Bilderzeugung)
GUI-Bildschneidewerkzeug mit Python + Tkinter
Probleme beim Erstellen eines CSV-JSON-Konvertierungstools mit Python
Erstellen Sie mit python wxpython + openCV ein einfaches Videoanalysetool
[Python] Zugreifen auf und Zuschneiden von Bildpixeln mit OpenCV (für Anfänger)