Python Performanceprobleme (mit numpy)

Mort626

Lieutenant
Registriert
Feb. 2013
Beiträge
715
Hallo,

ich arbeite für ein Uniprojekt gerade an einem Analysetool für JPEG-Bilder. Dafür müssen der Encoder und Decoder manuell implementiert werden. Die Funktion steht erstmal, aber mit Performance aus der Hölle.

Beispiel: Für die Farbraumtransformation von RGB nach YCbCr soll diese Formel genutzt werden (siehe Code):
Screenshot_2020-05-05 JPEG – Wikipedia.png

Nun war die Idee, mathematische Operationen müssten mit Numpy schneller gehen als mit den Standard-Listen von Python. Die Matrixmultiplikation (in version 2) läuft an sich auch 10x schneller, allerdings ist die gesamtfunktion umgekehrt 10x langsamer als version 1. Also vermtlich irgendwo im Zugriff auf die Daten ist etwas faul, was lange dauert.
Kann mir jemand sagen woran das liegen könne?
Python:
# Version 1
            #"for d in img_e_data" returns a tupel [r,g,b] for each pixel
            y_img = [round(0.299*d[0] + 0.587*d[1] + 0.114*d[2])"]
            cb_img = [round(-0.169*d[0] - 0.331*d[1] + 0.5*d[2] +128) for d in img_e_data]
            cr_img = [round(0.5*d[0] - 0.419*d[1] - 0.081*d[2] +128) for d in img_e_data]
           
          
            #version 2
            #Transformation matrix for rgb -> ycbcr
            trafo_table = np.array([
            [0.299,0.587,0.114],
            [-0.169,-0.331,0.5],
            [0.5,-0.419,-0.081]])

            y_arr = np.zeros((self.height_e, self.width_e), dtype=np.uint8)
            cb_arr = np.zeros((self.height_e, self.width_e), dtype=np.uint8)
            cr_arr = np.zeros((self.height_e, self.width_e), dtype=np.uint8)

            for i in range(self.height_e):
                for j in range(self.width_e):
                    #extract rgb values from each pixel
                    pxl = img_e_data[i,j]
                    rgb = np.array([pxl[0],pxl[1],pxl[2]])
                    #matrix multiplikation with transformation matrix
                    ycbcr = np.dot(trafo_table, rgb)
                    #normalize color channels
                    y_arr[i,j]=round(ycbcr[0])
                    cb_arr[i,j]=round(ycbcr[1])+128
                    cr_arr[i,j]=round(ycbcr[2])+128
      
                     return y_arr, cb_arr, cr_arr
 
ohne Erfahrung mit Matrizen in Python zu haben:

Wenn ich das richtig versteh, dauern beide gleich lang? dann schau dir mal beim Zeit stoppen auch den doppelten for-loop an.

ich weiß zwar nicht, wie img_e_data aussieht, aber in version 2 wird halt der doppelte for-loop die dauer ausmachen, da du jedes Matrix/Array element extra ansprichst, während bei version1 direkt alle Pixel berechnet werden, aber halt in einer "Rechnung" sozusagen.
 
  • Gefällt mir
Reaktionen: Mort626
Könnte u.a. am round liegen. Dir ist klar, dass du da (imHo) auf die nächste (gerade) Ganzzahl rundest?
Aber ich würde sowieso die Loops in beiden Versionen weglassen. Bau dir eine große Matrix und multipliziere dann einfach. Bin nicht so super fit in python, aber so wie du da indizierst sieht das so aus als wenn du die Matrix schon hast und gar nichts bauen musst. Ggf. die Dimensionen permutieren und halt auf numpy drauf werfen.

Das dürfte ziemlich schnell sein dann.
 
  • Gefällt mir
Reaktionen: Mort626 und Rossibaer
Geschachtelte For-Schleife ist O(n^2), daher die miese Performance. Ansonsten siehe Mercurys Antwort.
 
Dein Code ist falsch formatiert.
Kannst du bitte 2 Code tags nutzen?
Dann kann ich es gerne mal anschauen.


Erster Gedanke wenn ich Idee 1 richtig interpretiere:
Teste mal was in Richtung von diesen 3-Zeiler:
Python:
trafo_table = np.array([
            [0.299,0.587,0.114],
            [-0.169,-0.331,0.5],
            [0.5,-0.419,-0.081]])

for d in img_e_data:
    (y_img, cb_img, cr_img) = (0,128,128) + trafo_table.dot(d)

Obige schreibweise ist ganz nebenbei auch trivial zu parallelisieren.
Einfach jeweils 1/N Zeilen an jeden Kern geben.

Noch effizienter dürftest du mit einer Convolution/Faltung sein.
Dafür müsste ich aber wissen wie dein img_e_data aussieht.
Daher wie oben gesagt bitte nochmal getrennt formatieren und etwas mehr Hintergrundinfos geben.
Die Performance bekommen wir auf jeden Fall hin, dass sollte selbst in Python kein Problem sein.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Mort626 und BeBur
Guck dir mal numpy.tensordot oder numpy.einsum an. Damit sollte die Matrixmultiplikation ein Einzeiler sein.
Dann kannst du anschließend noch den konstanten Vektor drauf addiern für das gesamte Array. Musst aber möglicherweiße mit das Bild transponieren, damit es von den Achsen her passt.
 
Schau mal ob das was wird ...

Python:
import numpy as np

# erstmal paar Daten zum spielen generieren
width = 2000
height = 3000
bytes_per_pixel = 3
img_e_data = np.reshape(np.round(np.random.rand(width * height, bytes_per_pixel) * 256), (width * height, bytes_per_pixel))
print(img_e_data)

Python:
# in #1 beim Berechnen von y_img fehlt das "for d in img_e_data" (Copy&Paste Bug?)
y_img = [round(0.299*d[0] + 0.587*d[1] + 0.114*d[2]) for d in img_e_data]
cb_img = [round(-0.169*d[0] - 0.331*d[1] + 0.5*d[2] +128) for d in img_e_data]
cr_img = [round(0.5*d[0] - 0.419*d[1] - 0.081*d[2] +128) for d in img_e_data]

# wieder zurück in ein 3D-Array (width, height, bytes_per_pixel) - evtl. nicht notwendig?
ycbcr_1 = np.reshape(np.array([y_img, cb_img, cr_img]), (width * height * bytes_per_pixel), order='F').reshape(width, height, bytes_per_pixel)
print(ycbcr_1)

Python:
# meine Variante #3
# initialer Kram, bestimmt optimierbar
base_trafo_values = np.array([[0.299, 0.587, 0.114, -0.169, -0.331, 0.5, 0.5, -0.419, -0.081]])
trafo_table = np.repeat(base_trafo_values, width * height, axis=0)
trafo_matrices = np.reshape(trafo_table, (width * height, bytes_per_pixel, bytes_per_pixel))
img_matrices = np.reshape(np.repeat(img_e_data, 3, axis=0), (width * height, bytes_per_pixel, bytes_per_pixel))
# sicher optimierbar, statt multiply und sum einfach nur np.dot oder np.tensordot? ich raffe es momentan nicht ...
values = np.multiply(trafo_matrices, img_matrices)
values = np.sum(values, axis=2)
# die restlichen Berechnungen (Runden + Addition)
values = np.round(values)
ycbcr_3 = np.add(values, np.array([.0, 128.0, 128.0]))

# wieder zurück in ein 3D-Array (width, height, bytes_per_pixel) - evtl. nicht notwendig?
ycbcr_3 = np.reshape(ycbcr_3, (width, height, bytes_per_pixel))
print(ycbcr_3)
 
  • Gefällt mir
Reaktionen: Mort626 und blöderidiot
Mort626 schrieb:
Dafür müssen der Encoder und Decoder manuell implementiert werden.
Du hast nicht erwähnt "warum"! Ich vermute, ihr wollt auf dem Bild sowas wie (farb/helligkeits-abhängigen) Histogramm-Ausgleich machen? Vielleich könntest Du das näher erläutern. Und: warum nicht in OpenCV?
 
Rossibaer schrieb:
Schau mal ob das was wird ...
[...]

Python:
# meine Variante #3
# initialer Kram, bestimmt optimierbar
base_trafo_values = np.array([[0.299, 0.587, 0.114, -0.169, -0.331, 0.5, 0.5, -0.419, -0.081]])
trafo_table = np.repeat(base_trafo_values, width * height, axis=0)
trafo_matrices = np.reshape(trafo_table, (width * height, bytes_per_pixel, bytes_per_pixel))
img_matrices = np.reshape(np.repeat(img_e_data, 3, axis=0), (width * height, bytes_per_pixel, bytes_per_pixel))
# sicher optimierbar, statt multiply und sum einfach nur np.dot oder np.tensordot? ich raffe es momentan nicht ...
values = np.multiply(trafo_matrices, img_matrices)
values = np.sum(values, axis=2)
# die restlichen Berechnungen (Runden + Addition)
values = np.round(values)
ycbcr_3 = np.add(values, np.array([.0, 128.0, 128.0]))

# wieder zurück in ein 3D-Array (width, height, bytes_per_pixel) - evtl. nicht notwendig?
ycbcr_3 = np.reshape(ycbcr_3, (width, height, bytes_per_pixel))
print(ycbcr_3)
Ja, so geht das. Den Anfang würde man vermutlich nicht optimieren in diesem Kontext.
Mit multiply und sum hat die Umsetzung auch die entsprechend hohe Ausdrucksstärke davon ausgehend, dass der Leser die Formel des TE im Kopf hat beim lesen. Da würde ich nur bei echter Notwendigkeit weiter optimieren.

Bleibt die Frage offen, warum ihr hier fertige Lösungen für Hausaufgaben bzw. Uni-Hausaufgaben abliefert. Pseudo-Code wenn man denn schon will hätte es ja auch getan.
 
Bleibt die Frage offen, warum ihr hier fertige Lösungen für Hausaufgaben bzw. Uni-Hausaufgaben abliefert. Pseudo-Code wenn man denn schon will hätte es ja auch getan.

1. Hatte es mich heute morgen vor meinem ersten Kaffee mal selber interessiert, ob ich in der Lage wäre, das als Python Anfänger auch zu lösen...
2. Gegenfrage: Wenn ich dann eine Lösung habe, warum dann nicht teilen?
3. Das es eine Hausaufgabe bzw. Uni-Hausaufgabe ist, sehe ich so nicht gegeben. Es war nur eine Rede von ...für ein Uniprojekt... Das ist aus meiner Sicht eine Frage der Interpretation, da mir nicht bekannt ist, ob er studiert oder vielleicht festangestellt ist, aber hey - da kenne ich mich nicht aus.
4. Thema "Eigenleistung": sehe ich für mich als erfüllt, da der TE bereits 2 Varianten hatte und diese hier mit uns teilte. Ist jedoch auch nur eine Deutung meinerseits, dass dieser Code auch wirklich von ihm stammt.

Wenn einer einfach nur einen Screenshot oder noch besser ein mieses Handyfoto von einer Aufgabenstellung aus seinem Lehrbuch hier reinballert mit der freundlichen Aufforderung "Macht mal!" ohne Abschlussformel, dann wird dieser Thread recht schnell und zuverlässig geschlossen. Deswegen auch ein Dankeschön an diejenigen, die dieses Forum betreuen.

Am Ende bleibt dem Ganzen noch der fade Beigeschmack, dass ich mit meiner Variante und meinen Fehlinterpretationen wohl doch in ein Fettnäpfchen getreten bin und damit dunkle Geister rief...

Bleibt nur zu sagen: 12 Jahre ist eine lange Zeit, schön und vollmundig am Anfang, gut gereift im mittleren Abschnitt und dann zu lange stehen gelassen. Tschau Computerbase, war schön so lange, wie es gedauert hatte.
 
  • Gefällt mir
Reaktionen: Mort626, Kanibal und pumuck|
Rossibaer schrieb:
3. Das es eine Hausaufgabe bzw. Uni-Hausaufgabe ist, sehe ich so nicht gegeben. Es war nur eine Rede von ...für ein Uniprojekt... Das ist aus meiner Sicht eine Frage der Interpretation, da mir nicht bekannt ist, ob er studiert oder vielleicht festangestellt ist, aber hey - da kenne ich mich nicht aus.

...

Bleibt nur zu sagen: 12 Jahre ist eine lange Zeit, schön und vollmundig am Anfang, gut gereift im mittleren Abschnitt und dann zu lange stehen gelassen. Tschau Computerbase, war schön so lange, wie es gedauert hatte.
Bleib mal schön hier! 12 Jahre? Sehr lange bist Du ja noch gar nicht da, was soll ich da sagen ...

Das ist sicher keine "Hausaufgabe" gewesen und wenn - eine Lösung wurde vom TE, wie erforderlich, auch vorgestellt. Also, sei mal bitte nicht so empfindlich - nur weil ein anderer treuer CB'ler, der hier noch gar nicht so lange dabei ist, mal etwas zu laut geworden ist. Alle anderen (mich eingeschlossen) haben sich mit hoher Wahrscheinlichkeit über Deine Implementation gefreut.
 
  • Gefällt mir
Reaktionen: Kanibal, Rossibaer und BeBur
Sehr lange bist Du ja noch gar nicht da, was soll ich da sagen ...

Stimmt, ich musste schmunzeln, als ich das gelesen habe. Im Grunde ist das ja der Unterschied zwischen einem 6 Klässler und einem, der in der 10. Klasse vor der Prüfung steht... :D Abgesehen davon, hast du recht ... ich sollte nicht so empfindlich reagieren. Ist nur sehr demotivierend hier zu sein, wo gefühlt jeder 2. Thread fast ungeprüft mit diesem "Hausaufgaben"-Kommentar versehen wird. Klar sind diese Threads ein großes Problem, jedoch wenn man sieht, dass der TE bereits seit 2013 hier ist, (Stand gestern) 683 Posts beigetragen hat, eine konkrete Frage stellt, eigene Leistung (noch dazu in 2 Varianten) zeigt, dann sind das halt für mich eher Hinweise, dass es hier sich nicht um einen Newbie mit einem einzigen Hausaufgaben-Post handelt um mal schnell eine 1+ im Informatikunterricht abzugreifen.

Ich habe mir nochmal die einzelnen Kommentare hier im Thread genauer angeschaut und musste erfreut feststellen, dass BeBur quasi sehr ähnlich in Worten das umschrieben hatte, was ich eigentlich in meinem Code versucht habe abzubilden (Post #3)...

Ein paar Anmerkungen dazu:
Das "Round" ist meiner Meinung nach nicht das eigentliche Problem (wohl eher ein Nebenschauplatz) an der 2. Variante, da es erstmal nur ein einzelner Aufruf einer Funktion ist, die nicht wirklich viel Spannendes macht. Genaueres kann man sagen, wenn man sich deren Implementierung einmal anschaut, gefühlt sind es jedoch nur einige wenige simple Zeilen, siehe z.B. hier https://github.com/python/cpython/blob/master/Python/pymath.c
Auch der Punkt, wie die Rundung ausgeführt wird, ob aufwärts, abwärts, kaufmännisch oder doch ganz anders, ist nicht so spannend, da es u.U. einfach eine Anforderung in diesem Projekt sein kann und entsprechend ganz bewusst so gewählt wurde. Da kann der TE evtl. mehr dazu sagen...

Wo BeBur und weitere Teilnehmer in diesem Thread dann jedoch bereits den schmutzigen Finger in die blutige Wunde steckten, sind auch meiner Meinung nach das eigentliche Problem die 2 geschachtelten for Schleifen, wodurch die Stärke von numpy gegenüber normalem Python-Code ganz elegant ausgehebelt wird. Wenn ich diese Berechnungen für jedes Pixel einzeln in 2 mehr oder weniger große for Schleifen packe, läuft schlicht der Python-Interpreter diese Zeilen immer wieder durch. Wenn ich stattdessen auf die for Schleifen ganz verzichte und "nur" ein paar numpy Funktionen nacheinander aufrufe, dann wird abgesehen von den wenigen Zeilen Python-Code selbst schlicht nativ kompilierter Code ausgeführt, was schon mal einen Geschwindigkeitsvorteil bringen wird. Desweiteren gibt es im Numpy diverse Optimierungen, wo unter anderem die Berechnungen parallel ausgeführt werden, die Typen an sich optimiert sind etc. Damit ich diese Features in dem konkreten Fall nutzen konnte, habe ich halt diesen Init-Kram am Anfang gemacht, wo ich mit np.repeat etc. die Arrays so in Form bringe, damits auch ohne Schleifen läuft. Im Endeffekt ist es jedoch immer noch langsam, da hier Numpy auf der CPU ausgeführt wird. Gibt soweit ich mich noch erinnern kann auch Möglichkeiten und Alternativen, wo quasi diese Funktionen auf die GPU gebracht werden können.

Das führt mich dann nochmal zu dem Post von ...
@blöderidiot: Wäre echt cool von Dir, wenn du für alle hier eine Version mit dem von Dir erwähnten OpenCV posten könntest. Es muss nicht 100% perfekt sein oder vollständig lauffähig. Mir persönlich gehts nur darum Unterschiede zur Numpy-Variante zu sehen und etwas Neues zu lernen. Für den TE hätte es den Vorteil auch eine echte Alternative zu sehen um dann vielleicht nochmal die Entscheidung zugunsten Numpy zu überdenken.
Und wenn du mich schon durch sehr gezielte Worte dazu bewegen konntest, doch hier zu bleiben, dann will ich mal versuchen dich hier im Thread zu halten... ;-) Alles basiert natürlich auf Freiwilligkeit, kein Zwang, kein Muss.


PS: Nicht jeder Mensch lernt auf die gleiche Weise gleich effektiv. Implementierung ist stets aktueller, als die Dokumentation dessen es je sein kann. "RTFM!" ist zwar die einfachste und schnellste Variante um einen Fragesteller abzuwimmeln, jedoch ist es nicht immer die beste Lösung um einen interessierten Menschen an diese komplexe Materie oder gar Magie des Programmierens heranzuführen und erfolgreich anzufixen.
 
  • Gefällt mir
Reaktionen: kuddlmuddl, Mort626 und BeBur
Ich verwende für solche Benchmarks immer gerne das timeit-Package. Ich habe das für Dein Beispiel mal für drei Libraries (scikit-image, numpy und Pillow) ausprobiert und als Github Gist hochgeladen:
https://gist.github.com/ckanibal/abdfb28f093dbb8a1d299397be4cdb01
Falls jemand noch andere Möglichkeiten dafür kennt, gerne einen Pull-Request stellen ;-)

Spoiler: Pillow OpenCV ist am schnellsten.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: BeBur, blöderidiot, kuddlmuddl und eine weitere Person
blöderidiot schrieb:
Das ist sicher keine "Hausaufgabe" gewesen und wenn - eine Lösung wurde vom TE, wie erforderlich, auch vorgestellt. Also, sei mal bitte nicht so empfindlich - nur weil ein anderer treuer CB'ler, der hier noch gar nicht so lange dabei ist, mal etwas zu laut geworden ist. Alle anderen (mich eingeschlossen) haben sich mit hoher Wahrscheinlichkeit über Deine Implementation gefreut.
Man kann da ja unterschiedlicher Meinung sein und das ist völlig ok.

Rossibaer schrieb:
# initialer Kram, bestimmt optimierbar
  • Du kannst eine mxn und eine nxk Matrix multiplizieren, das Ergebnis ist eine mxk Matrix.
  • Die Generierung von img_matrices sieht falsch aus, wegen dem Repeat.
  • Imho reicht es wirklich aus ggf. die Dimensionen zu vertauschen mit transpose. Die Funktion garantiert auch keine Kopiervorgänge auf den Daten zu durchzuführen.

Rossibaer schrieb:
Das "Round" ist meiner Meinung nach nicht das eigentliche Problem (wohl eher ein Nebenschauplatz) an der 2. Variante, da es erstmal nur ein einzelner Aufruf einer Funktion ist, die nicht wirklich viel Spannendes macht. Genaueres kann man sagen, wenn man sich deren Implementierung einmal anschaut, gefühlt sind es jedoch nur einige wenige simple Zeilen, siehe z.B. hier https://github.com/python/cpython/blob/master/Python/pymath.c
Ja, kann gut sein. Evtl. wirklich einfach die Summe der overheads mit den ganzen mini-arrays, bzw. die doppelte Schleife an sich.
 
Zuletzt bearbeitet: (Aufzählungszeichen eingefügt)
  • Gefällt mir
Reaktionen: Mort626
Python:
kernel = np.array([[0.299, 0.587, 0.114], [-0.169, -0.331, 0.5], [0.5, -0.419, -0.081]])
%timeit Y = (image @ kernel + [0, 128, 128]).astype(np.uint8)

@Kannibal: Ich merke, ich muss noch viel lernen. Ein simpler 2 Zeiler und ich brech mir mit den repeats etc. einen ab! Echt Klasse von dir, was du da gezaubert hast. Das Round ist nicht mehr notwendig, wenn ich das richtig sehe, da du ja dann nach uint8 castest. Hätte da am liebsten deinen Beitrag gleich 2x geliked! Pillow kenne ich nicht, aber gut zu wissen... Danke dir für deinen Beitrag.
 
  • Gefällt mir
Reaktionen: Mort626
@Kanibal - OpenCV TAPI mit i7-5820K + Vega 56
opencvtapi.jpg

(Sorry, das auf GitHub hochzuschieben ist mir zu umständlich. Vielleicht kannst Du das hinzufügen? Danke!)
 
  • Gefällt mir
Reaktionen: Rossibaer
Erstmal danke für die vielen Antworten! War erstmal genug Input, um damit etwas zu experimentieren.

Aber eins nach dem anderen.
ZuseZ3 schrieb:
Dein Code ist falsch formatiert.
Kannst du bitte 2 Code tags nutzen?
Dann kann ich es gerne mal anschauen.


Erster Gedanke wenn ich Idee 1 richtig interpretiere:
Teste mal was in Richtung von diesen 3-Zeiler:
Python:
trafo_table = np.array([
            [0.299,0.587,0.114],
            [-0.169,-0.331,0.5],
            [0.5,-0.419,-0.081]])

for d in img_e_data:
    (y_img, cb_img, cr_img) = (0,128,128) + trafo_table.dot(d)

Obige schreibweise ist ganz nebenbei auch trivial zu parallelisieren.
Einfach jeweils 1/N Zeilen an jeden Kern geben.

Noch effizienter dürftest du mit einer Convolution/Faltung sein.
Dafür müsste ich aber wissen wie dein img_e_data aussieht.
Daher wie oben gesagt bitte nochmal getrennt formatieren und etwas mehr Hintergrundinfos geben.
Die Performance bekommen wir auf jeden Fall hin, dass sollte selbst in Python kein Problem sein.
Wie meinst du Convulotion?
blöderidiot schrieb:
Du hast nicht erwähnt "warum"! Ich vermute, ihr wollt auf dem Bild sowas wie (farb/helligkeits-abhängigen) Histogramm-Ausgleich machen? Vielleich könntest Du das näher erläutern. Und: warum nicht in OpenCV?
Am Ende soll eine Error level analysis (wikipedia) durchgeführt werden. Dafür muss das Bild erst komprimiert und dann wieder dekomprimiert werden. Ja, es ist Teil einer "Hausaufgabe", sonst würde man natürlich einfach eine fertige Bibliothek nutzen. OpenCV klingt schonmal interessant, danke.

Ich hab die Beispiele mal etwas ausführlicher gemacht. In Beispiel 2 erstelle ich zuerst drei Arrays, welche ich dann elementweise durchgehe (inspiriert von der Antowrt von ZuseZ3) und in die Ergebnisarrays schreibe. Allerdings ist die Version in Beispiel 2 immernoch deutlich langsamer.

Stichwort Parallelisierung: Steht auf der ToDo Liste. Wir hatten schon versucht numpy durch cupy oder numba zu ersetzen. Noch nicht ganz erfolgreich bisher.
For-Schleifen: Dachte ich mir schon. Die diskrete Cosinustransformation läuft 150x schneller, nachdem wir diese vier verschachtelten schleifen ( :freak: ) durch einen einzelnen Numpy-Befehl ersetzt haben. Aber nicht bei der rgb-ycbcr-Transformation...
Python:
from PIL import Image, ImageOps # Bibliothek zum Einlesen von Bilddateien und Bearbeiten des Bildes
import numpy as np # used for mathematical operations
# --input
infile = Image.open(args.input)
img_ext_data = infile.getdata()
 # alt
            y = np.array([round(0.299*d[0] + 0.587*d[1] + 0.114*d[2]) for d in img_ext_data])
            cb = np.array([round(-0.169*d[0] - 0.331*d[1] + 0.5*d[2] +128) for d in img_ext_data])
            cr = np.array([round(0.5*d[0] - 0.419*d[1] - 0.081*d[2] +128) for d in img_ext_data])
Python:
import numpy as np # used for mathematical operations
from PIL import Image, ImageOps # Bibliothek zum Einlesen von Bilddateien und Bearbeiten des Bildes
infile = Image.open(args.input)
img_ext = infile

img_ext_data_r = np.array(img_ext.getdata(band=0))
img_ext_data_g = np.array(img_ext.getdata(band=1))
img_ext_data_b = np.array(img_ext.getdata(band=2))
            
            kernel = np.array([
            [0.299,0.587,0.114],
            [-0.169,-0.331,0.5],
            [0.5,-0.419,-0.081]])

            # ergebniswerte
            y = np.empty(shape=len(img_ext_data))
            cb = np.empty(shape=len(img_ext_data))
            cr = np.empty(shape=len(img_ext_data))

            # eingangswerte
            # r,g,b = list of pixel values
            r = img_ext_data_r
            g = img_ext_data_g
            b = img_ext_data_b
            
            for i in range(len(img_ext_data)):
                (y[i],cb[i],cr[i]) = (0,128,128)+kernel@(r[i],g[i],b[i])
 
Zurück
Oben