Forum: Mikrocontroller und Digitale Elektronik Schnellere For-Schleife Open CV


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von EdB (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich hab ein kleines Problem mit einer Anwendung in OpenCV bei der ich 
ein Grauwertbild Pixel für Pixel bearbeiten will.

Die Pixel sollen anhand der Kriterien Position und Wert unterschieden 
werden und entsprechend zu weiß(255) oder schwarz(0) geändert werden.
Das ganze habe ich in Python umgesetzt.

Hierfür habe ich eine for-Schleife verwendet:

Beispiel:


for x in range(100):
    for y in range(100):
        if(bedingung):
           image[x,y]= 0 oder 255


Der Code funktioniert, aber braucht sehr viel Zeit.
Die Ausführung dauert einige Sekunden, selbst bei kleineren Bildern.

Gibt es eine Möglichkeit die einzelnen Pixel auf eine schnellere Weise 
abzufragen und zu ändern?

von M. K. (mkn)


Bewertung
-2 lesenswert
nicht lesenswert
C statt Python?

von Experte (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Und was ist 'Bedingung' genau?

von Cyblord -. (cyblord)


Bewertung
1 lesenswert
nicht lesenswert
Deine Schleife stellt offensichtlich den trivialen Algorithmus für dein 
Problem dar. Je nach konkreten Problem kann es effizientere Algorithmen 
geben.
Das ist keine Frage der Programmiersprache.

von EdB (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Experte schrieb:
> Und was ist 'Bedingung' genau?

Da wird überprüft ob der Pixelwert größer als eine Schwelle ist und ob 
er links oder rechts im Bild ist.

von Cyblord -. (cyblord)


Bewertung
0 lesenswert
nicht lesenswert
EdB schrieb:
> Experte schrieb:
>> Und was ist 'Bedingung' genau?
>
> Da wird überprüft ob der Pixelwert größer als eine Schwelle ist und ob
> er links oder rechts im Bild ist.

Wenn grundsätzlich alle Pixel einzeln angeschaut werden müssen, führt 
kein Weg an deinem Algo vorbei, die untere Schranke wäre dann bei O(n), 
und das macht deine Schleife bereits. (vgl. Sortierung, da muss 
mindestens jedes Element einmal betrachtet werden, darum kann es keinen 
Algo besser O(n) theoretisch geben).

von Nils (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ruf doch einfach die API Funktion dafür auf:

  https://docs.opencv.org/3.4/d7/d4d/tutorial_py_thresholding.html

von Imonbln (Gast)


Bewertung
1 lesenswert
nicht lesenswert
EdB schrieb:
> for x in range(100):
>     for y in range(100):

Dein Example sehr minimalistisch, aber "for x in range" ist in Python 
eigentlich immer falsch (okay es gibt ein paar exotische Ausnahmen), 
Leider kenne ich den Type von Image nicht aber nach ich bin mir ziemlich 
sicher das man über Image Iterieren kann. Alleine das Kann schon etwas 
Geschwindigkeit bringen.

von EdB (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nils schrieb:
> Ruf doch einfach die API Funktion dafür auf:
>
>   https://docs.opencv.org/3.4/d7/d4d/tutorial_py_thresholding.html

Ich frage aber mehr als nur den Threshold ab

von Dirk K. (d-k)


Bewertung
0 lesenswert
nicht lesenswert
In python benutzt openCV numpy als image container.

Deshalb sollte die numpy where function hier hilfreich sein.

https://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html
np.where(image < 5, 0, 255)
Die Bedingung kann natürlcih komplizierter sein. Wenn es nur das < ist 
gibt es dafür explizite openCV Funktionen.


Das schaut zwar immer noch jeden Pixel an (muss es ja), macht das aber 
optimiert.

: Bearbeitet durch User
von dodadi (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Imonbln schrieb:
> "for x in range" ist in Python
> eigentlich immer falsch

Warum das? range(100) erzeugt halt im Speicher eine Liste mit 100 
Einträgen. Müsste nicht sein, lieber xrange(), juckt bei heutigen 
Maschinen aber auch keinen und im Vergleich zum Speicherplatzbedarfs des 
Bildes ist das eh Kleinkram.

von Imonbln (Gast)


Bewertung
0 lesenswert
nicht lesenswert
dodadi schrieb:
> Warum das? range(100) erzeugt halt im Speicher eine Liste mit 100
> Einträgen. Müsste nicht sein, lieber xrange(), juckt bei heutigen
> Maschinen aber auch keinen und im Vergleich zum Speicherplatzbedarfs des
> Bildes ist das eh Kleinkram.

xrange ist Python2 und seit Anfang des Jahres damit eigentlich im EOL. 
Was das Iterieren angeht, der Hauptgrund warum es eigentlich immer 
Falsch ist eine "for x in range" zu schrieben, ist der das man 
eigentlich jedes Objekt (wo es sinnvol ist) seinen eigenen Iterator 
mitbringt. meisten Will man ja das nächste Objekt haben und nicht seinen 
Index kennen, was Python intern nutzen kann zum den Zugriff auf die 
nächste instance zu Optimieren.

Ausserdem geben seit Python3 viele Funktionen lieber ihren Iterator 
zurück als Speicher für alle Elemente zu verbrauchen z.b map.

von Teddy (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
EdB schrieb:
> Gibt es eine Möglichkeit die einzelnen Pixel auf eine schnellere Weise
> abzufragen und zu ändern?

Gibt es.
Es gibt 2 einfache Lösungen.

von Dirk K. (merciless)


Bewertung
0 lesenswert
nicht lesenswert
Es kann einen Unterschied machen, wie man iteriert:
for x in range(100):
  for y in range(100):
vs.
for y in range(100):
  for x in range(100):
Ausprobieren!

merciless

von Johannes O. (jojo_2)


Bewertung
0 lesenswert
nicht lesenswert
Zeig bitte mal etwas mehr Code.

- Du kannst aus Python auch C Code aufrufen, falls in dieser Anwendung 
akzeptabel. Das läuft oft deutlich schneller. Wenn du willst, kannst du 
auch auf ASM Ebene dann optimieren.
Für den C-Fall noch ein paar Tipps:
- Wo kommt die Bedingung her? Wird die on-the-fly berechnet? Ggf liegt 
hier auch ein Problem.
- Welche Datentypen verwendest du? Eventuell lassen sich mehrere Werte 
in einem Schritt abarbeiten (8-bit Pixel in 32-bit Registern)
- Loop unrolling mal probiert? Macht ggf hässlichen Code, kann aber 
schneller werden. Sprünge kosten üblicherweise viel Performance.

Für Python wurden schon genügend Optionen genannt

von M. K. (mkn)


Bewertung
-2 lesenswert
nicht lesenswert
Cyblord -. schrieb:
> Das ist keine Frage der Programmiersprache.

https://devopsworld.de/1133

Ist es nicht?

von Cyblord -. (cyblord)


Bewertung
-2 lesenswert
nicht lesenswert
M. K. schrieb:
> Cyblord -. schrieb:
>> Das ist keine Frage der Programmiersprache.
>
> https://devopsworld.de/1133
>
> Ist es nicht?

Nein weil die absolute Laufzeit für die Betrachtung eines Algorithmus 
keine Rolle spielt. Ist dir die Zeit zu lange, dann kannst du immer noch 
auf einem Supercomputer oder Cluster ausführen.
Ein sehr schlechter Algo bleibt sehr schlecht, auch wenn er ein wenig 
schneller in C als in Python ausgeführt ist. Erst wenn der Algo 
nachweislich optimal ist, kann man sich über solche Dinge wie Maschine, 
CPU oder Programmiersprache Gedanken machen.

: Bearbeitet durch User
von EdB (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Dirk K. schrieb:
> Es kann einen Unterschied machen, wie man iteriert:for x in range(100):
>   for y in range(100):
> vs.for y in range(100):
>   for x in range(100):
> Ausprobieren!
>
> merciless

Danke, aber das habe ich bereits getestet.



Johannes O. schrieb:
> - Wo kommt die Bedingung her? Wird die on-the-fly berechnet? Ggf liegt
> hier auch ein Problem

Die if-Anfrage wird in der Schleife ausgewertet. Bsp. if img[x,y]>100:
                                                           img[x,y]=255

Johannes O. schrieb:
> - Welche Datentypen verwendest du? Eventuell lassen sich mehrere Werte
> in einem Schritt abarbeiten (8-bit Pixel in 32-bit Registern)

uint8



Dirk K. schrieb:
> In python benutzt openCV numpy als image container.

Das habe ich auch bereits gefunden.
Laut anderen Beiträgen soll man mit numpy den größten Zeitgewinn 
erzielen.
Allerdings verstehe ich nicht so recht wie man damit arbeitet.

von M. K. (mkn)


Bewertung
0 lesenswert
nicht lesenswert
Cyblord -. schrieb:
> Ein sehr schlechter Algo bleibt sehr schlecht

Ja, da hast Du recht.
Aber ein sehr schlechter Algo läuft auf unterscheidlichen 
Programierspachen immer noch unterschiedlich schnell.

Ein sehr schlechter Algo in einer sehr schnellen Sprache geschrieben ist 
u.U. schneller als ein sehr guter Algo in Python.

Also verstehe ich Deinen Ansatz nicht, das die Sprache keine Rolle 
spielt.

Aber lassen wir das nicht zum Schlagabtausch werden.
Die laufen nicht so gut zwischen uns.
Also, hast Du recht und ich Unrecht und dabei belassen wir das.
Das hält mein Ego aus ;-)

von Dennsi W. (googoo)


Bewertung
0 lesenswert
nicht lesenswert
100x100 müsste selbst in Python fluppen. Da stimmt was nicht mit dem 
Zugriff auf ,,image", wird wohl jedesmal neu geladen oder so. Wäre wohl 
besser ein richtiges array/liste im Pythonstil zu haben, evtl. gibt da 
eine Funktion die ein solches zurückliefert, welches man dann 
zwischenspeichert in einer Variable....

von EdB (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Dennsi W. schrieb:
> 100x100 müsste selbst in Python fluppen

100x100 ist ein Beispielwert.

von Dennsi W. (googoo)


Bewertung
0 lesenswert
nicht lesenswert
> Der Code funktioniert, aber braucht sehr viel Zeit.
> Die Ausführung dauert einige Sekunden, selbst bei kleineren Bildern.

Wie klein?
Ersetze mal image durch ein erstelltes array gleicher Größe und 
vergleiche.

Und poste den code der sich hinter deiner Bedingungsfunktion verbirgt

von Cyblord -. (cyblord)


Bewertung
-4 lesenswert
nicht lesenswert
M. K. schrieb:

> Also, hast Du recht und ich Unrecht und dabei belassen wir das.
> Das hält mein Ego aus ;-)

Putzig. Schlagabtausch? Das wäre so als würde ich nem Hund einen Knochen 
hinwerfen und er mag ihn nicht. So ein Schlagabtausch wäre das. Und was 
das mit seinem Ego macht und ich dabei Recht habe, interessiert mich 
ungefähr ähnlich.

: Bearbeitet durch User
von EdB (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hier der gesamte Code:


import cv2
import numpy as np
import matplotlib.pyplot as plt


img = cv2.imread('/home/pi/Desktop/Bilder2/Bild1.png',0)


for x in range(img.shape[0]):
    for y in range(img.shape[1]):
        if(img[x,y]>180 or y>450):
            img[x,y]=255

cv2.imshow('Image',img)

cv2.waitKey(0)

cv2.destroyAllWindows()

von Asdf (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Damit dein code schneller ( viel schneller)  wird, führt kein Weg an der 
Benutzung der OpenCV Routinen bzw numpy vorbei. Bedeutet für Dich: Du 
musst Dich in beides einarbeiten, was nicht so schwer ist und Dir auch 
bei anderen Aufgabenstellungen helfen wird.

Der Code ist langsam, weil alles durch den python Interpreter läuft. Mit 
OpenCV/numpy würde das in optimierten (C-)Routinen ausgeführt. Ein 
Full-HD Bild sollte  mit Deiner Schwellenwertoperation auf einem 
aktuellen Rechner deutlich unter 100ms berechnet werden.

von EdB (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Asdf schrieb:
> Damit dein code schneller ( viel schneller)  wird, führt kein Weg an der
> Benutzung der OpenCV Routinen bzw numpy vorbei. Bedeutet für Dich: Du
> musst Dich in beides einarbeiten, was nicht so schwer ist und Dir auch
> bei anderen Aufgabenstellungen helfen wird.

Ok, ich habe schon eine Weile in der Richtung recherchiert aber noch 
nichts funktionierendes hinbekommen.

Weiß jemand wie man numpy für meine Aufgabe anwenden würde?

von Mike B. (mike_b97) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Cyblord -. schrieb:

> Nein weil die absolute Laufzeit für die Betrachtung eines Algorithmus
> keine Rolle spielt. Ist dir die Zeit zu lange, dann kannst du immer noch
> auf einem Supercomputer oder Cluster ausführen.

nicht gleich so dick auftragen
je nach Prozessor geht auch erstmal multithreading mittels OpenMP
auch unter Open CV
https://stackoverflow.com/questions/54466572/how-to-properly-multithread-in-opencv-in-2019

von Cyblord -. (cyblord)


Bewertung
-3 lesenswert
nicht lesenswert
Mike B. schrieb:
> je nach Prozessor geht auch erstmal multithreading mittels OpenMP

WENN dein Algorithmus parallelisierbar ist. Und selbst dann ist das 
immer aufwändiger als einfach die große Maschine rauszuholen, wenn das 
möglich ist.

Aber an 1. Stelle steht der Algorithmus selbst. Der ist das wichtigste. 
Alles andere ist nachgeordnet. Ich bin dagegen auf einen schlechten Algo 
mit dem Hammer Rechenleistung einzuschlagen.

: Bearbeitet durch User
von M. K. (mkn)


Bewertung
1 lesenswert
nicht lesenswert
Cyblord -. schrieb:
> und ich dabei Recht habe

Und das ist es ja, worum es geht, nicht wahr?
Ich würde ja auf Quarantänekoller bei Dir tippen, aber Du bist ja immer 
so.
Naja, Du must ja mit Dir leben.

von M. K. (mkn)


Bewertung
0 lesenswert
nicht lesenswert
Asdf schrieb:
> Der Code ist langsam, weil alles durch den python Interpreter läuft. Mit
> OpenCV/numpy würde das in optimierten (C-)Routinen ausgeführt.

Nein das kann nicht stimmen.
Denn Cyblord sagt das das völlig unabhängig von der Sprache ist, und 
Cyblord hat immer recht, sonst bekommt er Stresspickel, wirft sich auf 
den Boden und brüllt bis er einen Lutscher bekommt.
:-)))

von Imonbln (Gast)


Bewertung
0 lesenswert
nicht lesenswert
EdB schrieb:
> Weiß jemand wie man numpy für meine Aufgabe anwenden würde?

ungefähr so
import cv2


img = cv2.imread('/home/pi/Desktop/Bilder2/Bild1.png',0)

img[ img > 180] = 255 # dein img[x,y] > 180 in numpy parallel
img[ 0:img.shape[0], 450:] = 255 # dein y > 450 in numpy

img[ img <= 180] = 0 # bin nicht sicher ob du das einfach vergessen hast

cv2.imshow('Image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

von Mehlmilch (Gast)


Bewertung
0 lesenswert
nicht lesenswert
EdB schrieb:

> for x in range(img.shape[0]):
>     for y in range(img.shape[1]):
>         if(img[x,y]>180 or y>450):
>             img[x,y]=255

Das Ganze in zwei Konstrukte auflösen (je halbe Y-Auflösung). Einmal 
alle Y-Positionen größer 450 den Wert 255 zuweisen (Vorteil: kein if ) 
und einmal alle andere mit der Abfrage größer 180 bearbeiten (Vorteil: 
nur eine Abfrage).

von EdB (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Imonbln schrieb:
> ungefähr so
> import cv2
>
> img = cv2.imread('/home/pi/Desktop/Bilder2/Bild1.png',0)
>
> img[ img > 180] = 255 # dein img[x,y] > 180 in numpy parallel
> img[ 0:img.shape[0], 450:] = 255 # dein y > 450 in numpy
>
> img[ img <= 180] = 0 # bin nicht sicher ob du das einfach vergessen hast
>
> cv2.imshow('Image',img)
> cv2.waitKey(0)
> cv2.destroyAllWindows()

Danke erstmal für den Tipp.
Das versuch ich mal.

von Mike B. (mike_b97) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Cyblord -. schrieb:
> Mike B. schrieb:
>> je nach Prozessor geht auch erstmal multithreading mittels OpenMP
>
> WENN dein Algorithmus parallelisierbar ist. Und selbst dann ist das
> immer aufwändiger als einfach die große Maschine rauszuholen, wenn das
> möglich ist.
>
> Aber an 1. Stelle steht der Algorithmus selbst. Der ist das wichtigste.
> Alles andere ist nachgeordnet. Ich bin dagegen auf einen schlechten Algo
> mit dem Hammer Rechenleistung einzuschlagen.

Sorry aber hier gehts grad um einen Algo der 100x100 Bildpunkte 
nacheinander aber unabhängig voneinander abcheckt.
Leichter parallelisierbar gehts doch kaum noch.
bei 8 nutzbaren cores teilt der scheduler die Arbeit in 8 Teile, fertig.

Und dass der angegebene Algo für die Aufgabe kaum besser geht wurde 
schon gesagt.
Hängt halt nur von der Hardware ab.
Einen singlecore mit Hyperthreading würde auch nicht extra mit 
multithreading belasten, aber ab 3 nutzbaren cores geht da schon was.

: Bearbeitet durch User
von EdB (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Imonbln schrieb:
> img[ img > 180] = 255 # dein img[x,y] > 180 in numpy parallel
> img[ 0:img.shape[0], 450:] = 255 # dein y > 450 in numpy

Der Code klappt super und ist schnell...
Nur eine Frage:

Ist es möglich beide Bedingungen in einem abzufragen?

z.B.:

Alle Pixel die in der linken Bildhälfte sind und über 180 sind werden zu 
255.

von Imonbln (Gast)


Bewertung
0 lesenswert
nicht lesenswert
EdB schrieb:
> Ist es möglich beide Bedingungen in einem abzufragen?

Ich bin kein numpy Experte, keine Ahnung ob das geht. Da musst du ins 
Handbuch gucken.

von Rolf M. (rmagnus)


Bewertung
1 lesenswert
nicht lesenswert
Cyblord -. schrieb:
> Mike B. schrieb:
>> je nach Prozessor geht auch erstmal multithreading mittels OpenMP
>
> WENN dein Algorithmus parallelisierbar ist. Und selbst dann ist das
> immer aufwändiger als einfach die große Maschine rauszuholen, wenn das
> möglich ist.
>
> Aber an 1. Stelle steht der Algorithmus selbst. Der ist das wichtigste.
> Alles andere ist nachgeordnet. Ich bin dagegen auf einen schlechten Algo
> mit dem Hammer Rechenleistung einzuschlagen.

Irgendwie widersprechen sich diese beiden Aussagen. Oben schreibst du, 
man soll doch einfach einen dickeren Rechner nehmen, unten bist du 
dagegen, das zu tun.

von Cyblord -. (cyblord)


Bewertung
-1 lesenswert
nicht lesenswert
Rolf M. schrieb:

> Irgendwie widersprechen sich diese beiden Aussagen. Oben schreibst du,
> man soll doch einfach einen dickeren Rechner nehmen, unten bist du
> dagegen, das zu tun.

Könnte man meinen. Geb ich zu. ;-)
Das mit dem dickeren Rechner war auf Geschwindigkeitsprobleme wegen der 
Programmiersprache bezogen und darauf dass mehr Rechenleistung bis zu 
einem gewissen Punkt einfacher zu bekommen ist als Parallelisierung.

Bezüglich eines schlechten Algo macht aber mehr Rechenleistung nur 
begrenzt sinn, da hier es hier sehr schnell um extreme Größenänderungen 
gehen kann.

z.B. bei einer Datenbankabfrage mit JOIN kann die Auswahl des 
Optimierers bezüglich der inneren und äußeren Tabelle einen Unterschied 
von 1ms bis 1 Million Jahre ausmachen.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Bewertung
-2 lesenswert
nicht lesenswert
EdB schrieb:
> Ist es möglich beide Bedingungen in einem abzufragen?

 Genau wie in deinem Beispiel ist es normalerweise falsch.

 Wenn du OR in einer Abfrage hast, werden fast immer beide
 Bedingungen geprüft (Extremfall - alle Adressen über 450 haben
 Wert kleiner 180).
 Wie schon jemand geschrieben hat - Adressen über 450 auf 255 setzen,
 nur auf Wert über 180 abfragen.

: Bearbeitet durch User
von mIstA (Gast)


Bewertung
0 lesenswert
nicht lesenswert
EdB schrieb:
> Der Code funktioniert, aber braucht sehr viel Zeit.

Auf welcher Hardware läuft denn Dein Code?
C64? Arduino?


> Die Ausführung dauert einige Sekunden, selbst bei kleineren Bildern.

Sogar auf 'nem RasPi hab ich auf 1000x1000 Pixel erhöhen müssen, damit 
ich eine Laufzeit von 4-5 Sekunden erreicht hab. Was mach ich bloß 
falsch.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.