Iterations Variable zurücksetzen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
nikh22
User
Beiträge: 28
Registriert: Montag 27. Juni 2022, 15:46

Hallo,

ich will über ein gesamtes Bildes jedes Pixel mit einem Schwellwert vergleichen. Nachdem das ganze Bild durch gegangen wurde und darüber eine Maske und weitere Berechnungen durchgeführt wurden,soll das ganze wieder von vorne mit einem anderen Schwellwert beginnen. Der Schwellwert ist ein einfacher 1D-Array dessen Größe und Inhalt bekannt sind allerdings geringer als die des Bildes.
Aktuell erhalte ich immer folgende Fehler:

TypeError: only integer scalar arrays can be converted to a scalar index oder IndexError: index 66 is out of bounds for axis 0 with size 65

Muss ich das mit der äußersten for schleife anders lösen ? Kann ich die iterationsvariablen i und j irgendwie zurücksetzen?

[optional]
Kann ich die ganzen Schleifen auch durch sogenannte "Universal Functions" ersetzen ?
https://jakevdp.github.io/PythonDataSci ... funcs.html

Mein Code:

Code: Alles auswählen

import cv2
import time
import numpy
import numpy as np
from matplotlib import pyplot as plt
import json

start_time= time.time()

# Load image as BGR
img_bgr = cv2.imread(r'bilder/test-patches/cloudy_sun_DARK_SKY.jpg', cv2.IMREAD_COLOR)

img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
# Split BGR Picture in each chanel
(b, g, r) = cv2.split(img_bgr)


# get width and height
height, width = img_bgr.shape[:2]
#create binary mask
binary_mask = np.empty([height, width])


# Cast Array to int-values for bigger number space, cause cv.split has limit to 255 (8bit-colorspace)
b_value, g_value, r_value = b.astype(int), g.astype(int), r.astype(int)

# Calculate Sum ∑ of R,G,B Channels
sum_rgb = b_value + g_value + r_value

# Compute pixel composition delta
delta = 4 * (r - g) ** 2 + (r - b) ** 2 + 4 * (g - b) ** 2

# Blur image for better initial threshold
img_blur = cv2.medianBlur(img_bgr,5)
threshold,image_result = cv2.threshold(img_gray, 0, 255, cv2.THRESH_OTSU)

# Lowered Otsu Threshold to ensure low initial value  but save some iterations for improving calculation time
initial_threshold = int(threshold/3)

# Limiting condition
limit_condition1 = 760

# Create iterable threshold array
threshold_iterations = np.arange(initial_threshold,224,3,dtype= int )




for k in np.nditer(threshold_iterations):
    for i in range(height):
        for j in range(width):
            if delta[i][j] >= threshold_iterations[k]:
                if sum_rgb[i][j] > limit_condition1:
                    binary_mask[i][j] = 0
                else:
                    binary_mask[i][j] =1
            elif delta[i][j] < threshold_iterations[k]:
                if (b_value[i][j] > r_value[i][j]) and (b_value[i][j] > g_value[i][j]):
                    binary_mask[i][j] = 0
                else:
                    binary_mask[i][j] = 1





print("--- %s seconds ---" % (time.time() - start_time))


Sirius3
User
Beiträge: 17768
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum verwendest Du np.arange, wo doch range genauso funktionieren würde?
Du weißt was np.nditer macht und warum Du es hier benutzt?
Den Wert aus einem Array wieder als Index in dieses Array zu benutzen ist selten sinnvoll.
Wenn man mit Schleifen ein Array verarbeitet, macht man in den meisten Fällen etwas falsch.
Wenn im elif die gegenteilige Bedingung wie im if-Teil hat, dann benutzt man else
Da in jedem threshold-Durchgang binary_mask komplett überschrieben wird, bleibt nur der letzte Durchlauf übrig, die Schleife kann also weg.
Der ganz Code sollte also das selbe machen, wie das hier:

Code: Alles auswählen

threshold = range(initial_threshold, 224, 3)[-1]
bgr_mask = (b_value <= r_value) | (b_value <= g_value)
sum_mask = sum_rgb <= limit_condition1
binary_mask = np.where(delta > threshold, sum_mask, bgr_mask)
nikh22
User
Beiträge: 28
Registriert: Montag 27. Juni 2022, 15:46

Sirius3 hat geschrieben: Dienstag 13. Dezember 2022, 20:23 Warum verwendest Du np.arange, wo doch range genauso funktionieren würde?
Du weißt was np.nditer macht und warum Du es hier benutzt?
Den Wert aus einem Array wieder als Index in dieses Array zu benutzen ist selten sinnvoll.
Wenn man mit Schleifen ein Array verarbeitet, macht man in den meisten Fällen etwas falsch.
Wenn im elif die gegenteilige Bedingung wie im if-Teil hat, dann benutzt man else
Da in jedem threshold-Durchgang binary_mask komplett überschrieben wird, bleibt nur der letzte Durchlauf übrig, die Schleife kann also weg.
-Ich hab np.arange verwendet weil ich nicht wusste dass es auch mit range funktioniert. Aber jetzt mal ganz blöd gefragt macht das überhaupt einen Unterschied?
-np.nditer hatte ich verwendet da ich die werte des arrays threshold_iterations in der schleife nutzen wollte , gleichzeitig aber die schleife nur solange inkrementiere wie viele elemente der array enthält. Scheint hier auch der falsche Ansatz gewesen zu sein.
- das mit der gegenteiligen Nutzung mit else statt elif leuchtet mir ein.
- zum letzten Punkt hab ich vergessen dass verschiedene dinge mit binary mask berechnet werden und die in einer anderen variablen gespeichert werden sollen.

etwa so:
"binary_mask" wurde zu "mask"

Code: Alles auswählen

cloud_coverage= np.empty(len(threshold_iterations))

for k in np.nditer(threshold_iterations):
    for i in range(height):
        for j in range(width):
            if delta[i][j] >= threshold_iterations[k]:
                if sum_rgb[i][j] >= limit_condition1:
                    mask[i][j] = 0  # Sun
                else:
                    mask[i][j] =1   # Cloud
            else:
                if (b_value[i][j] > r_value[i][j]) and (b_value[i][j] > g_value[i][j]):
                    mask[i][j] = 0  # Sky
                else:
                    mask[i][j] = 2  # Environment eg. tree

    allpixels = mask.size
    number0, number1 , number2 = np.count_nonzero(mask==0), np.count_nonzero(mask==1), np.count_nonzero(mask==2)
    cloud_coverage[k] = (allpixels-number1-number2)/(allpixels-number2)

                    

Nachtrag: ich seh gerade dass das mit cloud_coverage[k] so keinen sinn macht, cloud_coverage sollte nur so oft berechnet werden wie es auch werte in threshold_iterations gibt.
Muss sagen verliere aber langsam den Überblick, ich hoffe ich konnte mich möglichst verständlich ausdrücken.
Sirius3
User
Beiträge: 17768
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich weiß jetzt nicht, ob die ganzen anderen Anmerkungen bei Dir Sonnen-klar sind und Du sie deshalb nicht erwähnst, ob Du sie überlesen hast oder sie nicht verstehst.
Arbeitest Du den Rest gerade noch durch? Meine Lösung läßt sich ja recht direkt auf Deine geänderten Anforderungen übertragen.
`cloud_coverage` sollte eine ganz normale Pythonliste sein. Manche Sachen lassen sich klarer mit numpy ausdrücken, andere nicht.
nikh22
User
Beiträge: 28
Registriert: Montag 27. Juni 2022, 15:46

Sirius3 hat geschrieben: Dienstag 13. Dezember 2022, 20:23 Warum verwendest Du np.arange, wo doch range genauso funktionieren würde?
Du weißt was np.nditer macht und warum Du es hier benutzt?
Den Wert aus einem Array wieder als Index in dieses Array zu benutzen ist selten sinnvoll.
Wenn man mit Schleifen ein Array verarbeitet, macht man in den meisten Fällen etwas falsch.
Wenn im elif die gegenteilige Bedingung wie im if-Teil hat, dann benutzt man else
Da in jedem threshold-Durchgang binary_mask komplett überschrieben wird, bleibt nur der letzte Durchlauf übrig, die Schleife kann also weg.
Anworten zu den Fragen und Anmerkungen:
-Ich hab np.arange verwendet weil ich nicht wusste dass es auch mit range funktioniert. Aber jetzt mal ganz blöd gefragt macht das überhaupt einen Unterschied?
-ich wollte die schleife solange inkrementieren wie der array threshold iterations lang ist aber bei jedem inkrement den wert der nullten ,ersten, zweiten... stelle von threshold_iterations nutzen, geht das mit np.nditer nicht ?
-schleifen zu umgehen wäre mir auch lieb da ich gelesen habe dass diese in python oft langsam sind, allerdings wusste ich nicht wie ich das ohne schleifen umsetzen sollte.
- das mit der gegenteiligen Nutzung mit else statt elif leuchtet mir ein.
- zum letzten Punkt hab ich vergessen dass verschiedene dinge mit binary mask berechnet werden und die in einer anderen variablen gespeichert werden sollen.
- ich versteh nicht ganz wie ich die äußerste schleife weglassen kann, wenn bei jedem durchgang die maske überschrieben wird ich diese aber zur berechnung brauche wenn ich damit die variable cloud_coverage berechne.
Sirius3 hat geschrieben: Dienstag 13. Dezember 2022, 20:23 Der ganz Code sollte also das selbe machen, wie das hier:

Code: Alles auswählen

threshold = range(initial_threshold, 224, 3)[-1]
bgr_mask = (b_value <= r_value) | (b_value <= g_value)
sum_mask = sum_rgb <= limit_condition1
binary_mask = np.where(delta > threshold, sum_mask, bgr_mask)
Das mit den eckigen Klammern nach range()[-1] hab ich nicht ganz verstanden, wird damit nicht nur der letzte wert herausgegeben?
Nach den de-morganschen-gesetzen kann ich die logik doch nicht durch eine einfache verneinung umkehren dabei muss es sich doch stets um eine doppelte handeln oder?
Die Idee mit np.where gefällt mir sehr muss ich sagen.
Wie kann ich das aber mit dem steigernden Wert treshhold_iterations nutzen, weil so wie ich das aktuell verstehe findet die ganze Berechnung "nur" für einen threshhold statt oder ?

Ja hatte mich wirklich erst einlesen müssen und wollte die andere Berechnung nicht unerwähnt lassen, da es sonst vielleicht eine andere bessere Lösung gegeben hätte.
nikh22
User
Beiträge: 28
Registriert: Montag 27. Juni 2022, 15:46

Nachtrag:

Habe meine ursprüngliche Version zum laufen bekommen allerdings dauert es schlappe 118s zum berechnen. Das ist natürlich viel zu lange deswegen würde mich wirklich brennend interessieren wie man das auf die andere Art lösen kann.
Sirius3
User
Beiträge: 17768
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn np.arange keinen Vorteil gegenüber einem einfacheren range hat, warum sollte man es dann benutzen? Natürlich bedeutet das [-1], dass nur der letzte Wert genommen wird, weil das in Deinem ursprünglichen Code der einzig relevante Wert war.
Das mit den for-Schleifen hast Du ganz generell noch nicht ganz verstanden. Lass Dir doch einfach mal das `k` ausgeben, dann sollte klar werden, was np.nditer in diesem Fall liefert.
Die inneren if-Vergleiche sind nicht abhängig vom threshold, also kann man die im Voraus berechnen. Da die Schleifen trivial über alle Elemente gehen, kann man die Operationen auch gleich auf die ganze Matrix anwenden.
number0 benutzt Du gar nicht, mußt Du also gar nicht berechnen. Besser wäre es hier doch auch, direkt die relevanten Werte zu ermitteln. Der Name `cloud_coverage` ist auch sehr verwirrend, weil es den wolkenfreien Anteil am Himmel beschreibt.

Code: Alles auswählen

# mask values
# 0 Sun / Sky
# 1 Cloud
# 2 Environment eg. tree
sky_mask = sum_rgb <= limit_condition1
tree_mask = (b_value <= r_value) | (b_value <= g_value) * 2

clearsky_coverage = []
for threshold in range(initial_threshold, 224, 3):
    mask = np.where(delta > threshold, sky_mask, tree_mask)
    sky_count = np.count_nonzero(mask == 0)
    sky_or_cloud_count = np.count_nonzero(mask != 2)
    clearsky_coverage.append(sky_count / sky_or_cloud_count)
Antworten