CannyCam

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Habe mich nach langer Zeit wieder mit python beschäftigt und den Canny-Algorithmus entdeckt. Mit diesem Script lasse ich Bilder enstehen, die abgepaust werden können. Blatt auf Bildschirm mit fester Unterlage legen und mit den Richtungstasten die Auflösung verändern. Ist man mit dem Bild zufrieden, dann kann das Abzeichen beginnen.

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8

try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk
    
from PIL import Image, ImageTk, ImageChops
from functools import partial
import cv

UPDATE_TIME = 100
MODUS = "raw"
BGR = "P"
RGB = "P"
CAM = -1
TITLE ="CANNY CAM"
FONT = "Arial 10 bold"
BG = "white"

NO_CAM_TEXT = "NO CAM FOUND"
NO_CAM_FONT = "Arial 26"

RIGHT_PRESS = "<Right>"
LEFT_PRESS = "<Left>"
RIGHT_RELEASE = "<KeyRelease-Right>"
LEFT_RELEASE = "<KeyRelease-Left>"
UP_PRESS = "<Up>"
DOWN_PRESS = "<Down>"
UP_RELEASE = "<KeyRelease-Up>"
DOWN_RELEASE = "<KeyRelease-Down>"

CANNY_STEP_X = 1
CANNY_STEP_Y = 1

RANGE = 300

START_X = 150
START_Y = 30


class Cam(tk.Label):
    def __init__(self, root, cam=CAM, text=TITLE, update_time=UPDATE_TIME, bg=BG):
        tk.Label. __init__(self, root, bg=bg)
        self.window = tk.Label(self)
        self.window.pack()
        self.root = root
        self.canny_x = START_X
        self.canny_y = START_Y
        self.update_time = update_time
        self.camera = cv.CaptureFromCAM(cam)
        self.camera_width = int(cv.GetCaptureProperty(self.camera, 
            cv.CV_CAP_PROP_FRAME_WIDTH))
        self.camera_height = int(cv.GetCaptureProperty(self.camera, 
            cv.CV_CAP_PROP_FRAME_HEIGHT))
        for button, step, function in ((RIGHT_PRESS, CANNY_STEP_X, self.shift_x),
                        (LEFT_PRESS, -CANNY_STEP_X,self.shift_x),
                        (RIGHT_RELEASE, 0, self.shift_x),
                        (LEFT_RELEASE, 0, self.shift_x),
                        (UP_PRESS, CANNY_STEP_Y, self.shift_y),
                        (DOWN_PRESS, -CANNY_STEP_Y, self.shift_y),
                        (UP_RELEASE, 0, self.shift_y),
                        (DOWN_RELEASE, 0, self.shift_y)):
            root.bind(button, partial(function, step))
                
    def shift_x(self, x, event):
        if (self.canny_x > - 0 and x < 0 or self.canny_x < RANGE and x > 0):
            self.canny_x += x
        
    def shift_y(self, y, event):
        if (self.canny_y > - 0 and y < 0 or self.canny_y < RANGE and y > 0):
            self.canny_y += y
        
    def run(self):
        self.frame = cv.QueryFrame(self.camera)
        if self.frame == None:
            self.window.config(text=NO_CAM_TEXT, font=NO_CAM_FONT,
                width=33, height=10)
        else:
            frame_grau = cv.CreateImage((self.frame.width, self.frame.height), cv.IPL_DEPTH_8U, 1)
            cv.CvtColor(self.frame, frame_grau, cv.CV_BGR2GRAY)
            frame_canny = cv.CreateImage((self.frame.width, self.frame.height), cv.IPL_DEPTH_8U, 1)
            cv.Canny(frame_grau, frame_canny, self.canny_x, self.canny_y)
            self.image = Image.fromstring(RGB, (self.camera_width, 
                self.camera_height), frame_canny.tostring(), MODUS, BGR)
            self.image = ImageChops.invert(self.image)
            self.tk_image = ImageTk.PhotoImage(self.resize_image(self.image))
            self.window.config(image = self.tk_image)
        self.after(self.update_time, self.run)
        
    def resize_image(self, img):
        width, height = self.root.winfo_screenwidth(), root.winfo_screenheight()
        img_width, img_height = img.size
        if height >= width:
            div_faktor = (float(str(int(img_width)) + ".0")
                / float(str(width) + ".0"))
            img_width = img_width / div_faktor
            img_height = img_height / div_faktor
        else:
            div_faktor = (float(str(int(img_height)) + ".0")
                / float(str(height) + ".0"))
            img_width = img_width / div_faktor
            img_height = img_height / div_faktor
        return img.resize((int(img_width), int(img_height)))
        

if __name__ == '__main__':
    root = tk.Tk()
    root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
    cam = Cam(root, CAM)
    root.title(TITLE)
    cam.pack(expand=True, fill="both")
    cam.run()
    root.mainloop()
Gruß Frank
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@kaytec: Was genau ging eigentlich in deinem Kopf vor als du in der Methode ``resize_image()`` die Bezeichner ``div_faktor`` definiert hast...? ;)
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo snafu,

habe mein schlechtes Deutsch in schlechtes Englisch übersetzt und habe eigentlich gar nicht mehr darüber nachgedacht. Mit Tab kann man die Aufnahme stoppen und das Malen kann beginnen. Habe es heute mit einem Beamer auf eine Wand geworfen und Portraits in A1 mit einer Mädchengruppe angefertigt. Den div_faktor entsorgt.

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8

try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk
    
from PIL import Image, ImageTk, ImageChops
from functools import partial
import cv

UPDATE_TIME = 100
MODUS = "raw"
BGR = "P"
RGB = "P"
CAM = -1
TITLE ="CANNY CAM"
FONT = "Arial 10 bold"
BG = "white"

NO_CAM_TEXT = "NO CAM FOUND"
NO_CAM_FONT = "Arial 26"

RIGHT_PRESS = "<Right>"
LEFT_PRESS = "<Left>"
RIGHT_RELEASE = "<KeyRelease-Right>"
LEFT_RELEASE = "<KeyRelease-Left>"
UP_PRESS = "<Up>"
DOWN_PRESS = "<Down>"
UP_RELEASE = "<KeyRelease-Up>"
DOWN_RELEASE = "<KeyRelease-Down>"
TAB_PRESS = "<Tab>"

CANNY_STEP_X = 1
CANNY_STEP_Y = 1

RANGE = 300

START_X = 150
START_Y = 30


class CannyCam(tk.Label):
    def __init__(self, root, width, height, cam=CAM, text=TITLE, update_time=UPDATE_TIME, bg=BG):
        tk.Label. __init__(self, root, bg=bg)
        self.window = tk.Label(self)
        self.window.pack()
        self.root = root
        self.cam_width = width
        self.cam_height = height
        self.run_cam = True
        self.canny_x = START_X
        self.canny_y = START_Y
        self.update_time = update_time
        self.camera = cv.CaptureFromCAM(cam)
        self.camera_width = int(cv.GetCaptureProperty(self.camera, 
            cv.CV_CAP_PROP_FRAME_WIDTH))
        self.camera_height = int(cv.GetCaptureProperty(self.camera, 
            cv.CV_CAP_PROP_FRAME_HEIGHT))
        for button, step, function in ((RIGHT_PRESS, CANNY_STEP_X, self.shift_x),
                                       (LEFT_PRESS, -CANNY_STEP_X,self.shift_x),
                                       (RIGHT_RELEASE, 0, self.shift_x),
                                       (LEFT_RELEASE, 0, self.shift_x),
                                       (UP_PRESS, CANNY_STEP_Y, self.shift_y),
                                       (DOWN_PRESS, -CANNY_STEP_Y, self.shift_y),
                                       (UP_RELEASE, 0, self.shift_y),
                                       (DOWN_RELEASE, 0, self.shift_y),
                                       (TAB_PRESS, 0, self.start_stop_cam)):
            root.bind(button, partial(function, step))
    
    def start_stop_cam(self, dummy, event):
        print self.run_cam
        if self.run_cam == False:
            self.run_cam = True
            self.run()
        else:
            self.run_cam = False
            
    def shift_x(self, x, event):
        if (self.canny_x > - 0 and x < 0 or self.canny_x < RANGE and x > 0):
            self.canny_x += x
        
    def shift_y(self, y, event):
        if (self.canny_y > - 0 and y < 0 or self.canny_y < RANGE and y > 0):
            self.canny_y += y
        
    def run(self):
        self.frame = cv.QueryFrame(self.camera)
        if self.frame == None:
            self.window.config(text=NO_CAM_TEXT, font=NO_CAM_FONT,
                width=self.root.winfo_screenwidth(), height=self.root.winfo_screenheight())
        else:
            frame_grau = cv.CreateImage((self.frame.width, self.frame.height), cv.IPL_DEPTH_8U, 1)
            cv.CvtColor(self.frame, frame_grau, cv.CV_BGR2GRAY)
            frame_canny = cv.CreateImage((self.frame.width, self.frame.height), cv.IPL_DEPTH_8U, 1)
            cv.Canny(frame_grau, frame_canny, self.canny_x, self.canny_y)
            self.image = Image.fromstring(RGB, (self.camera_width, 
                self.camera_height), frame_canny.tostring(), MODUS, BGR)
            self.image = ImageChops.invert(self.image)
            self.tk_image = ImageTk.PhotoImage(self.resize_image(self.image))
            self.window.config(image = self.tk_image)
        if self.run_cam == True:
            self.after(self.update_time, self.run)
        
    def resize_image(self, img):
        width, height = self.cam_width, self.cam_height
        img_width, img_height = img.size
        if height >= width:
            img_width = img_width / (float(str(int(img_width)) + ".0")
                / float(str(width) + ".0"))
            img_height = img_height / (float(str(int(img_width)) + ".0")
                / float(str(width) + ".0"))
        else:
            img_width = img_width / (float(str(int(img_height)) + ".0")
                / float(str(height) + ".0"))
            img_height = img_height / (float(str(int(img_height)) + ".0")
                / float(str(height) + ".0"))
        return img.resize((int(img_width), int(img_height)))
        

if __name__ == '__main__':
    root = tk.Tk()
    root_width = root.winfo_screenwidth()
    root_height = root.winfo_screenheight()
    root.geometry("{0}x{1}+0+0".format(root_width, root_height))
    cannycam = CannyCam(root, root_width, root_height, CAM)
    root.title(TITLE)
    cannycam.pack(expand=True)
    cannycam.run()
    root.mainloop()
Gruß Frank
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich bezog mich gar nicht mal auf die Namensgebung der Variable, sondern vielmehr auf das Kauderwelsch auf der rechten Seite der Definition, aber egal... :mrgreen:
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: es handelt sich hier um Kunst. Da ist ein schnödes "img_width = width" viel zu profan :twisted: .
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich find's jedenfalls toll, dass es im zweiten Beitrag *noch* schlimmer geworden ist. :lol:
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo Sanfu u Sirius3,

"resige_image" soll die Seitenverhältnisse des Aufnahmebildes zur Fenstergrösse beibehalten. Ohne den "div_faktor" geht es auch nicht mehr richtig ? Habe es wieder reingebastelt und jetzt funktioniert es wieder. Ob es Kunst ist kann ich nicht sagen, da ich keiner Künstler bin. Falls jemand eine schönere Lösung hat, dann darf er sie gerne zeigen.

Also doch so:

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8

try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk
    
from PIL import Image, ImageTk, ImageChops
from functools import partial
import cv

UPDATE_TIME = 100
MODUS = "raw"
BGR = "P"
RGB = "P"
CAM = -1
TITLE ="CANNY CAM"
FONT = "Arial 10 bold"
BG = "white"

NO_CAM_TEXT = "NO CAM FOUND"
NO_CAM_FONT = "Arial 26"

RIGHT_PRESS = "<Right>"
LEFT_PRESS = "<Left>"
RIGHT_RELEASE = "<KeyRelease-Right>"
LEFT_RELEASE = "<KeyRelease-Left>"
UP_PRESS = "<Up>"
DOWN_PRESS = "<Down>"
UP_RELEASE = "<KeyRelease-Up>"
DOWN_RELEASE = "<KeyRelease-Down>"
TAB_PRESS = "<Tab>"

CANNY_STEP_X = 1
CANNY_STEP_Y = 1

RANGE = 300

START_X = 150
START_Y = 30


class CannyCam(tk.Label):
    def __init__(self, root, width, height, cam=CAM, text=TITLE, update_time=UPDATE_TIME, bg=BG):
        tk.Label. __init__(self, root, bg=bg)
        self.window = tk.Label(self)
        self.window.pack()
        self.root = root
        self.cam_width = width
        self.cam_height = height
        self.run_cam = True
        self.canny_x = START_X
        self.canny_y = START_Y
        self.update_time = update_time
        self.camera = cv.CaptureFromCAM(cam)
        self.camera_width = int(cv.GetCaptureProperty(self.camera, 
            cv.CV_CAP_PROP_FRAME_WIDTH))
        self.camera_height = int(cv.GetCaptureProperty(self.camera, 
            cv.CV_CAP_PROP_FRAME_HEIGHT))
        for button, step, function in ((RIGHT_PRESS, CANNY_STEP_X, self.shift_x),
                                       (LEFT_PRESS, -CANNY_STEP_X,self.shift_x),
                                       (RIGHT_RELEASE, 0, self.shift_x),
                                       (LEFT_RELEASE, 0, self.shift_x),
                                       (UP_PRESS, CANNY_STEP_Y, self.shift_y),
                                       (DOWN_PRESS, -CANNY_STEP_Y, self.shift_y),
                                       (UP_RELEASE, 0, self.shift_y),
                                       (DOWN_RELEASE, 0, self.shift_y),
                                       (TAB_PRESS, 0, self.start_stop_cam)):
            root.bind(button, partial(function, step))
    
    def start_stop_cam(self, dummy, event):
        print self.run_cam
        if self.run_cam == False:
            self.run_cam = True
            self.run()
        else:
            self.run_cam = False
            
    def shift_x(self, x, event):
        if (self.canny_x > - 0 and x < 0 or self.canny_x < RANGE and x > 0):
            self.canny_x += x
        
    def shift_y(self, y, event):
        if (self.canny_y > - 0 and y < 0 or self.canny_y < RANGE and y > 0):
            self.canny_y += y
        
    def run(self):
        self.frame = cv.QueryFrame(self.camera)
        if self.frame == None:
            self.window.config(text=NO_CAM_TEXT, font=NO_CAM_FONT,
                width=self.root.winfo_screenwidth(), height=self.root.winfo_screenheight())
        else:
            frame_grau = cv.CreateImage((self.frame.width, self.frame.height), cv.IPL_DEPTH_8U, 1)
            cv.CvtColor(self.frame, frame_grau, cv.CV_BGR2GRAY)
            frame_canny = cv.CreateImage((self.frame.width, self.frame.height), cv.IPL_DEPTH_8U, 1)
            cv.Canny(frame_grau, frame_canny, self.canny_x, self.canny_y)
            self.image = Image.fromstring(RGB, (self.camera_width, 
                self.camera_height), frame_canny.tostring(), MODUS, BGR)
            self.image = ImageChops.invert(self.image)
            self.tk_image = ImageTk.PhotoImage(self.resize_image(self.image))
            self.window.config(image = self.tk_image)
        if self.run_cam == True:
            self.after(self.update_time, self.run)
        
    def resize_image(self, img):
        img_width, img_height = img.size
        if self.cam_height >= self.cam_width:
            div_faktor = (float(str(int(img_width)) + ".0")
                / float(str(self.cam_width) + ".0"))
            img_width = img_width / div_faktor
            img_height = img_height / div_faktor
        else:
            div_faktor = (float(str(int(img_height)) + ".0")
                / float(str(self.cam_height) + ".0"))
            img_width = img_width / div_faktor
            img_height = img_height / div_faktor
        
        return img.resize((int(img_width), int(img_height)))
        

if __name__ == '__main__':
    root = tk.Tk()
    root_width = root.winfo_screenwidth()
    root_height = root.winfo_screenheight()
    root.geometry("{0}x{1}+0+0".format(root_width, root_height))
    cannycam = CannyCam(root, root_width, root_height, CAM)
    root.title(TITLE)
    cannycam.pack(expand=True)
    cannycam.run()
    root.mainloop()
Gruß Frank
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@keytec: nimm mal die Zeile

Code: Alles auswählen

div_faktor = (float(str(int(img_height)) + ".0")
                / float(str(self.cam_height) + ".0"))
auseinander und erkläre, was Du da im einzelnen machst und warum Du das so machst.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo Sirius3,

ich bastel aus "int" ein "float" und mache daraus "str" und wieder ein "float" - ist schon schräg, doch ich brauchte die ".0". Das habe ich mal für ein Script zur Herstellung von Trickfilmen gebraucht - warum auch immer es bei mir nur so funktioniert hat, kann ich nicht mehr reproduzieren.

Gruß Frank
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

kaytec hat geschrieben:warum auch immer es bei mir nur so funktioniert hat, kann ich nicht mehr reproduzieren.
Du hast irgendwann mal versucht irgendeinen Fehler in deinem Programm durch wildes Herumexperimentieren zu lösen und bist bei dieser Lösung angekommen? Der Code ist mit den Begriffen "obskur" und "wahnsinnig" noch sehr wohlwollend beschrieben. Im Mittelalter wärst du für so etwas verbrannt worden und ehrlich gesagt, ich hätte mitgeholfen das Holz zu sammeln.

Warum sollte der folgende Code nicht das tun was du brauchst (gesetzt den Fall, bei beiden height-Angaben handelt es sich um Integer-Werte)?

Code: Alles auswählen

div_faktor = float(img_height) / self.cam_height
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo me,

das mit dem Herumexperimentieren würde ich unterschreiben. In dieser Zeit hätte man auch solch einen "Ketzerkot" verbrannt - zündest du den Server an oder soll ich dir meine Festplatte zuschicken? Ich wäre bestimmt auch in die Hölle gekommen, doch dort soll die Küche eh besser sein. Bei den wenigen Menschen im Himmel wird es nicht warm gekocht und die scheinen auch gerne zu zündeln.

Deine Lösung funktioniert auch und ist natürlich um einiges schöner.

Gruß Frank
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Im Fallen von Python3 oder (division aus __future__) kann man sich das explizite Umwandeln mit float auch ganz sparen:

Code: Alles auswählen

div_faktor = img_height / self.cam_height
Das Ergebnis ist in jedem Fall eine Fließkommazahl.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

bwbg hat geschrieben:Im Fallen von Python3 oder (division aus __future__) kann man sich das explizite Umwandeln mit float auch ganz sparen.
Das ist ein guter Hinweis für die Zukunft. Hier handelt es sich allerdings um Code für Python 2 und der pauschale Einsatz des future-Imports könnte an anderen Stellen des Programms zu unerwünschten Ergebnissen führen.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Ich unterstelle bei Python2/3 grundsätzliche die aktuelle Version des jeweiligen Zweiges. Wobei man natürlich darauf achten sollte, welche Auswirkungen die jeweiligen imports aus __future__ auf den gesamten Programmablauf haben. Darüber hinaus hat der Quelltext schon eine 2/3-Weiche beim Import von (T|t)kinter und ist (bzw. soll) damit implizit unter Python3 lauffähig.

Weiter ist mir seitens der Namensgebung "div_faktor" sauer aufgestoßen. Was denn nun? Division oder Multplikation? In den Folgezeilen wird es als Divisor verwendet und nicht als Faktor.

Wobei hier ebenfalls die Ganzzahldivision (//) verwendet werden kann (wieder Python3 oder division aus __future__), um die Typumwandlung durch int zu vermeiden.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@bwbg: die Ganzzahl-Division verhindert nicht, dass das Ergebnis ein float ist, wenn einer der Operanden ein float ist. Ein __future__ import ist auch nicht nötig.

Kürzesterweise also:

Code: Alles auswählen

    def resize_image(self, img):
        img_width, img_height = img.size
        if self.cam_height >= self.cam_width:
            img_height = img_height * self.cam_width // img_width
            img_width = self.cam_width 
        else:
            img_width = img_width * self.cam_height // img_height
            img_height = self.cam_height 
       
        return img.resize((img_width, img_height))
Antworten