[PYTHON] Echtzeitklassifizierung mehrerer Objekte in Kamerabildern mit tiefem Erlernen von Raspberry Pi 3 B + & PyTorch

Holen Sie sich Razpai 3B + und Picamera für Universitätsklassen. Da ich Freizeit habe, habe ich beschlossen, Razpai mithilfe von Deep Learning klassifizieren zu lassen. Anstatt die im Voraus aufgenommenen Fotos zu klassifizieren, werden die Objekte im Echtzeitbild von Picamera klassifiziert und auf schöne Weise angezeigt.

Es mag auf Studentenebene sein, aber ich hoffe, es wird teilweise hilfreich sein.

Was ich mir vorgestellt habe

Ich habe beschlossen, in Razpie eine "Funktion zu erstellen, die ** in Echtzeit klassifiziert und anzeigt **, wenn mehrere persönliche Gegenstände im festen Gesichtsfeld der Picamera platziert werden".

Insbesondere wird das Objekt durch ** Hintergrunddifferenz ** (eine Methode zum Extrahieren des geänderten Teils aus dem Hintergrundbild) extrahiert, und tiefes Lernen wird durch ** PyTorch [PyTorch] ** (ähnlich wie Keras, TensorFlow) durchgeführt. Wir werden eine Politik der Klassifizierung nach verfolgen.

** (* YOLO, SSD usw. werden nicht behandelt!) **

Also habe ich es im nächsten Schritt implementiert.

Raspeye ist langsam, also habe ich ** auf meinem eigenen PC gelernt und es auf Raspai anhand der erhaltenen Parameterdatei ** klassifiziert. Also habe ich PyTorch sowohl auf dem PC als auch auf Raspberry installiert.

Das Folgende ist eine Reihe von Prozessen. Notieren Sie sich die Bereiche, in denen Sie mit dem Symbol ** [⚠Hinweis] ** zu kämpfen hatten.

Erste Vorbereitung

Bereiten Sie die Ausführungsumgebung auf dem PC und Raspeye vor.

Ausführungsumgebung

Die Versionen desselben Pakets unterscheiden sich für PC und Raspeye, aber keine Sorge. Ihr eigener PC dient zum Lernen.

Eigener PC (Windows 10)

** * Torchvision ** ist ein Set mit PyTorch und eine Bibliothek, die für die ** Bildvorverarbeitung und Datensatzerstellung ** verwendet wird.

Raspberry Pi 3 Model B+ (Raspbian Stretch)

Ich habe ** Raspberry Pi Camera Module V2 ** als Kamera zum Anschließen an den Raspberry Pi verwendet. Ich habe auch VNC Viewer auf meinen PC installiert und Raspeye mit ** SSH-Verbindung ** betrieben.

Wie zu bauen

Stellen Sie die obige Version des Pakets auf jeden Computer. Ich werde die Details weglassen, aber ich habe auf den Link-Site-Bereich verwiesen.

PyTorch / Torchvision

Installieren Sie auf ** PC **, indem Sie die Umgebung unter PyTorch Official auswählen.

** [⚠Hinweis] ** Die GPU kann nur verwendet werden, wenn sie von NVIDIA erstellt wurde. Wenn Sie also "Intel" haben, wählen Sie ** CUDA → Keine ** (normalerweise CPU verwenden).

Zu ** Raspberry Pi **, "PyTorch v1.3.0 in Raspberry Pi 3 einfügen" und "Quellcode des PyTorch Deep Learning Framework in Raspberry Pi" Erstellen aus " ist eine gute Referenz für das Erstellen.

** [⚠Hinweis] ** Geben Sie die Version als git clone ~~~ -b v1.3.0 usw. an. ** [⚠Hinweis] ** In PyTorch 1.4.0 ist "schwerwiegender Fehler: immintrin.h" nicht vorhanden, und der Build wurde bei etwa 80% gestoppt. Ein Geheimnis. (2020/3/20)

OpenCV

"OpenCV 3 so einfach wie möglich auf Raspberry Pi + Python 3 installieren" usw. und auf ** Raspberry ** installieren.

Beide brauchen ein paar Stunden, um ...

Sofort implementiert

Nach vielen Versuchen und Irrtümern habe ich gerade ein Python-Skript erstellt.

* Schritt 1 *: Erstellen Sie Trainingsdaten für Bilder, die Sie selbst lernen möchten

Ich habe die Bilddaten meiner persönlichen Gegenstände erstellt, um sie zum Lernen zu verwenden. Es wird angenommen, dass der Picamera in die Raspeltorte eingeführt und ** fixiert ist, damit sich der Picamera nicht bewegt **.

Programm zum Erstellen

Nachdem Sie den Bildschirm mit der Taste "r" gedreht haben, drücken Sie "p", um den Hintergrund aufzunehmen, ohne etwas aufzunehmen. Wenn Sie die persönlichen Gegenstände, die Sie aufnehmen möchten, platzieren und erneut mit "p" aufnehmen, wird der Hintergrundunterschied hergestellt und das Foto im ** grünen Rahmen ** gespeichert.

Dieses Mal werde ich die drei Kategorien ** "bestimmtes Telefon", "Uhr" und "Brieftasche" ** klassifizieren, also mache ich nur diese drei Bilder.

take_photo.py


# coding: utf-8
import cv2
from datetime import datetime
import picamera
import picamera.array

MIN_LEN = 50  #Mindestlänge einer Seite des Objekterkennungsrahmens
GRAY_THR = 20  #Konzentrationsänderungsschwelle
CUT_MODE = True  # True:Schneiden Sie das erkannte Objekt aus und speichern Sie es, False:Speichern Sie das gesamte Bild wie es ist


def imshow_rect(img, contour, minlen=0):
"""
Schließen Sie alle Objekterkennungspunkte im erfassten Bild mit einem quadratischen Rahmen ein
Streit:
    img:Kamerabild
    contour:Kontur
    minlen:Schwellenwert für die Erkennungsgröße (ausgenommen Bereiche, in denen eine Seite des Rahmens kürzer ist)
"""
    for pt in contour:
        x, y, w, h = cv2.boundingRect(pt)
        if w < minlen and h < minlen: continue
        cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
    cv2.imshow('Preview', img)


def save_cutimg(img, contour, minlen=0):
"""
Schneiden Sie alle Objekterkennungspunkte im aufgenommenen Bild aus und speichern Sie sie
Streit:
Das gleiche wie oben
"""
    #Holen Sie sich Datum und Uhrzeit und verwenden Sie sie für den Dateinamen
    dt = datetime.now()
    f_name = '{}.jpg'.format(dt.strftime('%y%m%d%H%M%S'))
    imgs_cut = []
    for pt in contour:
        x, y, w, h = cv2.boundingRect(pt)
        if w < minlen and h < minlen: continue
        imgs_cut.append(img[y:y+h, x:x+w])

    #Schneiden Sie das Objekt aus und speichern Sie es
    if not imgs_cut: return -1
    if len(imgs_cut) > 1:
        for i in range(len(imgs_cut)):
            cv2.imwrite(f_name[:-4]+'_'+str(i+1)+f_name[-4:], imgs_cut[i])
    else:
        cv2.imwrite(f_name, imgs_cut[0])
    return len(imgs_cut)


def save_img(img):
"""
Speichern Sie das aufgenommene Bild so wie es ist
Streit:
Das gleiche wie oben
"""
    dt = datetime.now()
    fname = '{}.jpg'.format(dt.strftime('%y%m%d%H%M%S'))
    cv2.imwrite(fname, img)


def take_photo():
"""
Hintergrundaufnahmen->Objektfotografie,sparen
Tasteneingabe: 
    "p":machen Sie ein Foto
    "q":Halt
    "r":Drehen Sie den Bildschirm (beim Aufnehmen des Hintergrunds)
    "i":Beginnen Sie von vorne (beim Aufnehmen eines Objekts)
"""
    cnt = 0
    #Starten Sie picamera
    with picamera.PiCamera() as camera:
        camera.resolution = (480, 480)  #Auflösung
        camera.rotation = 0  #Kameradrehwinkel(Jedes Mal)
        #Starten Sie das Streaming
        with picamera.array.PiRGBArray(camera) as stream:
            print('Set background ... ', end='', flush=True)
            #Schießen Sie zuerst den Hintergrund
            while True:
                #Streaming-Bilder abrufen und anzeigen
                camera.capture(stream, 'bgr', use_video_port=True)
                cv2.imshow('Preview', stream.array)

                wkey = cv2.waitKey(5) & 0xFF  #Tastatureingang

                stream.seek(0)  #2 Zauber, um neue zu erfassen
                stream.truncate()

                if wkey == ord('q'):
                    cv2.destroyAllWindows()
                    return print()
                elif wkey == ord('r'):
                    camera.rotation += 90
                elif wkey == ord('p'):
                    camera.exposure_mode = 'off'  #Weißabgleich behoben
                    save_img(stream.array)
                    #Graustufen und als Hintergrundbild festgelegt
                    back_gray = cv2.cvtColor(stream.array, 
                                             cv2.COLOR_BGR2GRAY)
                    print('done')
                    break

            #Nach dem Einstellen des Hintergrunds,Aufnehmen von Objekten ohne Bewegen der Kamera
            print('Take photos!')
            while True:
                camera.capture(stream, 'bgr', use_video_port=True)
                #Graustufen des aktuellen Rahmens
                stream_gray = cv2.cvtColor(stream.array, 
                                           cv2.COLOR_BGR2GRAY)

                #Berechnen Sie den absoluten Wert der Differenz und binärisieren Sie ihn,Maskenherstellung
                diff = cv2.absdiff(stream_gray, back_gray)
                mask = cv2.threshold(diff, GRAY_THR, 255, 
                                     cv2.THRESH_BINARY)[1]
                cv2.imshow('mask', mask)

                #Kontur zur Objekterkennung,Maskenherstellung
                contour = cv2.findContours(mask,
                                           cv2.RETR_EXTERNAL,
                                           cv2.CHAIN_APPROX_SIMPLE)[1]

                #Alle erkannten Objekte werden in einem Quadrat angezeigt
                stream_arr = stream.array.copy()
                imshow_rect(stream_arr, contour, MIN_LEN)

                wkey = cv2.waitKey(5) & 0xFF

                stream.seek(0)
                stream.truncate()

                if wkey == ord('q'):
                    cv2.destroyAllWindows()
                    return
                elif wkey == ord('i'):
                    break
                elif wkey == ord('p'):
                    if CUT_MODE:
                        num = save_cutimg(stream.array, contour, MIN_LEN)
                        if num > 0:
                            cnt += num
                            print('  Captured: {} (sum: {})'.format(num, cnt))
                    else:
                        save_img(stream.array)
                        cnt += 1
                        print('  Captured: 1 (sum: {})'.format(cnt))

    print('Initialized')
    take_photo()


if __name__ == '__main__':
    take_photo()

Lauf

Ich mache nur Fotos. Das zugeschnittene Bild für jeden grünen Rahmen wird wie folgt gespeichert.

200328174638.jpg 200328174642_1.jpg 200328174642_2.jpg 200328174642_3.jpg

** [⚠Hinweis] Wenn zu wenige Fotos vorhanden sind, wird es nicht gut gelernt. ** **. Ich habe mehr als 50 Fotos für jede Klasse für Trainingsdaten gemacht, aber ich frage mich, ob es noch wenige gibt ... Während des Lernens werden vorerst verschiedene Geräusche hinzugefügt, und die Datenmenge nimmt zu.

Legen Sie die Fotos in einen Ordner und verschieben Sie sie mit Slack oder etwas anderem auf Ihren PC. (Semi-Analog) Und speichern Sie jedes persönliche Foto in der Ordnerstruktur unten **. ** **.

image_data
├─train
│  ├─phone
│  │       191227013419.jpg
│  │       191227013424.jpg
│  │              :
│  ├─wallet
│  │       191227013300.jpg
│  │       191227013308.jpg
│  │              :
│  └─watch
│          191227013345.jpg
│          191227013351.jpg
|                 :
└─val
    ├─phone
    │       191227013441.jpg
    │       191227013448.jpg
    |              :
    ├─wallet
    │       191227013323.jpg
    │       191227013327.jpg
    |              :
    └─watch
            191227013355.jpg
            191227013400.jpg
                   :

* Schritt 2 *: Deep Learning mit PyTorch auf dem PC

Bauen Sie ein Netzwerk auf und trainieren Sie mit dem obigen Bild.

Programm zum Erstellen

Bei der Ausführung wird das Bild aus dem vorherigen Ordner gelesen und das Lernen gestartet, und die Fortschrittsdatei, das Verlust- und Genauigkeitsübergangsdiagramm und die endgültige Parameterdatei werden ausgegeben.

Bei der Erstellung habe ich auf "PyTorch Neural Network Implementation Handbook" (Hidewa-System) verwiesen.

Selbst wenn Sie mit "Strg + C" unterbrechen, wird der Lernfortschritt bis zu diesem Punkt als ** "train_process.ckpt" ** gespeichert und Sie können ab der nächsten Ausführung weiter lernen. Es ist in Ordnung, die Hyperparameter unterwegs zu ändern.

Übrigens erstellt der ** Bildordner ** von torchvsion einen Datensatz mit dem Ordnernamen, der die Fotos als Klassennamen enthält. Einfach! !! Die Fotos im Zugordner werden zum Lernen verwendet, und die Fotos im Val-Ordner werden zur Auswertung verwendet.

train_net.py


# coding: utf-8
import os
import re
import torch.nn as nn
import torch.optim as optim
import torch.utils
from torchvision import datasets, transforms
import matplotlib.pyplot as plt

DATA_DIR = 'image_data'  #Name des Bildordners
CKPT_PROCESS = 'train_process.ckpt'  #Lernfortschritt Dateinamen speichern
CKPT_NET = 'trained_net.ckpt'  #Name der gelernten Parameterdatei
NUM_CLASSES = 3  #Anzahl der Klassen
NUM_EPOCHS = 100  #Anzahl des Lernens

#Hyperparameter, die sich häufig ändern
LEARNING_RATE = 0.001  #Lernrate
MOMENTUM = 0.5  #Trägheit

checkpoint = {}  #Variablen zum Speichern des Fortschritts


#Definition der Bilddatenkonvertierung (sperrig)
#Mit der Größe von Resize,Bezogen auf die erste lineare Eingabegröße des Klassifikators
data_transforms = transforms.Compose([
    transforms.Resize((112, 112)),  #Größe ändern
    transforms.RandomRotation(30),  #Nach dem Zufallsprinzip drehen
    transforms.Grayscale(),  #Binarisierung
    transforms.ToTensor(),  #Tensolisierung
    transforms.Normalize(mean=[0.5], std=[0.5])  #Normalisierung (Zahlen sind Text)
])

val_transforms = transforms.Compose([
    transforms.Resize((112, 112)),
    transforms.Grayscale(),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5], std=[0.5])
])

#Datensatzerstellung
train_dataset = datasets.ImageFolder(
    root=os.path.join(DATA_DIR, 'train'),
    transform=train_transforms
)

val_dataset = datasets.ImageFolder(
    root=os.path.join(DATA_DIR, 'val'),
    transform=val_transforms
)

#Holen Sie sich Mini-Batch
train_loader = torch.utils.data.DataLoader(
    dataset=train_dataset,
    batch_size=10,  #Chargengröße zum Zeitpunkt des Lernens
    shuffle=True  #Mische Trainingsdaten
)

val_loader = torch.utils.data.DataLoader(
    dataset=val_dataset,
    batch_size=10,
    shuffle=True
)


class NeuralNet(nn.Module):
    """Netzwerkdefinition. nn.Modulvererbung"""
    def __init__(self, num_classes):
        super(NeuralNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 8, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(8, 16, kernel_size=5, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
        )

        self.classifier = nn.Sequential(
            nn.Dropout(p=0.5),
            nn.Linear(400, 200),
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5),
            nn.Linear(200, num_classes)
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), -1)
        x = self.classifier(x)
        return x


def main():
    """Lesen Sie die Daten während des Trainings->Lernen(->Speichern von Daten während des Trainings)->Darstellung der Ergebnisse"""
    global checkpoint
    print('[Settings]')
    #Geräteeinstellungen
    device = 'cuda' if torch.cuda.is_available() else 'cpu'

    #Netzwerk,Bewertungsfunktion,Einstellung der Optimierungsfunktion
    net = NeuralNet(NUM_CLASSES).to(device)
    criterion = nn.CrossEntropyLoss()  #Bewertungsfunktion
    optimizer = optim.SGD(  #Optimierungsalgorithmus
        net.parameters(),
        lr=LEARNING_RATE,
        momentum=MOMENTUM,
        weight_decay=5e-4
    )

    #Einstellungen anzeigen
    # print('  Device               :', device)
    # print('  Dataset Class-Index  :', train_dataset.class_to_idx)
    # print('  Network Model        :', re.findall('(.*)\(', str(net))[0])
    # print('  Criterion            :', re.findall('(.*)\(', str(criterion))[0])
    # print('  Optimizer            :', re.findall('(.*)\(', str(optimizer))[0])
    # print('    -Learning Rate     :', LEARNING_RATE)
    # print('    -Momentum          :', MOMENTUM)

    t_loss_list = []
    t_acc_list = []
    v_loss_list = []
    v_acc_list = []
    epoch_pre = -1

    #Schulung (unterwegs) Datenerfassung
    if os.path.isfile(CKPT_PROCESS):
        checkpoint = torch.load(CKPT_PROCESS)
        net.load_state_dict(checkpoint['net'])
        optimizer.load_state_dict(checkpoint['optimizer'])
        t_loss_list = checkpoint['t_loss_list']
        t_acc_list = checkpoint['t_acc_list']
        v_loss_list = checkpoint['v_loss_list']
        v_acc_list = checkpoint['v_acc_list']
        epoch_pre = checkpoint['epoch']
        print("Progress until last time = {}/{} epochs"\
              .format(epoch_pre+1, NUM_EPOCHS))

    print('[Main process]')
    for epoch in range(epoch_pre+1, NUM_EPOCHS):
        t_loss, t_acc, v_loss, v_acc = 0, 0, 0, 0

        #Lernen---------------------------------------------------------
        net.train()  #Lernmodus
        for _, (images, labels) in enumerate(train_loader):
            images, labels = images.to(device), labels.to(device)
            optimizer.zero_grad()
            outputs = net(images)
            loss = criterion(outputs, labels)
            t_loss += loss.item()
            t_acc += (outputs.max(1)[1] == labels).sum().item()
            loss.backward()
            optimizer.step()
        avg_t_loss = t_loss / len(train_loader.dataset)
        avg_t_acc = t_acc / len(train_loader.dataset)

        #Auswertung---------------------------------------------------------
        net.eval()  #Bewertungsmodus
        with torch.no_grad():  #Beenden Sie die Aktualisierung des Verlaufs
            for images, labels in val_loader:
                images, labels = images.to(device), labels.to(device)
                images = images.to(device)
                labels = labels.to(device)
                outputs = net(images)
                loss = criterion(outputs, labels)
                v_loss += loss.item()
                v_acc += (outputs.max(1)[1] == labels).sum().item()
        avg_v_loss = v_loss / len(val_loader.dataset)
        avg_v_acc = v_acc / len(val_loader.dataset)
        # --------------------------------------------------------------
        print('\rEpoch [{}/{}] | Train [oss:{:.3f}, acc:{:.3f}] | Val [loss:{:.3f}, acc:{:.3f}]'\
              .format(epoch+1, NUM_EPOCHS, avg_t_loss, avg_t_acc, avg_v_loss, avg_v_acc), end='')

        #Verlust,Genauigkeitsaufzeichnung
        t_loss_list.append(avg_t_loss)
        t_acc_list.append(avg_t_acc)
        v_loss_list.append(avg_v_loss)
        v_acc_list.append(avg_v_acc)

        #Prozess zum Speichern des Fortschritts
        checkpoint['net'] = net.state_dict()
        checkpoint['optimizer'] = optimizer.state_dict()
        checkpoint['t_loss_list'] = t_loss_list
        checkpoint['t_acc_list'] = t_acc_list
        checkpoint['v_loss_list'] = v_loss_list
        checkpoint['v_acc_list'] = v_acc_list
        checkpoint['epoch'] = epoch

    graph()
    save_process()
    save_net()


def save_process():
    """Fortschritt speichern"""
    global checkpoint
    if not checkpoint: return
    torch.save(checkpoint, CKPT_PROCESS)


def save_net():
    """Speichern Sie nur Netzwerkinformationen"""
    global checkpoint
    if not checkpoint: return
    torch.save(checkpoint['net'], CKPT_NET)


def graph():
    """Verlust,Grafikgenauigkeit"""
    global checkpoint
    if not checkpoint: return
    t_loss_list = checkpoint['t_loss_list']
    t_acc_list = checkpoint['t_acc_list']
    v_loss_list = checkpoint['v_loss_list']
    v_acc_list = checkpoint['v_acc_list']

    plt.figure(figsize=(10, 4))
    plt.subplot(1, 2, 1)
    plt.plot(range(len(t_loss_list)), t_loss_list,
             color='blue', linestyle='-', label='t_loss')
    plt.plot(range(len(v_loss_list)), v_loss_list,
             color='green', linestyle='--', label='v_loss')
    plt.legend()
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.title('Training and validation loss')
    plt.grid()

    plt.subplot(1, 2, 2)
    plt.plot(range(len(t_acc_list)), t_acc_list,
             color='blue', linestyle='-', label='t_acc')
    plt.plot(range(len(v_acc_list)), v_acc_list,
             color='green', linestyle='--', label='v_acc')
    plt.legend()
    plt.xlabel('epoch')
    plt.ylabel('acc')
    plt.title('Training and validation accuracy')
    plt.grid()
    plt.show()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print()
        graph()
        save_process()

** [⚠Hinweis] Der Umfang des Netzwerks ist moderat. ** **. Wenn Sie die Anzahl der Ebenen und Knoten zu stark erhöhen, wird die Fehlermeldung "DefaultCPUAllocator: Speicher kann nicht zugeordnet werden: Sie haben versucht, 685198800 Byte zuzuweisen" angezeigt ..

Lauf

Klicken Sie hier für den Lernfortschritt. Die linke ist der Verlust und die rechte ist die Genauigkeit. Die blaue Linie steht für Trainingsdaten und die grüne gestrichelte Linie für Verifizierungsdaten. 0.png Die Genauigkeit der Verifizierungsdaten beträgt ca. 72%. Es gibt Raum für Verbesserungen ...

Wenn Sie das Training beendet haben, haben Sie eine ** "" trainierte_net.ckpt "" ** Datei, in der nur die trainierten Parameter gespeichert sind, und senden Sie sie erneut mit Slack oder etwas ** an Rasppie.

* Schritt 3 *: Echtzeitklassifizierung von Kamerabildern mit Raspeye und Anzeige der Ergebnisse

Als Ziel werden die Objekte im Kamerabild in Echtzeit klassifiziert und auf schöne Weise angezeigt.

Programm zum Erstellen

Nehmen Sie zuerst den Hintergrund auf, teilen Sie dann den Hintergrund von den nachfolgenden Bildern, schneiden Sie die entstehenden Objekte aus und erstellen Sie durch definierte Vorverarbeitung einen 4-dimensionalen Tensor-Batch. Der gesamte Stapel wird durch das Netzwerk geleitet, in die Wahrscheinlichkeit jeder Klasse konvertiert, und die Klasse (Name des Objekts) mit der höchsten Wahrscheinlichkeit wird überlagert und im Fenster angezeigt.

Laden Sie die zuvor erstellte "trainierte_net.ckpt".

** [⚠Hinweis] Wenn Sie keine Obergrenze für die Stapelgröße festlegen (die Anzahl der gleichzeitig zu erkennenden Objekte), friert der Raspeltorte möglicherweise ein, wenn Sie versuchen, eine große Anzahl erkannter Bereiche gleichzeitig zu verarbeiten. ** **.

raltime_classification.py


# coding: utf-8
import os
from PIL import Image
from time import sleep
import cv2
import picamera
import picamera.array
import torch
#Im Pytorch-Verzeichnis"export OMP_NUM_THREADS=1 or 2 or 3"Verpflichtend(Der Standardwert ist 4)
#Die Anzahl der Parallelverarbeitungskerne"print(torch.__config__.parallel_info())"Bestätigen mit
import torch.nn as nn
import torch.utils
from torchvision import transforms

CKPT_NET = 'trained_net.ckpt'  #Geschulte Parameterdatei
OBJ_NAMES = ['Phone', 'Wallet', 'Watch']  #Anzeigename jeder Klasse
MIN_LEN = 50
GRAY_THR = 20
CONTOUR_COUNT_MAX = 3  #Chargengröße(Anzahl der Objekte, die gleichzeitig erkannt werden sollen)Obergrenze von
SHOW_COLOR = (255, 191, 0)  #Rahmenfarbe(B,G,R)

NUM_CLASSES = 3
PIXEL_LEN = 112  #Größe nach Größenänderung(1 Seite)
CHANNELS = 1  #Anzahl der Farbkanäle(BGR:3,Graustufen:1)


#Definition der Bilddatenkonvertierung
#Mit Größe ändern,Bezogen auf die erste lineare Eingabe des Klassifikators
data_transforms = transforms.Compose([
    transforms.Resize((PIXEL_LEN, PIXEL_LEN)),
    transforms.Grayscale(),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5], std=[0.5])
])


class NeuralNet(nn.Module):
    """Netzwerkdefinition.Muss mit dem zum Lernen verwendeten identisch sein"""
    def __init__(self, num_classes):
        super(NeuralNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 8, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(8, 16, kernel_size=5, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
        )
        self.classifier = nn.Sequential(
            nn.Dropout(p=0.5),
            nn.Linear(400, 200),
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5),
            nn.Linear(200, num_classes)
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), -1)
        x = self.classifier(x)
        return x


def detect_obj(back, target):
    """
Mit OpenCV Hintergrunddifferenzverarbeitung,Erstellen Sie einen Tapple mit erkannten Objekten
Streit:
        back:Hintergrundbild eingeben
Farbbild
        target:Bild für Hintergrundunterschied
Farbbild.Schneiden Sie mehrere Objekte aus,In einem Farbbild Taple zusammenfügen
    """
    print('Detecting objects ...')
    #Binarisierung
    b_gray = cv2.cvtColor(back, cv2.COLOR_BGR2GRAY)
    t_gray = cv2.cvtColor(target, cv2.COLOR_BGR2GRAY)
    #Berechnen Sie die Differenz
    diff = cv2.absdiff(t_gray, b_gray)

    #Kontur entsprechend der Schwelle,Erstellen Sie eine Maske,Objekte extrahieren
    #Der Index von findContours ist, cv2.__version__ == 4.2.0->[0], 3.4.7->[1]
    mask = cv2.threshold(diff, GRAY_THR, 255, cv2.THRESH_BINARY)[1]
    cv2.imshow('mask', mask)
    contour = cv2.findContours(mask,
                               cv2.RETR_EXTERNAL,
                               cv2.CHAIN_APPROX_SIMPLE)[1]

    #Koordinaten des Änderungsbereichs über einer bestimmten Höhe und Breite erfasst,Größenstapel erstellen
    pt_list = list(filter(
        lambda x: x[2] > MIN_LEN and x[3] > MIN_LEN,
        [cv2.boundingRect(pt) for pt in contour]
    ))[:CONTOUR_COUNT_MAX]

    #Schneiden Sie den Rahmen gemäß den Positionsinformationen aus,In ein Tupel PIL-Bild konvertieren und zurückkehren
    obj_imgaes = tuple(map(
        lambda x: Image.fromarray(target[x[1]:x[1]+x[3], x[0]:x[0]+x[2]]),
        pt_list
    ))
    return (obj_imgaes, pt_list)


def batch_maker(tuple_images, transform):
    """
Transformieren Sie das Tupel des Bilds im PIL-Format,Konvertieren Sie in einen Tensor-Batch, der im Netzwerk verarbeitet werden kann
Streit:
        tuple_images:PIL Bild Taple
        transform:Definition der Fackelbildkonvertierung
    """
    return torch.cat([transform(img) for img
                      in tuple_images]).view(-1, CHANNELS, PIXEL_LEN, PIXEL_LEN)


def judge_what(img, probs_list, pos_list):
    """
Bestimmen Sie das Objekt aus der Wahrscheinlichkeit, zu jeder Klasse zu gehören,Zeigen Sie den Rahmen und den Namen an dieser Position an,Gibt den Index der Klasse zurück
Streit:
        probs_list:Sekundäres Array von Wahrscheinlichkeiten.Stapelformat
        pos_list:Sekundäre Anordnung von Positionen.Stapelformat
    """
    print('Judging objects ...')
    #Konvertieren Sie in eine Liste der höchsten Wahrscheinlichkeiten und ihrer Indizes
    ip_list = list(map(lambda x: max(enumerate(x), key = lambda y:y[1]),
                       F.softmax(probs_list, dim=-1)))  # <- 4/30 Korrekturen

    #Index in Objektnamen konvertieren,Schreiben Sie den Objektnamen und die Sicherheit an der Position des Objekts und zeigen Sie sie an
    for (idx, prob), pos in zip(ip_list, pos_list):
        cv2.rectangle(img, (pos[0], pos[1]), (pos[0]+pos[2], pos[1]+pos[3]), SHOW_COLOR, 2)
        cv2.putText(img, '%s:%.1f%%'%(OBJ_NAMES[idx], prob*100), (pos[0]+5, pos[1]+20),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.8, SHOW_COLOR, thickness=2)
    return ip_list


def realtime_classify():
    """Geschulte Modellbeladung->Testdaten lesen->Einstufung->Zeigen Sie das dem Bild überlagerte Ergebnis an"""
    #Geräteeinstellungen
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    #Netzwerkeinstellungen
    net = NeuralNet(NUM_CLASSES).to(device)

    #Geschulte Datenerfassung
    if os.path.isfile(CKPT_NET):
        checkpoint = torch.load(CKPT_NET)
        net.load_state_dict(checkpoint)
    else:
        raise FileNotFoundError('No trained network file: {}'.format(CKPT_NET))

    #Bewertungsmodus
    net.eval()
    #Starten Sie picamera
    with picamera.PiCamera() as camera:
        camera.resolution = (480, 480)
        #Starten Sie das Streaming
        with picamera.array.PiRGBArray(camera) as stream:
            print('Setting background ...')
            sleep(2)
    
            camera.exposure_mode = 'off'  #Weißabgleich behoben
            camera.capture(stream, 'bgr', use_video_port=True)
            #Als Hintergrundsbild festlegen
            img_back = stream.array

            stream.seek(0)
            stream.truncate()
            
            print('Start!')
            with torch.no_grad():
                while True:
                    camera.capture(stream, 'bgr', use_video_port=True)
                    #Hintergrundunterschied für zukünftige Eingabebilder
                    img_target = stream.array
                    #Erkennt Objekte und ihre Positionen
                    obj_imgs, positions = detect_obj(img_back, img_target)
                    if obj_imgs:
                        #Konvertieren Sie erkannte Objekte in das Netzwerkeingabeformat
                        obj_batch = batch_maker(obj_imgs, data_transforms)
                        #Einstufung
                        outputs = net(obj_batch)
                        #Beurteilung
                        result = judge_what(img_target, outputs, positions)
                        print('  Result:', result)

                    #Anzeige
                    cv2.imshow('detection', img_target)

                    if cv2.waitKey(200) == ord('q'):
                        cv2.destroyAllWindows()
                        return

                    stream.seek(0)
                    stream.truncate()


if __name__ == "__main__":
    try:
        realtime_classify()
    except KeyboardInterrupt:
        cv2.destroyAllWindows()

Lauf

Bringen Sie die vorherige "trainierte_net.ckpt" auf den Raspelkuchen und führen Sie sie im selben Verzeichnis aus. Der Name des erkannten Objekts und seine Sicherheit werden angezeigt.

Das Ausführungsergebnis ist ... Ich bin von dem Moment an mit der hochpräzisen Klassifizierung zufrieden! !!

0.jpg 1.jpg 2.jpg

** [⚠Hinweis] Es wird empfohlen, die Anzahl der für die Ausführung verwendeten Kerne zu ändern (Standard 4). ** **. Bei Verwendung mit 4 vollen Kernen besteht eine große Frostgefahr. Ändern Sie den Befehl im Pytorch-Verzeichnis in "export OMP_NUM_THREADS = 2" (mit 2 Kernen). Sie können die Anzahl der Kerne mit print (torch .__ config __. Parallel_info ()) überprüfen. Wenn Sie die Shell jedoch schließen, werden die Änderungen verworfen, um sie dauerhaft zu machen, und zwar unter "... ~ fi" am Ende von ** ". Profil" ** in "/ home / pi", "exportiere OMP_NUM_THREADS" Schreiben Sie = 2` und starten Sie neu.

Zusammenfassung

Ich konnte machen was ich wollte! (Es tut mir leid für die mangelnde Lesbarkeit ...) Wenn Sie die OpenCV-Gesichtserkennung verwenden, können Sie sie anscheinend sofort auf die sehr einfache Gesichtserkennung anwenden.

Ursprünglich dachte ich über die Implementierung von SSD nach, aber ich dachte, es wäre schwierig, einen Datensatz mit Standortinformationen zu erstellen, und gab auf, weil ich den Fehler, den ich beim Training mit Beispieldaten bekam, nicht lösen konnte. ..

Im Gegensatz zu SSDs besteht der Nachteil darin, dass der Hintergrundunterschied diesmal nicht die Trennung der überlappenden Objekte ermöglicht und als eins beurteilt wird.

Es war eine gute Studie ~

Recommended Posts

Echtzeitklassifizierung mehrerer Objekte in Kamerabildern mit tiefem Erlernen von Raspberry Pi 3 B + & PyTorch
Die Geschichte des tiefen Lernens mit TPU
Zählen Sie die Anzahl der Parameter im Deep-Learning-Modell
Nehmen Sie den Wert des SwitchBot-Thermo-Hygrometers mit Raspberry Pi
Umschalten der Bot-Thermo-Hygrometer-Werte mit Raspberry Pi
Eine Geschichte, bei der ich darüber nachdachte, die Informationen über die Fülle des Parkplatzes anhand des von der Webkamera und Razpai erhaltenen Bildes und durch tiefes Lernen zu ermitteln.
Notieren Sie sich, was Sie in Zukunft mit Razpai machen möchten
Steuern Sie die Anzeige des RGB LED Matirix Lightning Bulletin Boards mit Raspberry Pi 3B + frei
Spielen Sie mit dem Raspberry Pi Zero WH-Kameramodul Teil 1
[Deep Learning] Bildklassifizierung mit Faltungsnetz [DW Tag 4]
[PyTorch] Bildklassifizierung von CIFAR-10
Ich habe versucht, die Bewässerung des Pflanzgefäßes mit Raspberry Pi zu automatisieren
Protokollieren Sie die Omron-Umgebungssensorwerte regelmäßig mit Raspberry Pi
Diagramm der Geschichte der Anzahl der Ebenen des tiefen Lernens und der Änderung der Genauigkeit
Fügen Sie Ihre eigenen Bilddaten in Deep Learning ein und spielen Sie damit
Protokollierung der Omron-Umgebungssensorwerte mit Raspberry Pi (USB-Typ)
Multi-Class Multi-Label-Klassifizierung von Bildern mit Pytorch
Deep Learning 2 durch Implementierung gelernt (Bildklassifizierung)
Othello-Aus der dritten Zeile von "Implementation Deep Learning" (3)
Bilderkennungsmodell mit Deep Learning im Jahr 2016
Beschleunigen Sie Deep Learning mit der Rasperry Pi 4-CPU
Emotionale Analyse von Tweets mit Deep Learning
Othello-Aus der dritten Zeile von "Implementation Deep Learning" (2)
Ich habe die Beleuchtungsstärke des Raumes mit Raspberry Pi, Arduino und einem optischen Sensor getwittert
Extrahieren Sie die Farbe des Objekts im Bild mit Mask R-CNN und K-Means Clustering
Deep Learning von Grund auf neu Die Theorie und Implementierung des mit Python erlernten Deep Learning Kapitel 3
Benachrichtigen Sie regelmäßig den Verarbeitungsstatus von Raspberry Pi mit Python → Google Spreadsheet → LINE
So installieren Sie das Deep Learning Framework Tensorflow 1.0 in der Windows Anaconda-Umgebung