[PYTHON] Ich habe versucht, das Bild zu verarbeiten und zu transformieren und die Daten für maschinelles Lernen zu erweitern

Überblick

Deep Learning erfordert eine große Datenmenge, um für effektives Lernen vorbereitet zu sein. Wenn es jedoch schwierig ist, eine große Datenmenge vorzubereiten, kann eine als Datenerweiterung bezeichnete Technik verwendet werden, um eine kleine Datenmenge aufzublasen (zu vermehren) und für das Training zu verwenden.

Wenn die Daten "Bild" sind, ** parallele Bewegung **, ** Drehung **, ** Vergrößerung / Verkleinerung **, ** verkehrt herum **, ** Links / Rechts-Inversion **, ** Helligkeitsanpassung wie folgt Daten werden durch Bildverarbeitung erweitert, die Verformung und Verarbeitung wie ** kombiniert. f1ex_arr.png

In der Umgebung von Tensorflow (2.x) + Keras wird eine Klasse namens "ImageDataGenerator" für die Datenerweiterung vorbereitet. Wenn Sie diese verwenden, können Sie ** Daten mit zufälliger Bildverarbeitung verwenden (dies wird in diesem Artikel erweitert. Es ist relativ einfach, ein Bild zu erzeugen. Dieser Artikel beschreibt die Datenerweiterung mit diesem "ImageDataGenerator".

Ohne Verwendung von ImageDataGenerator können Bibliotheken wie ** OpenCV ** und ** tf.keras ** ** affine Transformationen ** (cv2.warpAffine undtensorflow.keras.preprocessing.image.apply_affine_transform "verwenden. Verwenden Sie), um ** parallele Bewegung, Drehung und Skalierung ** durchzuführen und eine manuelle Datenerweiterung zu versuchen. Vergleichen Sie auch die Verarbeitungsgeschwindigkeiten beider Bibliotheken (das Ergebnis ist ein überwältigender Sieg für OpenCV).

Ausführungsumgebung

Wir überprüfen die Ausführung in der Google Colab. Umgebung.

opencv-python      4.1.2.30
tensorflow         2.1.0rc1

Erweiterung von Bilddaten mit ImageDataGenerator

Vorbereitung: Bibliothek importieren

Wechseln Sie die Version von Tesorflow und importieren Sie die Bibliothek. Es verwendet auch matplotlib, um die Bilder vor und nach der Verarbeitung anzuzeigen. Importieren Sie diese also ebenfalls.

Vorbereitung: Bibliothek importieren


%tensorflow_version 2.x
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

Vorbereitung: Vorbereitung der Bilddaten

Erfassen Sie die Zieldaten zur Verarbeitung. Hier verwenden wir die Trainingsdaten von "** CIFAR-10 **". CIFAR-10 ist ein Datensatz, der aus 10 Arten von Bildern besteht: "Flugzeug", "Automobil", "Vogel", "Katze", "Hirsch", "Hund", "Frosch", "Pferd", "Schiff" und "LKW".

Vorbereitung: Vorbereitung der Bilddaten


(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
img_cifar10 = x_train/255.
print(img_cifar10.shape) # -> (50000, 32, 32, 3)  32x32 3ch RGB 

img_cifar10 enthält $ 3 $ ch-RGB $ 50.000 $ Bilder von $ 32 \ times 32 $ px im numpy.ndarray-Format.

Vorbereitung: Definition der Bildanzeigefunktion

Erstellen Sie eine Funktion zum Anzeigen des Bildes. Definieren Sie "showImage (...)", um nur ein Bild anzuzeigen, und "showImageArray (...)", um mehrere Bilder gleichzeitig anzuzeigen, indem Sie ein Bildarray angeben.

Vorbereitung: Definition der Bildanzeigefunktion


def showImage(img,title=None):
  plt.figure(figsize=(3, 3))
  plt.gcf().patch.set_facecolor('white')
  plt.xticks([])
  plt.yticks([])
  plt.title(title)
  plt.imshow(img)
  plt.show()
  plt.close()

def showImageArray(img_arry):
  n_cols = 8
  n_rows = ((len(img_arry)-1)//n_cols)+1
  fig, ax = plt.subplots(nrows=n_rows, ncols=n_cols, figsize=(10, 1.25*n_rows))
  fig.patch.set_facecolor('white')
  for i,ax in enumerate( ax.flatten() ):
    if i < len(img_arry):
      ax.imshow(img_arry[i])
      ax.set_xticks([])
      ax.set_yticks([])
    else :
      ax.axis('off') #Margin-Verarbeitung
  plt.show()
  plt.close()

Rufen Sie diese Funktionen wie folgt auf:

Aufrufen einer Bildanzeigefunktion


# img_Zeigen Sie das 12. Bild von cifar10 an
showImage(img_cifar10[12],title='CIFAR-10 Train Data [12]')

#Bilder vom 0. bis 39. (=40 Blatt) wird angezeigt
showImageArray(img_cifar10[:40])

Geben Sie für "showImage (...)" numpy.ndarray mit der Form "(32,32,3)" als Argument an. Geben Sie für "showImageArray (...)" numpy.ndarray mit der Form "(arr_len, 32,32,3)" als Argument an (wobei "arr_len" die Länge des Bildarrays ist).

Das Ausführungsergebnis ist wie folgt. f1.png f2.png

Erweiterung von Bilddaten mit ImageDataGenerator

ImageDataGenerator-Klasse Kann Bilddateien in das angegebene Verzeichnis laden und die Daten erweitern. Hier wird es zur Datenerweiterung verwendet.

Geben Sie bei Verwendung zur Datenerweiterung den Parameter "** Welche Art der Verarbeitung (Verschieben, Drehen, Vergrößern?) Wird zum Zeitpunkt der Initialisierung zufällig mit welcher Intensität durchgeführt **" an. Initialisieren Sie es beispielsweise mit den folgenden Parametern.

ImageDataGenerator-Initialisierung


ImageDataGenerator = tf.keras.preprocessing.image.ImageDataGenerator
image_data_generator = ImageDataGenerator(
  rotation_range=20,       #Nach dem Zufallsprinzip innerhalb von ± 20 Grad drehen
  width_shift_range=8,     #Bewegen Sie sich zufällig nach links und rechts innerhalb des Bereichs von ± 8 Pixel
  height_shift_range=4,    #Bewegen Sie sich zufällig innerhalb eines Bereichs von ± 4 Pixel auf und ab
  zoom_range=(0.8, 1.2),   #Zufällig 0.8~1.Vergrößern Sie den 2-fachen Bereich
  horizontal_flip=True,    #Nach dem Zufallsprinzip nach links und rechts drehen
  channel_shift_range=0.2) #Zufälliger Verschiebungsbereich des Kanalwerts (Helligkeit)

Wenn width_shift_range und height_shift_range als Dezimalwerte unter $ 1 $ angegeben werden, können Sie einen zufälligen Bereich als Prozentsatz der Bildgröße angeben. Auch wenn Sie diesmal nicht verwendet werden, können Sie die verkehrte Verarbeitung mit vertical_flip = True hinzufügen.

Generieren Sie nur ein erweitertes Bild

Mit diesem Generator werde ich versuchen, die Daten für das "Pferde" -Bild von "img_cifar10 [12]" zu erweitern (zuerst wird ** nur ein ** erweitertes Bild generiert). Wenn Sie den folgenden Code ausführen, wird die Bildverarbeitung zufällig innerhalb des in der obigen Initialisierung angegebenen Parameterbereichs ausgeführt.

Generieren Sie nur ein erweitertes Bild


org_img = img_cifar10[12].copy() #Das Ziel ist das 12. "Pferd"
ex_img  = image_data_generator.flow( org_img.reshape(1,32,32,3), batch_size=1)[0][0]
print(ex_img.shape) # -> (32, 32, 3)
showImage(ex_img,title='CIFAR-10 Train Data [12] Ex')

Verwenden Sie die Methode flow (...), um das erweiterte Bild zu erhalten. Geben Sie als Argument das Originalbild ** array ** an (konvertiert in ein Array mit einem Element mit .reshape (1,32,32,3), da es keine einzelne Eingabe unterstützt). Der Rückgabewert ist NumpyArrayIterator. Bewerten Sie ihn daher mit [0] [0] und dem 0. Element. Wird in ex_img erfasst und gespeichert.

Das Ausführungsergebnis ist wie folgt. Links und rechts werden gespiegelt, die horizontale Bewegung wird angewendet und sie ist insgesamt heller (die Ausführungsergebnisse ändern sich mit jeder Ausführung). f1ex.png

Übrigens, wenn Sie das Bild drehen oder nach oben / unten / links / rechts verschieben, wird eine Lücke erstellt, aber der Rand des Bildes wird automatisch gedehnt und gefüllt (das Ergebnis ist ein natürliches Bild). Wenn Sie nicht möchten, dass dies angewendet wird, geben Sie in der Initialisierung fill_mode = 'Konstante' an. Dann werden die Ränder wie unten gezeigt mit Schwarz gefüllt. Außerdem können Sie "fill_mode =" Reflect "," fill_mode = "wrap" und "fill_mode =" next "(Standard) angeben. f1ex2.png

Generieren Sie mehrere erweiterte Bilder

Generieren Sie als Nächstes 39 erweiterte Bilder aus dem "Pferde" -Bild von "img_cifar10 [12]". image_data_generator.flow (...) gibt NumpyArrayIterator zurück. Verwenden Sie also next (), um die Bilder nacheinander abzurufen.

Generieren Sie ein oder mehrere erweiterte Bilder


org_img = img_cifar10[12].copy()
ex_img = np.empty([40, 32, 32, 3]) #Bereiten Sie einen Bereich vor, in dem 40 Blatt einschließlich des Originals aufbewahrt werden können
ex_img[0,:,:,:] = org_img          #Bewahren Sie das Original auf dem 0. Blatt auf
iter_ = image_data_generator.flow( org_img.reshape(1,32,32,3), batch_size=1)
for i in range(1,40):
  ex_img[i,:,:,:] = iter_.next()[0] #Speichern Sie die generierten Bilder nacheinander
showImageArray(ex_img)

Das Ausführungsergebnis ist wie folgt. f1ex_arr.png

Generieren Sie ein erweitertes Bild für jedes der 0. bis 23. Bilder von CIFAR-10

Im vorherigen Abschnitt haben wir mehrere erweiterte Bilder für ein Bild generiert. Dieses Mal wird für jedes der 40 Bilder vom 0. bis zum 23. der Trainingsdaten von CIFAR-10 ein erweitertes Bild erzeugt.

Generieren Sie ein erweitertes Bild für jedes Bildarray


showImageArray(img_cifar10[:24]) # CIFAR-10 Zeigen Sie das 0. bis 23. Originalblatt an

ex_img = np.empty([24, 32, 32, 3])
ex_img = image_data_generator.flow(img_cifar10[:24], batch_size=24, shuffle=False)[0]
showImageArray(ex_img)           # CIFAR-10 Erweitern Sie 0 bis 23 wird angezeigt

Das Ausführungsergebnis ist wie folgt. Hier sind zunächst die Originaldaten vom 0. bis zum 23. Trainingsdaten von CIFAR-10. cifar10.png

Als nächstes wird hier das mit ImageDataGenerator erweiterte Bild angezeigt. Sie können sehen, dass auf jedes Bild eine andere Verarbeitung (Kombination aus Bewegung, Drehung, Skalierung, Spiegeln usw.) angewendet wird. cifar10ex.png

Wenn Sie im Argument von "flow (...)" nicht "shuffle = False" angeben, wird die Reihenfolge der Ausgabebildsequenzen gemischt.

Vergleich der Verarbeitung durch OpenCV und tf.keras

Geben Sie den Bewegungsumfang, den Drehwinkel und die Vergrößerung manuell an und generieren Sie erweiterte Bilder mit den Bibliotheken OpenCV und tf.keras. Vergleichen Sie auch die Verarbeitungszeit.

Drehung

Das Bild wird ** gegen den Uhrzeigersinn </ font> um 45 Grad gedreht **. Die Drehachse ist die Bildmitte.

Ein Programm, das die OpenCV-Bibliothek verwendet, sieht folgendermaßen aus: In OpenCV ist die Richtung gegen den Uhrzeigersinn "positiv", daher wird der Drehwinkel "45" so wie er ist angegeben. cv2.warpAffine (...) ist der Hauptteil des Prozesses. Wenn Sie nicht "borderMode = cv2.BORDER_REPLICATE" angeben, wird die Lücke mit Schwarz gefüllt.

OpenCV-Version von CIFAR10 50,Rotationsbearbeitung von 000 Blatt


import time
import cv2
deg = 45  #Gegen den Uhrzeigersinn ist "positiv"
w, h = 32, 32  #Bildgröße
m = cv2.getRotationMatrix2D((w/2,h/2), deg, 1) #Transformationsmatrix
img_cifar10_ex = np.empty_like(img_cifar10) #Ergebnis Speicherort np.empty([50000,32,32,3])Gleich wie
t1 = time.time()
for i,img in enumerate( img_cifar10 ) :
  img = cv2.warpAffine(img, m, (w,h), borderMode=cv2.BORDER_REPLICATE)
  img_cifar10_ex[i,:,:,:] = img
t2 = time.time()
print(f'Verarbeitungszeit{t2-t1:.1f}[sec]')
showImageArray(img_cifar10_ex[:24]) #Zeigen Sie nur 24 Blätter an

Das Ausführungsergebnis ist wie folgt. Die Verarbeitungszeit betrug ** 1,3 [s] **. cv_r.png

Auf der anderen Seite sieht ein Programm, das die tf.keras-Bibliothek verwendet, folgendermaßen aus: Hier ist die Richtung gegen den Uhrzeigersinn "negativ", daher wird der Drehwinkel als "-45" angegeben.

tf.50 von Keras Version von CIFAR10,Rotationsbearbeitung von 000 Blatt


import time
from tensorflow.keras.preprocessing.image import apply_affine_transform
deg = -45  #Gegen den Uhrzeigersinn ist "negativ"
img_cifar10_ex = np.empty_like(img_cifar10) #Ergebnis Speicherort
t1 = time.time()
for i,img in enumerate( img_cifar10 ) :
  img = apply_affine_transform(img, theta=deg)
  img_cifar10_ex[i,:,:,:] = img
t2 = time.time()
print(f'Verarbeitungszeit{t2-t1:.1f}[sec]')
showImageArray(img_cifar10_ex[:24])

Das Ausführungsergebnis ist wie folgt. Die Verarbeitungszeit betrug ebenfalls ** 16,2 [s] **. Die Verarbeitungsgeschwindigkeit war mit OpenCV </ font> überwiegend ** schneller **. tf_r.png

Parallelbewegung

Verschiebt das Bild um $ 2 $ px nach ** rechts ** und um $ 5 $ px ** nach unten **.

Erstens ist die OpenCV-Version. Der Vorgang ist der gleiche wie bei der vorherigen "Drehung", nur der Inhalt der Umwandlungsmatrix "m" ist unterschiedlich.

OpenCV-Version von CIFAR10 50,Verschieben von 000 Blättern


import time
import cv2
tx, ty = 2, 5 
w, h = 32, 32  #Bildgröße
m = np.float32([[1,0,tx],[0,1,ty]]) #Transformationsmatrix
img_cifar10_ex = np.empty_like(img_cifar10) #Ergebnis Speicherort
t1 = time.time()
for i,img in enumerate( img_cifar10 ) :
  img = cv2.warpAffine(img, m, (w,h), borderMode=cv2.BORDER_REPLICATE)
  img_cifar10_ex[i,:,:,:] = img
t2 = time.time()
print(f'Verarbeitungszeit{t2-t1:.1f}[sec]')
showImageArray(img_cifar10_ex[:24])

Das Ausführungsergebnis ist wie folgt. Die Verarbeitungszeit betrug ** 1,3 [Sek] ** (da die wesentliche Verarbeitung mit "Rotation" identisch ist, ändert sich die Ausführungszeit nicht). cv_m.png

Als nächstes kommt die tf.keras-Version. Ich habe nicht verstanden, warum es so entworfen wurde, aber es sieht aus wie "apply_affine_transform (img, tx = -5, ty = -2)", um 2px nach rechts und 5px nach unten zu verschieben. Muss angegeben werden. Geheimnis ist.

--Referenz: [Die Keras ImageDataGenerator apply_transform () -Methode verschiebt das Bild in die entgegengesetzte Richtung](https://stackoverflow.com/questions/56580076/keras-imagedatagenerator-apply-transform-method-shifts-the-image-in-opposite- d)

tf.50 von Keras Version von CIFAR10,Rotationsbearbeitung von 000 Blatt


import time
from tensorflow.keras.preprocessing.image import apply_affine_transform
tx, ty = 2, 5 
img_cifar10_ex = np.empty_like(img_cifar10) #Ergebnis Speicherort
t1 = time.time()
for i,img in enumerate( img_cifar10 ) :
  img = apply_affine_transform(img, tx=-ty, ty=-tx) #Achten Sie darauf, wie Sie Argumente vorbringen
  img_cifar10_ex[i,:,:,:] = img
t2 = time.time()
print(f'Verarbeitungszeit{t2-t1:.1f}[sec]')
showImageArray(img_cifar10_ex[:24])

Das Ausführungsergebnis ist wie folgt. Die Verarbeitungszeit beträgt ** 13,9 [Sek] **, was ungefähr 10 Mal länger ist als die Verarbeitungszeit von OpenCV wie zuvor. tf_m.png

Erweiterung

Das Bild wird 1,2-fach vergrößert (die Bildgröße ändert sich auch bei Vergrößerung nicht).

Erstens ist die OpenCV-Version. Vergrößern Sie mit "Größe ändern" und schneiden Sie dann auf $ 32 \ mal 32 $ px.

OpenCV-Version von CIFAR10 50,Erweiterungsverarbeitung von 000 Blatt


import cv2
f = 1.2 
w, h = 32, 32               #Bildgröße
w2, h2 = int(w*f), int(h*f) #Vergrößerte Bildgröße
tx, ty = int((w2-w)/2),int((h2-h)/2) #Startkoordinaten trimmen
m = np.float32([[1,0,tx],[0,1,ty]]) #Transformationsmatrix
img_cifar10_ex = np.empty_like(img_cifar10) #Ergebnis Speicherort
t1 = time.time()
for i,img in enumerate( img_cifar10 ) :
  img = cv2.resize(img,(w2,h2)) 
  img_cifar10_ex[i,:,:,:] = img[tx:tx+w,ty:ty+h,:] #Schneiden Sie 32x32 aus dem vergrößerten Bild
t2 = time.time()
print(f'Verarbeitungszeit{t2-t1:.1f}[sec]')
showImageArray(img_cifar10_ex[:24])

Das Ausführungsergebnis ist wie folgt. Die Verarbeitungszeit betrug ** 1,2 [s] **. cv_f.png

Als nächstes kommt die tf.keras-Version. Es ist auch sehr verwirrend wie zuvor, aber wie bei "apply_affine_transform (img, zx = 1 / f, zy = 1 / f)" haben die Argumente "zx" und "zy" "** Umkehrung der Vergrößerung **". Ist gegeben.

tf.50 von Keras Version von CIFAR10,Erweiterungsverarbeitung von 000 Blatt


import time
from tensorflow.keras.preprocessing.image import apply_affine_transform
f = 1.2
img_cifar10_ex = np.empty_like(img_cifar10) #Ergebnis Speicherort
t1 = time.time()
for i,img in enumerate( img_cifar10 ) :
  img = apply_affine_transform(img, zx=1/f, zy=1/f) #Argument Aufmerksamkeit
  img_cifar10_ex[i,:,:,:] = img
t2 = time.time()
print(f'Verarbeitungszeit{t2-t1:.1f}[sec]')
showImageArray(img_cifar10_ex[:24])

Die Verarbeitungszeit beträgt 13,4 [Sek.] Und das Ergebnis ist wie folgt. tf_f.png

Recommended Posts