Leere Streifen im generierter Pixelgrafik

Fragen zu Tkinter.
Antworten
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Hallo liebe Python-Gemeinde,

ich habe folgendes Programm zur Darstellung einer Julia-Menge in der komplexen Ebene geschrieben:


Code: Alles auswählen

from tkinter import *
from math import *

ZOOM=200
LAENGE=500
SCHRITTE=16

    
class Julia(object):
    def __init__(self,c):
        self.c=c
        self.fenster=Tk()
        self.bild=PhotoImage(width=LAENGE,height=LAENGE)
        self.bildflaeche=Label(master=self.fenster, \
                              image=self.bild)
        self.bildflaeche.pack()
        self.zeichnen()
        self.fenster.mainloop()

    def zeichnen(self):
        intervalle=[]
        for i in range(int(-LAENGE/2),int(LAENGE/2)):
            intervalle=intervalle+[i/ZOOM]
        for x in intervalle:
            for y in intervalle:
                z=complex(x,y)
                if self.test(z) == -1:
                    self.bild.put("#000000",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 0:
                    self.bild.put("#111111",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 1:
                    self.bild.put("#222222",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 2:
                    self.bild.put("#333333",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 3:
                    self.bild.put("#444444",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 4:
                    self.bild.put("#555555",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 5:
                    self.bild.put("#666666",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 6:
                    self.bild.put("#777777",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 7:
                    self.bild.put("#888888",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 8:
                    self.bild.put("#999999",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 9:
                    self.bild.put("#aaaaaa",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 10:
                    self.bild.put("#bbbbbb",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 11:
                    self.bild.put("#cccccc",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 12:
                    self.bild.put("#dddddd",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 13:
                    self.bild.put("#eeeeee",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 14:
                    self.bild.put("#ffffff",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))
                elif self.test(z) == 15:
                    self.bild.put("#ffffff",(int(x*ZOOM+(LAENGE/2)),int(-y*ZOOM+(LAENGE/2))))


    def test(self,z):
        r=max(abs(self.c),2)
        i=0
        while i<SCHRITTE:
            if abs(z)>r:
                return i        # NICHT in der Gefangenenmenge
            z=z*z+self.c
            i=i+1
        return -1                 # in der Gefangenenmenge
Das generierte Bild stimmt soweit, es tauchen nur merkwürdige leere Streifen auf.
Siehe hier:
Bild

Ich bin ein wenig ratlos. Über Hinweise und Tipps würde wäre ich sehr dankbar!

Viele Grüße,
muhazz
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo und willkommen im Forum!

Ohne mir deinen Code genau angeschaut zu haben vermute ich mal, dass dir noch ein else-Teil fehlt, bzw. dass die test-Methode etwas liefert, was nicht abgefragt wird.

Du solltest übrigens deinen code zusammenfassen, offensichtlich unterscheiden sich nur die Farben. Mit einem Dictionary ist das in einer Zeile erledigt.

Deine Klasse ist auch überflüssig, eine Funtkion es hier geeigneter. Klassen sind kein Selbstzweck. Dann solltest du noch die *-importe ersetzen, damit müllst du dir nur den Namensraum zu. Und der \ in Zeile 15 ist ebenfalls überflüssig. Und die while-Schleife in der test-Methode sollte natürlich durch eine for-Schleife ersetzt werden. Oder besser: schau dir Generatoren und die all-Funktion an.

Ich hoffe, dass dir damit ein wenig geholfen ist.

Bis dann,
Sebastian
Das Leben ist wie ein Tennisball.
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Danke für die schnelle Rückmeldung :)

Ich dachte mir schon, dass es für den riesigen Abfrage-Teil eine elegantere Möglichkeit geben muss. Von Dictionaries habe ich keine Ahnung, aber ich werde mal etwas stöbern.

Die *-Importe sind für mich sehr bequem, weil ich nicht weiß, welche Module (ich hoffe, das ist der richtige Begriff) genau ich importieren muss.
Dadurch sollten aber keine allzu große Nachteile entstehen, oder?

Aber das sind alles Kleinigkeiten, die für mich momentan nicht von großer Bedeutung sind (auch wenn es eingefleischtem Programmierern vielleicht sehr weh tut, was ich da fabriziere :wink:)

Zum Hauptproblem:
Das war auch mein erster Gedanke, aber ich konnte bisher keine Möglichkeit entdecken, wie die test-Methode etwas anderes als -1, 0, 1, 2,...,15 liefern könnte.
Vielleicht stehe ich aber gerade auch nur auf dem Schlauch mit Tomaten auf den Augen.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo muhazz

Willkommen in unserem Forum.

Könnte es evtl. mit der Umrechung in Integers zu tun haben (Rundungsfehler). Es sieht fast so aus als ob an der Stelle wo sich die Linien befinden keine Pixel mit einer vorgegebenen Farbe gesetzt werden. Die Farbe der Linien entspricht nämlich der Hintergrundfarbe des leeren PhotoImages. Das heisst das nicht beschriebene PhotoImage ist eigentlich transparent und lässt die Hindergrundfarbe des Labels in welchem sich das PhotoImage befindet durchscheinen. Wähle für das Label eine andere Hintergrundfarbe (z.B. bg='red') dann siehst du das die Linien sich in roter Farbe zeigen.

Gruß wuf :wink:
Take it easy Mates!
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

wuf hat geschrieben:Könnte es evtl. mit der Umrechung in Integers zu tun haben (Rundungsfehler).
So ist es. Wenn man int() durch round() ersetzt, sind die Streifen weg.

Das ändert allerdings nichts daran, dass der Code unbedingt überarbeitungsbedürftig ist.
BlackJack

@muhazz: Denk mal bitte darüber nach wie oft `test()` für ein Pixel ausgeführt wird wenn das Ergebnis sagen wir zum Beispiel 10 ist. Und wie oft es eigentlich nur nötig wäre. :-)
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Danke, die Streifen sind weg!

...und das dictionary integriert.
Gibt es denn eine Methode, die Farben nicht manuell ins Dictionary einzugeben, sondern irgendwie generieren zu lassen?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ja, das ist hier machbar:

Code: Alles auswählen

>>> "c"*6
'cccccc'
Die Schleife überlasse ich dir
Das Leben ist wie ein Tennisball.
BlackJack

@muhazz: Den Code von EyDu kann man dann noch hiermit kombinieren um aus Zahlen die entsprechenden Hex-Ziffern zu generieren:

Code: Alles auswählen

In [352]: '%x' % 5
Out[352]: '5'

In [353]: '%x' % 12
Out[353]: 'c'

In [354]: '%x' % 15
Out[354]: 'f'

In [355]: '%x' % 12 * 6
Out[355]: 'cccccc'

In [356]: '#' + '%x' % 12 * 6
Out[356]: '#cccccc'
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

BlackJack hat geschrieben: [...] um aus Zahlen die entsprechenden Hex-Ziffern zu generieren
Das war der Knackpunkt. Vielen Dank.
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

muhazz hat geschrieben:Die *-Importe sind für mich sehr bequem, weil ich nicht weiß, welche Module (ich hoffe, das ist der richtige Begriff) genau ich importieren muss.
Dadurch sollten aber keine allzu große Nachteile entstehen, oder?
Du solltest *-Importe wirklich versuchen zu vermeiden, da dadurch schwer nachvollziehbare Fehler auftreten können.

Kurzes Beispiel:

Code: Alles auswählen

>>> import random
>>> random.randint(0,10)
6
>>> from pylab import *
>>> random.randint(0,10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'builtin_function_or_method' object has no attribute 'randint'
Besser:

Code: Alles auswählen

>>> import random
>>> import pylab
>>> random.randint(0,10)
6
Oder:

Code: Alles auswählen

>>> from random import randint
>>> import pylab as plt
>>> randint(0,10)
6
Grüße
Gerrit
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Danke für die Beispiele. Ich werde mich bei Gelegenheit damit auseinandersetzen - ich wusste bisher garnicht, dass der Befehl "import math" ohne eine genauere Angabe, was denn geladen werden soll, auch exisitiert.

Ich habe im Moment aber immer noch Probleme mit der Zuordnung von geeigneten Farben.
Ich bräuchte einen Algorithmus, der
  • dem Wert -1 schwarz zuordnet
  • und für positive ganze Zahlen von 0 bis zu einer bestimmten Zahl (Größenordnung 100) eine Abstufung von Dunkel(kleine Werte) nach Hell(größere Werte, Weiß in Größenordnung 20) und wieder nach Dunkel(größte Werte) erzeugt.
"Dunkel" heißt hier aber nicht schwarz, sondern eine andere dunkle Farbe, beispielsweise Dunkelblau.
Also z.B. eine Abstufung: Blau-->Weiß-->Blau

Wie setzt man so etwas denn üblicherweise um? Schreibt man sich die Finger an einem Dictionary wund und ordnet jedem Wert eine Farbe zu oder entwirft man eher einen Algorithmus, der die passende Farbe in Abhängigkeit vom Wert erzeugt?
BlackJack

@muhazz: Ich würde sagen genau dazwischen: Man benutzt einen Algorithmus der eine Farbtabelle errechnet. Und statt Dictionary böte sich hier auch einfach eine Liste an wenn die Schlüssel fortlaufende ganze Zahlen sind. Ich würde auch versuchen die -1 zu vermeiden und die Berechnung lieber so gestalten das etwas zwischen 0 und der Obergrenze/Anzahl der Farben herauskommt.

Du könntest auch versuchen Modell und Anzeige besser zu trennen, also als Ergebnis keine Farben sondern eben die Zahlen zwischen 0 und 100. Und dieses 2D-"Array" dann über den Zwischenschritt einer Farbtabelle darstellen. Hat zum Beispiel den Vorteil, dass der Anwender sich ein und das selbe Ergebnis mit verschiedenen Farbtabellen anzeigen lassen kann.

Du könntest Dir auch mal die Möglichkeiten Farbverläufe zu erstellen in Grafikprogrammen anschauen. Zum Beispiel wie das bei Gimp funktioniert und gespeichert wird. Zumindest früher war das mal ein relativ simples Textformat.
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Ich glaube, ich verstehe deinen Grundgedanken, aber mir sind wieder viele Begriffe und wohl auch übliche Vorgehensweisen fremd.
Das sollte trotzdem machbar sein - klingt aber wieder nach Arbeit.

Ich werde wohl erst mit meiner provisorischen Darstellung arbeiten und die Zeit lieber für wichtigere Aufgaben nutzen. Wenn mir noch Zeit übrig bleibt, werde ich das Problem aber definitiv nochmal angehen und mich dann auch wieder hier melden.

Vielen Dank und viele Grüße,
muhazz
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Hier ein minimales Beispiel:
http://paste.pocoo.org/show/198740/

:wink:
yipyip
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

Alternativ könntest du auch Matplotlib oder MayaVi (Beispiel) verwenden. Mit Matplotlib lässt sich das Beispiel wie folgt realisieren:

Code: Alles auswählen

import numpy as np
import pylab as plt

def julia():
    # Calculate the Julia set on a grid
    x, y = np.mgrid[-1.5:0.5:500j, -1:1:500j]
    z = x + 1j*y

    julia = np.zeros(z.shape)

    for i in range(50):
        z = z**2 -0.70176 -0.3842j
        julia += 1/float(2+i)*(z*np.conj(z) > 4)
    
    return x,y,julia

if __name__ == '__main__':
    x,y,z = julia()
    plt.contourf(x,y,z)
    plt.show()
Grüße
Gerrit
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Hmm, ich muss meine Aussage von vorhin korrigieren - ich sollte das Problem besser jetzt lösen.

@gkuhl:
Enthält der Quelltext, den du angeführt hast, schon eine Darstellung der Julia-Menge mit entsprechenden Abstufungen der Farben?
Wenn ich ihn ausführe, erhalte ich eine Fehlermeldung, dass es kein Modul "numpy" gibt.

In Matplotlib und MayaVi müsste ich mich erst einarbeiten, ich habe bisher absolut keine Ahnung.
Ist der Umgang damit leicht zu erlernen und bieten die beiden Möglichkeiten, die es lohnenswert für mich machen?

Ich möchte ausgefüllte Julia-Mengen mit farbigen Abstufungen darstellen, außerdem Ränder von Julia-Mengen und Bahnen einzelner Iterationen, evtl. auch über das Bild einer Julia-Menge gelegt.
Wie ich nur den Rand einer Julia-Menge darstellen soll, weiß ich übrigens noch nicht.
Also, eignet sich Matplotlib oder MayaVi besonders dafür?
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

NumPy ist nicht Teil der Standardbibliothek. Mein Beispiel hat auch Farbabstufungen (colormaps), auch wenn sie in dem Fall vielleicht nicht unbedingt die schönsten sind. Zur 2d Darstellung ist die Matplot Library ein Quasi-Standard, die dir sicherlich einiges an Arbeit abnehmen kann. Schau am besten Mal in die Galerie auf der Matplotlib-Seite. Für 3d wird häufig MayaVi verwendet.

Wenn du mit Python mathematische oder naturwissenschaftliche Probleme lösen willst, ist es auf jeden Fall sinnvoll sich SciPy und NumPy anzuschauen.
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Langsam passt der Titel wirklich nicht mehr zur Diskussion. Ich würde vorschlagen, hier gehts weiter:
http://www.python-forum.de/post-166404.html#166404
Antworten