[PYTHON] Super-Resolution-Technologie-SRCNN-I hat versucht, sie zu implementieren (Tensorflow 2.0) Lernphase

Überblick

Die Super-Resolution-Technologie ist eine Technologie, die aus einem Bild mit niedriger Auflösung ein Bild mit hoher Auflösung macht. Dieses Mal habe ich SRCNN implementiert, eine Art Super-Resolution-Technologie, die relativ einfach zu implementieren ist.

Diese Zeit ist ** SRCNN-Lernphase **. Das nächste Mal wird es die [Vorhersagephase] von SRCNN sein (https://qiita.com/hima_zin331/items/ebb6046a2a8d860254e1). Eigentlich habe ich versucht, SRGAN zusätzlich zu SRCNN zu implementieren, also werde ich das als eine Reihe von Super-Resolution-Technologien aufgreifen.

Umgebung

-Software- Windows 10 Home Anaconda3 64-bit(Python3.7) Spyder -Library- Tensorflow 2.1.0 opencv-python 4.1.2.30 -Hardware- CPU: Intel core i9 9900K GPU: NVIDIA GeForce RTX2080ti RAM: 16GB 3200MHz

Referenz

Seite? ˅SRCNN-Papier[Intern CV Report] Erkundung der Geschichte der Super-Resolution -2016 Edition-[Sparse Coding] Vorteile der Darstellung spärlicher DatenKeras: Super ResolutionIch habe eine einfache Superauflösung mit tiefem Lernen versucht ・ [Einführung in PyTorch und Super Resolution] (https://buildersbox.corp-sansan.com/entry/2019/02/21/110000) ・ [Ich habe das Modell SRCNN implementiert, das das Bild mit pytorch superauflösend macht](https://nykergoto.hatenablog.jp/entry/2019/05/18/%E7%94%BB%E5%83%8F % E3% 81% AE% E8% B6% 85% E8% A7% A3% E5% 83% 8F% E5% BA% A6% E5% 8C% 96% E3% 82% 92% E3% 81% 99% E3 % 82% 8B% E3% 83% A2% E3% 83% 87% E3% 83% AB_SRCNN_% E3% 82% 92_pytorch_% E3% 81% A7% E5% AE% 9F% E8% A3% 85% E3% 81 % 97% E3% 81% A6)

Programm

Ich werde es auf Github posten. https://github.com/himazin331/Super-resolution-CNN Das Repository enthält eine Lernphase und eine Vorhersagephase.

Dieses Mal habe ich General-100 für den Datensatz verwendet. Ich habe den Datensatz auch zu Demonstrationszwecken in das GitHub-Repository gestellt.

Quellcode

** Bitte beachten Sie, dass der Code verschmutzt ist ... **

srcnn_tr.py


import argparse as arg
import os
import sys

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' #TF-Nachricht ausblenden

import tensorflow as tf
import tensorflow.keras.layers as kl
from tensorflow.python.keras import backend as K

import cv2
import numpy as np

import matplotlib.pyplot as plt

# SRCNN
class SRCNN(tf.keras.Model):

    def __init__(self, h, w):
        super(SRCNN, self).__init__()

        self.conv1 = kl.Conv2D(64, 3, padding='same', activation='relu', input_shape=(None, h, w, 3))
        self.conv2 = kl.Conv2D(32, 3, padding='same', activation='relu')
        self.conv3 = kl.Conv2D(3, 3, padding='same', activation='relu')

    def call(self, x):
        
        h1 = self.conv1(x)
        h2 = self.conv2(h1)
        h3 = self.conv3(h2)

        return h3

#Lernen
class trainer(object):

    def __init__(self, h, w):
        
        self.model = SRCNN(h, w)
        
        self.model.compile(optimizer=tf.keras.optimizers.Adam(),
                           loss=tf.keras.losses.MeanSquaredError(),
                            metrics=[self.psnr])
        
    def train(self, lr_imgs, hr_imgs, out_path, batch_size, epochs):

        #Lernen
        his = self.model.fit(lr_imgs, hr_imgs, batch_size=batch_size, epochs=epochs)

        print("___Training finished\n\n")

        #Parameter speichern
        print("___Saving parameter...")
        self.model.save_weights(out_path)
        print("___Successfully completed\n\n")

        return his, self.model

    # PSNR(Spitzensignal-Rausch-Verhältnis)
    def psnr(self, h3, hr_imgs):
        
        return -10*K.log(K.mean(K.flatten((h3 - hr_imgs))**2))/np.log(10)

#Datensatzerstellung
def create_dataset(data_dir, h, w, mag):

    print("\n___Creating a dataset...")
    
    prc = ['/', '-', '\\', '|']
    cnt = 0

    #Anzahl der Bilddaten
    print("Number of image in a directory: {}".format(len(os.listdir(data_dir))))

    lr_imgs = []
    hr_imgs = []

    for c in os.listdir(data_dir):

        d = os.path.join(data_dir, c)

        _, ext = os.path.splitext(c)
        if ext.lower() == '.db':
            continue
        elif ext.lower() != '.bmp':
            continue

        #Lesen, Größe ändern(Hochauflösendes Bild)
        img = cv2.imread(d)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.resize(img, (h, w))

        #Bild mit niedriger Auflösung
        img_low = cv2.resize(img, (int(h/mag), int(w/mag)))
        img_low = cv2.resize(img_low, (h, w))

        lr_imgs.append(img_low)
        hr_imgs.append(img)

        cnt += 1

        print("\rLoading a LR-images and HR-images...{}    ({} / {})".format(prc[cnt%4], cnt, len(os.listdir(data_dir))), end='')

    print("\rLoading a LR-images and HR-images...Done    ({} / {})".format(cnt, len(os.listdir(data_dir))), end='')

    #Normalisierung
    lr_imgs = tf.convert_to_tensor(lr_imgs, np.float32)
    lr_imgs /= 255
    hr_imgs = tf.convert_to_tensor(hr_imgs, np.float32)
    hr_imgs /= 255
    
    print("\n___Successfully completed\n")

    return lr_imgs, hr_imgs

# PSNR,Ausgabe des Verlustwertdiagramms
def graph_output(history):
    
    #PSNR-Grafik
    plt.plot(history.history['psnr'])
    plt.title('Model PSNR')
    plt.ylabel('PSNR')
    plt.xlabel('Epoch')
    plt.legend(['Train'], loc='upper left')
    plt.show()  

    #Verlustwertdiagramm
    plt.plot(history.history['loss'])
    plt.title('Model loss')
    plt.ylabel('Loss')
    plt.xlabel('Epoch')
    plt.legend(['Train'], loc='upper left')
    plt.show()

def main():

    #Erstellen Sie Befehlszeilenoptionen
    parser = arg.ArgumentParser(description='Super-resolution CNN training')
    parser.add_argument('--data_dir', '-d', type=str, default=None,
                        help='Angeben des Bildordnerpfads(Fehler, wenn nicht angegeben)')
    parser.add_argument('--out', '-o', type=str,
                        default=os.path.dirname(os.path.abspath(__file__)),
                        help='Geben Sie das Speicherziel der Parameter an(Standardwert=./srcnn.h5')
    parser.add_argument('--batch_size', '-b', type=int, default=32,
                        help='Angeben der Mini-Stapelgröße(Standardwert=32)')
    parser.add_argument('--epoch', '-e', type=int, default=3000,
                        help='Angabe der Anzahl der Lernvorgänge(Standardwert=3000)')
    parser.add_argument('--he', '-he', type=int, default=256,
                        help='Ändern Sie die Größenangabe(Standardwert=256)')      
    parser.add_argument('--wi', '-wi', type=int, default=256,
                        help='Geben Sie die Größenänderung an(Standardwert=256)')
    parser.add_argument('--mag', '-m', type=int, default=2,
                        help='Angabe des Untersetzungsverhältnisses(Standardwert=2)')                           
    args = parser.parse_args()

    #Bildordnerpfad nicht angegeben->Ausnahme
    if args.data_dir == None:
        print("\nException: Folder not specified.\n")
        sys.exit()
    #Wenn ein nicht vorhandener Bildordner angegeben wird->Ausnahme
    if os.path.exists(args.data_dir) != True:
        print("\nException: Folder \"{}\" is not found.\n".format(args.data_dir))
        sys.exit()
    #Wenn 0 entweder in der Breitenhöhe oder in der Verkleinerungsvergrößerung eingegeben wird->Ausnahme
    if args.he == 0 or args.wi == 0 or args.mag == 0:
        print("\nInvalid value has been entered.\n")
        sys.exit()

    #Ausgabeordner erstellen(Nicht erstellen, wenn der Ordner vorhanden ist)
    os.makedirs(args.out, exist_ok=True)
    out_path = os.path.join(args.out, "srcnn.h5")

    #Informationsausgabe einstellen
    print("=== Setting information ===")
    print("# Images folder: {}".format(os.path.abspath(args.data_dir)))
    print("# Output folder: {}".format(out_path))
    print("# Minibatch-size: {}".format(args.batch_size))
    print("# Epoch: {}".format(args.epoch))
    print("")
    print("# Height: {}".format(args.he))
    print("# Width: {}".format(args.wi))
    print("# Magnification: {}".format(args.mag))
    print("===========================\n")

    #Datensatzerstellung
    lr_imgs, hr_imgs = create_dataset(args.data_dir, args.he, args.wi, args.mag)
    
    #Fang an zu lernen
    print("___Start training...")
    Trainer = trainer(args.he, args.wi)
    his, model = Trainer.train(lr_imgs, hr_imgs, out_path=out_path, batch_size=args.batch_size, epochs=args.epoch)

    # PSNR,Ausgabe des Verlustwertdiagramms
    graph_output(his)

if __name__ == '__main__':
    main()

Ausführungsergebnis

Die Anzahl der Epochen beträgt 3000 und die Mini-Chargengröße 32.

Die folgende Grafik ist eine Aufzeichnung des PSNR (Peak Signal to Noise Ratio). Details werden später beschrieben. PSNR 30db ist die Decke. .. ..

image.png Die folgende Grafik zeigt die Verlustwerte. image.png

Beachten Sie, dass diese Diagramme nicht gespeichert werden.

Befehl python arcnn_tr.py -d <Ordner> -e <Anzahl der Lernvorgänge> -b <Batchgröße> (-o <Ziel speichern> -he <Höhe> -wi <Breite> -m <Verkleinerungsvergrößerung (Ganzzahl)>)

Erläuterung

Ich werde den Code erklären.

Netzwerkmodell

Das Netzwerkmodell unterscheidet sich nicht wesentlich von einem normalen CNN.

SRCNN-Klasse


# SRCNN
class SRCNN(tf.keras.Model):

    def __init__(self, h, w):
        super(SRCNN, self).__init__()

        self.conv1 = kl.Conv2D(64, 3, padding='same', activation='relu', input_shape=(None, h, w, 3))
        self.conv2 = kl.Conv2D(32, 3, padding='same', activation='relu')
        self.conv3 = kl.Conv2D(3, 3, padding='same', activation='relu')

    def call(self, x):
        
        h1 = self.conv1(x)
        h2 = self.conv2(h1)
        h3 = self.conv3(h2)

        return h3

Der Unterschied zum üblichen CNN besteht darin, dass der Ausgangskanal im Allgemeinen immer größer wird. Im Fall von SRCNN ist der Punkt, dass der Ausgangskanal allmählich reduziert wird.

Die Faltschicht besteht im Allgemeinen aus drei Schichten.

Die erste Ebene ** führt eine Patch-Extraktion und eine spärliche Darstellung in einem Raum mit niedriger Auflösung ** durch. Die zweite Schicht ** führt eine nichtlineare Abbildung auf den hochauflösenden Raum der in der ersten Schicht ** erfassten Darstellung durch. Die dritte Schicht ** rekonstruiert das hochauflösende Bild **. (Aus dem [Intern CV Report] Exploring the History of Super Resolution -2016 Edition-)

** Sparse Expression ** (Sparse Coding) dient zur Erstellung eines Wörterbuchs zum Ausdrücken von Daten und zum Ausdrücken von Daten mit möglichst wenigen Elementkombinationen **. (Aus [Sparse Coding] Vorteile der Sparse Data Representation)

Einfacher ausgedrückt ist der spärliche Ausdruck, wie realistisch er sein kann (ungefähre Genauigkeit), indem eine kleine Anzahl von Feature-Maps in Bezug auf das Eingabebild kombiniert wird. Das Kombinieren vieler Feature-Maps verbessert tendenziell die Genauigkeit der Approximation, aber der spärliche Ausdruck traut sich nicht dazu, und ein aussagekräftiger Ausdruck kann mithilfe einer kleinen Anzahl von Elementen extrahiert werden. Mit anderen Worten, es wird gesagt, dass ** klarstellen, welche Elemente nützlich sind und wie nützlich die Darstellung der Daten ist **.


Datensatzerstellung

** Sie benötigen nur hochauflösende Bilder **. Bilder mit niedriger Auflösung werden aus Bildern mit hoher Auflösung erstellt.

create_Datensatzfunktion


#Datensatzerstellung
def create_dataset(data_dir, h, w, mag):

    print("\n___Creating a dataset...")
    
    prc = ['/', '-', '\\', '|']
    cnt = 0

    #Anzahl der Bilddaten
    print("Number of image in a directory: {}".format(len(os.listdir(data_dir))))

    lr_imgs = []
    hr_imgs = []

    for c in os.listdir(data_dir):

        d = os.path.join(data_dir, c)

        _, ext = os.path.splitext(c)
        if ext.lower() == '.db':
            continue
        elif ext.lower() != '.bmp':
            continue

        #Lesen, Größe ändern(Hochauflösendes Bild)
        img = cv2.imread(d)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.resize(img, (h, w))

        #Bild mit niedriger Auflösung
        img_low = cv2.resize(img, (int(h/mag), int(w/mag)))
        img_low = cv2.resize(img_low, (h, w))

        lr_imgs.append(img_low)
        hr_imgs.append(img)

        cnt += 1

        print("\rLoading a LR-images and HR-images...{}    ({} / {})".format(prc[cnt%4], cnt, len(os.listdir(data_dir))), end='')

    print("\rLoading a LR-images and HR-images...Done    ({} / {})".format(cnt, len(os.listdir(data_dir))), end='')

    #Normalisierung
    lr_imgs = tf.convert_to_tensor(lr_imgs, np.float32)
    lr_imgs /= 255
    hr_imgs = tf.convert_to_tensor(hr_imgs, np.float32)
    hr_imgs /= 255
    
    print("\n___Successfully completed\n")

    return lr_imgs, hr_imgs

Laden Sie zuerst das Bild. Beim Lesen mit OpenCV ist die Pixelanordnung also BGR Mit "cv2.cvtColor (img, cv2.COLOR_BGR2RGB)" in RGB konvertieren. Danach wird die Größe auf die angegebene Größe angepasst. Jetzt ist das hochauflösende Bild vorerst fertig.

Erstellen Sie dann ein Bild mit niedriger Auflösung. Reduziert auf die Breite und Höhe geteilt durch das angegebene Reduktionsverhältnis. Ändern Sie danach die Größe auf die Größe, bevor sie verkleinert wurde, um die Erstellung abzuschließen.

        #Lesen, Größe ändern(Hochauflösendes Bild)
        img = cv2.imread(d)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.resize(img, (h, w))

        #Bild mit niedriger Auflösung
        img_low = cv2.resize(img, (int(h/mag), int(w/mag)))
        img_low = cv2.resize(img_low, (h, w))

Es sieht so aus, wenn man ein Diagramm verwendet. image.png

Standardmäßig verwendet der OpenCV-Interpolationsalgorithmus "cv2.resize ()" Bilinear.


Lernen

Richten Sie ein und lernen Sie, bevor Sie in der Trainerklasse maschinell lernen. ~~ Ich glaube nicht, dass ich in Tensorflow oft "Trainer" sage, aber ich bin eine Person, die mit Chainer angefangen hat ... ~~

Zunächst werde ich die Instanzmethode erläutern.

Trainerklasse(Instanzmethode)


#Lernen
class trainer(object):

    def __init__(self, h, w):
        
        self.model = SRCNN(h, w)
        
        self.model.compile(optimizer=tf.keras.optimizers.Adam(),
                           loss=tf.keras.losses.MeanSquaredError(),
                            metrics=[self.psnr])

Rufen Sie beim Erstellen einer Instanz die Instanzmethode __init __ auf, um den Algorithmus zur Erstellung und Optimierung des Netzwerkmodells zu bestimmen. Mit "self.model = SRCNN (h, w)" werden die Höhen- und Breiteninformationen an die Instanzmethode der SRCNN-Klasse übergeben. Sobald das Modell erstellt ist, legen Sie den Optimierungsalgorithmus und die Verlustfunktion im Modell fest. Diesmal ist der Optimierungsalgorithmus Adam **. Verwenden Sie den durchschnittlichen quadratischen Fehler ** für die ** Verlustfunktion.

Die Logik ist, dass ** das Bild mit niedriger Auflösung sich allmählich dem Bild mit hoher Auflösung nähert **, indem der durchschnittliche quadratische Fehler zwischen dem Bild mit niedriger Auflösung und dem Bild mit hoher Auflösung ** ermittelt und der Wert ** verringert wird.

In metrics = [self.psnr] wird PSNR in der Auswertungsfunktion festgelegt. Details werden später beschrieben.


Als nächstes folgt die Erklärung der Zugmethode.

Trainerklasse(Zugmethode)


    def train(self, lr_imgs, hr_imgs, out_path, batch_size, epochs):

        #Lernen
        his = self.model.fit(lr_imgs, hr_imgs, batch_size=batch_size, epochs=epochs)

        print("___Training finished\n\n")

        #Parameter speichern
        print("___Saving parameter...")
        self.model.save_weights(out_path)
        print("___Successfully completed\n\n")

        return his, self.model

Übergeben Sie das Bild "lr_imgs" mit niedriger Auflösung als Trainingsdaten und das Bild "hr_imgs" mit hoher Auflösung als korrekte Antwortbezeichnung an "self.model.fit ()" Fang an zu lernen. Speichern Sie die Parameter, sobald das Training abgeschlossen ist.


Abschließend werde ich die PSNR-Methode erläutern.

** PSNR ** (Spitzensignal-Rausch-Verhältnis) ist ein ** Bewertungsindex **, der eine Bildverschlechterung anzeigt, die als ** Spitzensignal-Rausch-Verhältnis ** bezeichnet wird. Was ist das Spitzensignal oder Rauschen? Sie mögen denken, aber es tut mir leid, ich bin kein Spezialist, also kann ich es nicht erklären.

Die Einheit dieser Metrik ist "db (Dezibel)". Im Allgemeinen scheint ** PSNR 30db oder mehr schön auszusehen **. Bitte beachten Sie jedoch, dass ** menschliche Wahrnehmung und PSNR-Wert nicht immer übereinstimmen **.

Ich werde die Definitionsformel setzen.

PSNR = 10 \log_{10}\frac{MAX^2}{MSE}\qquad(1.1)

$ MSE $ ist der durchschnittliche quadratische Fehler.

MSE = \frac{1}{n} \sum_{i=1}^{n} (SR_i - HR_i)^2\qquad(2)

$ MAX $ ist ** der Maximalwert, den ein Pixel annehmen kann **, aber da es durch 255 geteilt und auf 0 zu 1 normiert wird, ist ** $ MAX $ (Maximalwert) 1 **. Ersetzen von $ MAX $ durch 1 in Gleichung (1.1)

PSNR = 10 \log_{10}\frac{1}{MSE}\qquad(1.2)

Und durch die logarithmische Umrechnungsformel des Quotienten

PSNR = -10 \log_{10}MSE\qquad(1.3)

Es wird sein. Dieses Mal verwenden wir "tf.keras.backend.flatten ()" bei der Berechnung des durchschnittlichen quadratischen Fehlers $ MSE $. Wenn Sie eine Funktion "tf.keras.backend" verwenden, können Sie die Funktion "numpy" nicht verwenden. Ich bekomme eine Fehlermeldung. Das Protokoll in der Formel verwendet also "tf.keras.backend.log ()", ein natürliches Logarithmus und kein reguläres Logarithmus. Daher ist Gleichung (1.3)

PSNR = -10 \frac{\ln MSE}{\ln 10}\qquad(1.4)

Es ist notwendig, die Gleichung unter Verwendung der Transformationsformel des Bodens wie in Gleichung (1.4) zu transformieren.

Ich schäme mich zu sagen, dass tf.keras.backend.log () kein regulärer Logarithmus ist, und ich bin nicht gut in Mathematik, daher konnte ich nicht herausfinden, warum diese Formeltransformation in der Literatur auftreten würde. Daher wird als Memorandum der Zustand der Formeltransformation auf diese Weise ausführlich beschrieben.

Der Code für Gleichung (1.4) ist unten gezeigt.

Trainerklasse(PSNR-Methode)


    # PSNR(Spitzensignal-Rausch-Verhältnis)
    def psnr(self, h3, hr_imgs):
        
        return -10*K.log(K.mean(K.flatten((h3 - hr_imgs))**2))/np.log(10)

K.mean (K.flatten ((h3 --hr_imgs)) ** 2 entspricht $ MSE $ (mittlerer quadratischer Fehler). Der Nenner ist der Logarithmus von numpy, aber dies ist auch ein natürlicher Logarithmus. Wenn ich aus irgendeinem Grund versuche, das Nennerprotokoll auf "tf.keras.backend.log ()" zu setzen, wird eine Fehlermeldung angezeigt. Ich wundere mich warum?

Nun, ich konnte die PSNR-Formel so definieren. Übrigens, wenn $ SR_i $ und $ HR_i $ in Gleichung (2) dasselbe Bild sind, ist es PSNR $ + ∞ $ db.


abschließend

Ich habe versucht, eine einfache SRCNN-Implementierungsmethode in der Super-Resolution-Technologie zu erklären, aber wie war es? In der nächsten "Super Resolution Technology-SRCNN-implementierten (Tensorflow 2.0) Prediction Phase Edition" werde ich es tatsächlich zu einer Super Resolution machen.

Recommended Posts

Super-Resolution-Technologie-SRCNN-I hat versucht, sie zu implementieren (Tensorflow 2.0) Lernphase
Super-Resolution-Technologie-SRCNN-I hat versucht, diese (Tensorflow 2.0) Vorhersagephase zu implementieren
Maschinelles Lernen (TensorFlow) + Lotto 6
Versuchen Sie es mit TensorFlow
Versuchen Sie es mit TensorFlow Part 2