[PYTHON] Tool zum Erstellen von Trainingsdaten für die OpenCV-Objekterkennung

Um mit OpenCV Ihre eigene Objekterkennung zu erstellen, müssen Sie eine große Anzahl von Bildern als Trainingsdaten ausschneiden. Also habe ich zwei Tools erstellt.

screenshot.png

Schneiden Sie das Bild mit einer einfachen Benutzeroberfläche aus und generieren Sie eine Datei, die an opencv_createsamples.exe übergeben werden soll. Der Code ist unten. Da es durch Eilarbeit gemacht wurde, kann es viele Fehler geben.

wie benutzt man

Vorbereitung

Die Verzeichnisstruktur ist wie folgt.

Passendes Verzeichnis/
  ├── images/
  |├── Bilddatei 1.png
  |├── Bilddatei 2.jpg
  |      |     ︙
  |└── Bilddatei n.bmp
  ├── clipper.py
  ├── make_negative.py
  ├── opencv_createsamples.exe
  ├── opencv_traincascade.exe
└── (OpenCV-DLLs)

Verwendung des Support-Tools zum Zuschneiden von Bildern

Wenn Sie fertig sind, wird eine Datei pos.dat generiert, die in opencv_createsamples.exe eingegeben werden kann. Der Fortschritt wird in einer Datei gespeichert, sodass Sie die Arbeit fortsetzen können, wenn Sie fertig sind.

Verwendung des automatischen Erstellungswerkzeugs für negative Beispiele

Verwenden Sie einfach das Support-Tool zum Zuschneiden von Bildern und führen Sie dann "make_negative.py" aus. Dies erzeugt ein negatives Beispielbild im Verzeichnis "Negative" und eine Liste negativer Beispiele in "bg.dat".

Verwendung von opencv_createsamples.exe

Führen Sie vorerst einfach den folgenden Befehl aus.

opencv_createsamples.exe -info pos.dat -vec pos.vec

Verwendung von opencv_traincascade.exe

opencv_traincascade.exe -Datenausgabeverzeichnis-vec pos.vec -bg bg.dat

Code

Support-Tool zum Zuschneiden von Bildern

clipper.py


import cv2
import glob
import lzma
import os
import pickle


file_dir = './images'
state_file = './data'
output_file = './pos.dat'
display_size = 768
window_name = 'image_clip'

state = {}

mouse_position = [0, 0]
mouse_wheel    = 0
crop_origin    = None
crop_end       = None
selecting      = False
remove         = False

def load_state():
    global state

    if os.path.exists(state_file):
        with lzma.open(state_file, 'rb') as f:
            state = pickle.load(f)

def save_state():
    with lzma.open(state_file, 'wb') as f:
        pickle.dump(state, f)

def output():
    with open(output_file, 'w') as f:
        for file_name, rects in state.items():
            if len(rects) == 0:
                continue
            values  = [os.path.abspath(file_name), str(len(rects))]
            values += sum([[str(int(r)) for r in rect] for rect in rects], [])
            f.write(' '.join(values) + '\n')

def mouse_callback(event, x, y, flags, param):
    global mouse_position
    global mouse_wheel
    global crop_origin
    global selecting
    global crop_end
    global remove
    
    mouse_position = (x, y)

    if event == cv2.EVENT_LBUTTONDOWN:
        crop_origin = mouse_position
        selecting = True
    if event == cv2.EVENT_LBUTTONUP:
        crop_end = mouse_position
        selecting = False
    if event == cv2.EVENT_RBUTTONDOWN:
        remove = True
    if event == cv2.EVENT_MOUSEWHEEL:
        mouse_wheel = flags


def main():
    global state
    global mouse_wheel
    global crop_origin
    global crop_end
    global remove

    os.makedirs(file_dir, exist_ok=True)
    image_files = glob.glob(os.path.join(file_dir, '*'))

    if len(image_files) == 0:
        print('Bitte setzen Sie das Bild in Bilder')
        exit()

    load_state()

    cv2.namedWindow(window_name, cv2.WINDOW_AUTOSIZE)
    cv2.setMouseCallback(window_name, mouse_callback)

    image_counter = 0
    for i in range(len(image_files)):
        image_counter = i
        if image_files[image_counter] not in state.keys():
            break

    while True:
        if image_counter < 0:
            image_counter = image_counter + len(image_files)
        if image_counter >= len(image_files):
            image_counter = image_counter - len(image_files)

        save_state()

        image_file = image_files[image_counter]
        image = cv2.imread(image_file)
        scale = display_size / max(image.shape[0], image.shape[1])

        resized_image = cv2.resize(image, 
                                   dsize=None,
                                   fx=scale,
                                   fy=scale,
                                   interpolation=cv2.INTER_AREA)

        if image_file not in state:
            state[image_file] = []

        while True:
            display_image = resized_image.copy()

            for rect in state[image_file]:
                left_top = (int(rect[0] * scale), int(rect[1] * scale))
                right_bottom = (int((rect[0] + rect[2]) * scale), int((rect[1] + rect[3]) * scale))
                display_image = cv2.rectangle(display_image,
                                              left_top,
                                              right_bottom,
                                              (0, 0, 255),
                                              2)

            display_image = cv2.line(display_image,
                                     (mouse_position[0], 0),
                                     (mouse_position[0], display_image.shape[0]),
                                     (255, 0, 0),
                                     2)

            display_image = cv2.line(display_image,
                                     (0, mouse_position[1]),
                                     (display_image.shape[1], mouse_position[1]),
                                     (255, 0, 0),
                                     2)

            if selecting:
                display_image = cv2.rectangle(display_image,
                                              crop_origin,
                                              mouse_position,
                                              (0, 128, 255),
                                              2)

            cv2.imshow(window_name, display_image)

            key = cv2.waitKey(10) & 0xFF

            if crop_origin is not None and crop_end is not None:
                rect_x = min(mouse_position[0], crop_origin[0])
                rect_w = max(mouse_position[0], crop_origin[0]) - rect_x
                rect_y = min(mouse_position[1], crop_origin[1])
                rect_h = max(mouse_position[1], crop_origin[1]) - rect_y
                new_rect = [rect_x / scale, rect_y / scale, rect_w / scale, rect_h / scale]
                state[image_file].append(new_rect)

                crop_origin = None
                crop_end = None

            if remove:
                if len(state[image_file]) > 0:
                    state[image_file].pop(-1)
                remove = False

            if mouse_wheel != 0:
                image_counter += 1 if mouse_wheel > 0 else -1
                mouse_wheel = 0
                break

            if key == ord('q') or key == 27:
                return
                

if __name__ == '__main__':
    main()
    cv2.destroyAllWindows()
    save_state()
    output()

Negatives Beispiel für ein automatisches Erstellungswerkzeug

make_negative.py


import cv2
import glob
import lzma
import os
import pickle
import random


file_dir = './images'
output_dir = './negatives/'
output_list_file = './bg.dat'
state_file = './data'

def sample_start_point(width, height, positive_rects):
    for i in range(100):
        start_point = [random.randrange(width), random.randrange(height)]

        for rect in positive_rects:
            rect_left   = rect[0]
            rect_right  = rect[0] + rect[2]
            rect_top    = rect[1]
            rect_bottom = rect[1] + rect[3]

            if ((rect_left <= start_point[0] and rect_right  >= start_point[0]) and
                (rect_top  <= start_point[1] and rect_bottom >= start_point[1])):
                break
        else:
            return start_point
    
    return None

if __name__ == '__main__':
    if not os.path.exists(state_file):
        exit()

    with lzma.open(state_file, 'rb') as f:
        state = pickle.load(f)

    image_counter = 0
    
    for file_name, positive_rects in state.items():
        if len(positive_rects) == 0:
            continue

        image = cv2.imread(file_name)
        width = image.shape[1]
        height = image.shape[0]

        negative_rects = []

        for i in range(1000):
            start_point = sample_start_point(width, height, positive_rects)
            if start_point is None:
                continue
            
            negative_rect = [start_point[0], start_point[1], start_point[0], start_point[1]]

            min_x = 0
            max_x = width
            min_y = 0
            max_y = height

            directions = random.sample(['left', 'right', 'up', 'down'], 4)

            for direction in directions:
                for positive_rect in positive_rects:
                    positive_rect_left   = positive_rect[0]
                    positive_rect_right  = positive_rect[0] + positive_rect[2]
                    positive_rect_top    = positive_rect[1]
                    positive_rect_bottom = positive_rect[1] + positive_rect[3]

                    if not (negative_rect[1] > positive_rect_bottom or
                            negative_rect[3] < positive_rect_top):
                        if direction == 'left':
                            if negative_rect[0] > positive_rect_right:
                                min_x = max(min_x, positive_rect_right)
                        if direction == 'right':
                            if negative_rect[2] < positive_rect_left:
                                max_x = min(max_x, positive_rect_left)

                    if not (negative_rect[0] > positive_rect_right or
                            negative_rect[2] < positive_rect_left):
                        if direction == 'up':
                            if negative_rect[1] > positive_rect_bottom:
                                min_y = max(min_y, positive_rect_bottom)
                        if direction == 'down':
                            if negative_rect[3] < positive_rect_top:
                                max_y = min(max_y, positive_rect_top)
                        
                if direction == 'left':
                    negative_rect[0] = min_x
                if direction == 'right':
                    negative_rect[2] = max_x
                if direction == 'up':
                    negative_rect[1] = min_y
                if direction == 'down':
                    negative_rect[3] = max_y

            if negative_rect[0] == negative_rect[2] or negative_rect[1] == negative_rect[3]:
                continue

            negative_rects.append(tuple([int(x) for x in negative_rect]))
        
        negative_rects = set(negative_rects)
        
        for negative_rect in negative_rects:
            trimed_image = image[negative_rect[1]:negative_rect[3], negative_rect[0]:negative_rect[2], :]
            os.makedirs(output_dir, exist_ok=True)

            extention = os.path.splitext(file_name)[1]
            output_file_path = os.path.join(output_dir, '{}{}'.format(image_counter, extention))
            cv2.imwrite(output_file_path, trimed_image)
            image_counter += 1

    image_files = glob.glob(os.path.join(output_dir, '*'))
    with open(output_list_file, 'w') as f:
        for image_file in image_files:
            f.write('{}\n'.format(os.path.abspath(image_file)))

Recommended Posts

Tool zum Erstellen von Trainingsdaten für die OpenCV-Objekterkennung
Trainingsdaten erstellen
Machen Sie die Rotation der OpenCV-Objekterkennung unveränderlich
Beispiel für den Betrieb eines Cloud Pak for Data-Objekts in Python (WML-Client, project_lib)
Berechnung der mittleren IoU bei der Objekterkennung
Python-Visualisierungstool für die Datenanalyse
Windows → Linux Tipps zum Einbringen von Daten
Memorandum zur Erstellung von TFRecord-Dateien zur Objekterkennung
Erkennung von Zeitreihendatenanomalien für Anfänger
Hinweise zum Erstellen von Textformatierungswerkzeugen
Objekterkennung (Single Shot MultiBox Detector) Benutzeroberfläche zur Erstellung von XML-Dateien für Bilddaten
Vorlage zum Erstellen von Befehlszeilenanwendungen in Python
Ein Tool zum Erstellen symbolischer Links unter Windows
Ich habe versucht, Objekte mit Python und OpenCV zu erkennen
Zeigen Sie FX (Forex) Daten Candle Stick in Python an
Lassen Sie uns das arithmetische Lehrmittel "Jamaica" ❗️ Vol.02 "Hinweise zum Erstellen von Funktionen in Python" reproduzieren.