Python Forum

Full Version: Python file to slow, how peed up ?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello,

I've built a Python script to make pictures and cut them in the right size automatic.
Everything works ok only it is very slow.
One picture takes about 7 sec. to make and cut.
Is it possible to speed things up a little ?
I've read about Numba and Cython only i'm not a programmer so it is not so easy for me.
Maby somebody has some tips and tricks for me ?

from PIL import Image,ImageChops
import glob
import os
import sys
import threading
import time
import datetime

#########################################################
from ctypes import *
from pathlib import Path
import pythoncom
import datetime
########################################################

global fileName
global trimVariable
global c
old_path  = None
global image_to_save
global image_path

global cam
edsdk = windll.edsdk


def AddTime(fname):
    now = datetime.datetime.now()
    nname = fname[:-4]+'_'+now.isoformat()[:-7].replace(':','-')+fname[-4:]
    return nname


class EDSDKError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

def EDErrorMsg(code):
    return "EDSDK error code"+hex(code)
    
def Call(code):
    if code!=0:
            raise Exception(EDSDKError, EDErrorMsg(code))
        
def Release(ref):
    edsdk.EdsRelease(ref)
    
def GetChildCount(ref):
    i = c_int()
    Call(edsdk.EdsGetChildCount(ref,byref(i)))
    return i.value

def GetChild(ref,number):
    c = c_void_p()
    Call(edsdk.EdsGetChildAtIndex(ref,number,byref(c)))
    return c
    

kEdsObjectEvent_DirItemRequestTransfer  =    0x00000208
kEdsObjectEvent_DirItemCreated       =       0x00000204


ObjectHandlerType = WINFUNCTYPE   (c_int,c_int,c_void_p,c_void_p)
def ObjectHandler_py(event,object,context):
    if event==kEdsObjectEvent_DirItemRequestTransfer:
        DownloadImage(object)
    return 0
ObjectHandler = ObjectHandlerType(ObjectHandler_py)


kEdsStateEvent_WillSoonShutDown       =      0x00000303

StateHandlerType = WINFUNCTYPE   (c_int,c_int,c_int,c_void_p)
def StateHandler_py(event,state,context):
    if event==kEdsStateEvent_WillSoonShutDown:
        print ("cam about to shut off")
        Call(edsdk.EdsSendCommand(context,1,0))
    return 0
StateHandler = StateHandlerType(StateHandler_py)


PropertyHandlerType = WINFUNCTYPE   (c_int,c_int,c_int,c_int,c_void_p)
def PropertyHandler_py(event,property,param,context):
    return 0
PropertyHandler = PropertyHandlerType(PropertyHandler_py)


class DirectoryItemInfo(Structure):
    _fields_ = [("size", c_int),
                ("isFolder", c_int),
                ("groupID",c_int),
                ("option",c_int),
                ("szFileName",c_char*256),
                ("format",c_int)]

WaitingForImage = False
ImageFilename = None

def DownloadImage(Image,):
    dirinfo = DirectoryItemInfo()
    Call(edsdk.EdsGetDirectoryItemInfo(Image,byref(dirinfo)))
    stream = c_void_p()
    global ImageFilename
    if ImageFilename is None:
        print ("Image was taken manually")
        ImageFilename = AddTime(".jpg")
    print (("Saving as"),ImageFilename)
    Call(edsdk.EdsCreateFileStream(ImageFilename,1,2,byref(stream)))
    print (ImageFilename)
    Call(edsdk.EdsDownload(Image,dirinfo.size,ImageFilename,stream)) 
    Call(edsdk.EdsDownloadComplete(Image))
    Release(stream)
    
    p = Path('I')
    p.rename (ImageFilename)
    
    global WaitingForImage
    WaitingForImage = False


kEdsSaveTo_Camera       =   1
kEdsSaveTo_Host         =   2
kEdsSaveTo_Both         =   kEdsSaveTo_Camera | kEdsSaveTo_Host
kEdsPropID_SaveTo  = 0x0000000b



class EdsCapacity(Structure):
    _fields_ = [("numberOfFreeClusters", c_int),
                ("bytesPerSector", c_int),
                ("reset",c_int)]


class Camera:
    def __init__(self,camnum=0):
        self.cam = None
        l = CameraList()
        self.cam = l.GetCam(camnum)
        Call(edsdk.EdsSetObjectEventHandler(self.cam,0x200,ObjectHandler,None))
        Call(edsdk.EdsSetPropertyEventHandler(self.cam,0x100,PropertyHandler,None))
        Call(edsdk.EdsSetCameraStateEventHandler(self.cam,0x300,StateHandler,self.cam))
        Call(edsdk.EdsOpenSession(self.cam))
        
        self.SetProperty(kEdsPropID_SaveTo,kEdsSaveTo_Host)
        
        #set large capacity
        cap = EdsCapacity(10000000,512,1)
        Call(edsdk.EdsSetCapacity(self.cam,cap))
    def __del__(self):
        if self.cam is not None:
            Call(edsdk.EdsCloseSession(self.cam))
            Call(Release(self.cam))
    def SetProperty(self,property,param):
        d = c_int(param)
        Call(edsdk.EdsSetPropertyData(self.cam,property,0,4,byref(d)))
    def AutoFocus(self):
#   kEdsCameraCommand_ShutterButton_OFF                 = 0x00000000,
#   kEdsCameraCommand_ShutterButton_Halfway             = 0x00000001,
#   kEdsCameraCommand_ShutterButton_Completely          = 0x00000003,
#   kEdsCameraCommand_ShutterButton_Halfway_NonAF       = 0x00010001,
#   kEdsCameraCommand_ShutterButton_Completely_NonAF    = 0x00010003,
        # note that this can fail when AF fails (error code 0x8D01)
        self.SendCommand(4,1)
    def Shoot(self,fname=None):
        # set saving flag
        global WaitingForImage
        WaitingForImage = True

        # set filename
        global ImageFilename
        if fname is None:
            ImageFilename = AddTime("IMG.jpg") 
        else:
            ImageFilename = fname

        # note that this can fail when AF fails (error code 0x8D01)
        self.SendCommand(0)
        # capture succeeded so go on to download image
        while WaitingForImage:
            pythoncom.PumpWaitingMessages()
        return ImageFilename
    def KeepOn(self):
        # important command - keeps the camera connected when not used
        self.SendCommand(1)
    def SendCommand(self,command,param=0):
        #define kEdsCameraCommand_TakePicture                     0x00000000
        #define kEdsCameraCommand_ExtendShutDownTimer             0x00000001
        #define kEdsCameraCommand_BulbStart                       0x00000002 
        #define kEdsCameraCommand_BulbEnd                         0x00000003 
        #define kEdsCameraCommand_DoEvfAf                         0x00000102
        #define kEdsCameraCommand_DriveLensEvf                    0x00000103
        #define kEdsCameraCommand_DoClickWBEvf                    0x00000104        
        #define kEdsCameraCommand_PressShutterButton              0x00000004
        Call(edsdk.EdsSendCommand(self.cam,command,param))

class CameraList:
    def __init__(self):
        self.list = c_void_p(None)
        Call(edsdk.EdsGetCameraList(byref(self.list)))
        print (("found"),GetChildCount(self.list),"cameras")
    def Count(self):
        return GetChildCount(self.list)
    def GetCam(self,number=0):
        print ("get cam")
        if self.Count()<(number+1):
            raise (ValueError,("Camera not found, make sure it's on and connected"))
        return GetChild(self.list,number)
    def __del__(self):
        Release(self.list)
    

    


#########################################################

#BUFFER_SIZE = 1024



def setConnection():
    global c
    global trimVariable
    f= open("instellingen.txt","r")
    fl =f.readlines() 
    f.close()
    trimVariable=fl[4].rstrip("\n")
    h=fl[0].rstrip("\n")
    p=int(fl[1].rstrip("\n"))
    c = ModbusClient(host=h, port=p, auto_open=True)
    
    
    
def getLastImage():
    f= open("instellingen.txt","r")
    fl =f.readlines() 
    f.close()
    list_of_files = glob.glob(fl[2].rstrip("\n")+'/*.JPG') # * means all if need specific format then *.csv
    if not list_of_files==[]:
        latest_file = max(list_of_files, key=os.path.getctime)
        return latest_file
    else:
        return None
    
def extractFileName(qrCode):
    s=str(qrCode)
    filname=''
    for i in range(10,21):
        filname+=s[i]
    return str(filname)


    

def trim1(im):
    bg = Image.new(im.mode, im.size, im.getpixel((0,0)))
    diff = ImageChops.difference(im, bg)
    diff = ImageChops.add(diff, diff, float(trimVariable), -100)
    bbox = diff.getbbox()
    if bbox:
        return im.crop(bbox)
    
def makeItSequare(im):
    bg = Image.new(im.mode, im.size, im.getpixel((0,0)))
    diff = ImageChops.difference(im, bg)
    diff = ImageChops.add(diff, diff, float(trimVariable), -100)
    bbox = diff.getbbox()
    left,top,right,bottom=bbox
    left=left-50 #geef wat ruimte om foto
    right=right+50
    top=top-50
    width=right-left
    height=4000-top
    big_size=max(width,height)
    small_size=min(width,height)
    dif=(big_size-small_size)/2
    tot=(big_size-small_size)
    if big_size==width:
        top=top-tot
    else:
        left=left-dif
        right=right+dif
    cropped_img = im.crop((left,top,right, 4000))
    return cropped_img
    

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
app=0
app = QtWidgets.QApplication(sys.argv)

class Ui_ScannerApp(object):
    def setupUi(self, ScannerApp):
        ScannerApp.setObjectName("ScannerApp")
        ScannerApp.resize(1350, 690)
        self.input = QtWidgets.QLineEdit(ScannerApp)
        self.input.setGeometry(QtCore.QRect(20, 30, 261, 20))
        self.input.setObjectName("input")
        self.input.textChanged.connect(self.scanCode)
        
        self.codebare = QtWidgets.QLabel(ScannerApp)
        self.codebare.setGeometry(QtCore.QRect(390, 30, 301, 21))
        self.codebare.setText("")
        self.codebare.setObjectName("codebare")
        self.qrtest = QtWidgets.QLabel(ScannerApp)
        self.qrtest.setGeometry(QtCore.QRect(800, 30, 131, 21))
        self.qrtest.setText("Wacht op Code")
        self.qrtest.setObjectName("qrtest")
        self.checkBox = QtWidgets.QCheckBox(ScannerApp)
        self.checkBox.setGeometry(QtCore.QRect(20, 70, 211, 31))
        self.checkBox.setObjectName("checkBox")
        self.checkBox.setChecked(True)
        
        # updates #-----------------------
        self.make_button = QtWidgets.QPushButton(ScannerApp)
        self.make_button.setGeometry(QtCore.QRect(350, 70, 150, 30))
        self.make_button.setText("Make picture")
        self.make_button.setEnabled(False)
        self.make_button.clicked.connect(self.remake_image)
        
        #----- #######################
        
        self.imageDisplayer = QtWidgets.QLabel(ScannerApp)
        self.imageDisplayer.setGeometry(QtCore.QRect(20, 120,1700, 571))
        self.imageDisplayer.setText("")
        self.imageDisplayer.setObjectName("imageDisplayer")

        self.logoDisplayer = QtWidgets.QLabel(ScannerApp)
        self.logoDisplayer.setGeometry(QtCore.QRect(950,50,1500,100)) #logo plaats
        self.logoDisplayer.setText("")
        self.logoDisplayer.setObjectName("logoDisplayer")
        
        self.retranslateUi(ScannerApp)
        QtCore.QMetaObject.connectSlotsByName(ScannerApp)

    def retranslateUi(self, ScannerApp):
        _translate = QtCore.QCoreApplication.translate
        ScannerApp.setWindowTitle(_translate("ScannerApp", "Form"))
        self.checkBox.setText(_translate("ScannerApp", "Maak foto vierkant"))

    def setLogo(self):
        pixmap = QtGui.QPixmap()
        st=r'logo Mooij Techniek.png'#plaats logo
        pixmap.load(st)
        pixmap = pixmap.scaledToWidth(1500) #logo maat
        pixmap = pixmap.scaledToHeight(100)
        self.logoDisplayer.setPixmap(pixmap)
        
    def imageCropping(self,fileName):
        f= open("instellingen.txt","r")
        fl =f.readlines() 
        f.close()
        
        directory =fl[3].rstrip("\n")
        path=getLastImage()
        if path is not None:
            original=Image.open(path)
            if self.checkBox.isChecked():
                sequareImage=makeItSequare(original)
                st=str(directory+'/'+fileName+'.jpg')
                sequareImage.save(st)
                sequareImage.save ("voorbeeld.jpg")
            else:  
                im=trim1(original)
                st=str(directory+'/'+fileName+'.jpg')
                im.save(st)
                im.save ("voorbeeld.jpg")
            
            os.remove(path)
            pixmap = QtGui.QPixmap()
            pixmap.load("voorbeeld.jpg")
            pixmap = pixmap.scaledToWidth(700)
            pixmap = pixmap.scaledToHeight(571)
            self.imageDisplayer.setPixmap(pixmap)


                
    def remake_image(self):
        global cam
        cam.Shoot()
        while True:
            #time.sleep(0.1)
            self.imageCropping(fileName)
            self.display_image()
            break   
        
                
    def display_image(self):
        pixmap = QtGui.QPixmap()
        pixmap.load("voorbeeld.jpg")
        pixmap = pixmap.scaledToWidth(700)
        pixmap = pixmap.scaledToHeight(571)
        self.imageDisplayer.setPixmap(pixmap)
        
        

    def scanCode(self):
        scan=self.input.text()
        if len(scan)<31:
            return None
        else:
            return scan
        
        
        
    def verifiyQR(self,entry):
        global c
        global fileName
        if entry:    
            self.codebare.setText(str(entry))
            self.qrtest.setStyleSheet('color: green')
            self.qrtest.setText('Code OK')
            fileName=extractFileName(entry)
            while True:
                self.input.setText('')  
                self.codebare.setText('')
                self.qrtest.setStyleSheet('color: black')
                self.qrtest.setText('Wacht op code')
                break
            
        
    def backgroundThread(self):
        #old_path
        while True:
            app.processEvents()
            code =self.scanCode()
            time.sleep(0.1)
            if code is not None:
                self.make_button.setEnabled(True)
                print(' Just checking the code ')
                self.verifiyQR(code)
                time.sleep(0.1)
                                  
    def activateScan(self):
        thread=threading.Thread(name='backgroundThread', target=self.backgroundThread)
        thread.start()    
        
edsdk.EdsInitializeSDK()
if __name__ == "__main__":
    global cam
    pythoncom.CoInitialize()
    cam = Camera()
    ScannerApp = QtWidgets.QWidget()
    ui = Ui_ScannerApp()
    ui.setupUi(ScannerApp)
    setConnection()
    ui.setLogo()
    ui.activateScan()
    ScannerApp.show()
    app.exec_()
    edsdk.EdsTerminateSDK()
    
You don't really expect that someone will dig in 450 lines of spaghetti code - mixing GUI and logic, without comments, using global variables and full of magic numbers...
Do you really need those time sleeps in your threads? Is there a reason you are using PIL and not pillow?

I do not use PIL/pillow, but just googling around it appears that pillow-SIMD may be faster than PIL and pillow

http://www.dragcitycasting.com/pillow-simd/
https://www.reddit.com/r/Python/comments...ow_and_10/

(Jan-04-2019, 05:13 PM)Leon Wrote: [ -> ]I've built a Python script to make pictures and cut them in the right size
(Jan-04-2019, 05:13 PM)Leon Wrote: [ -> ]I've read about Numba and Cython only i'm not a programmer so it is not so easy for me.
You've built this script but your not a programmer? A lot of your coding style makes me wonder if there are bottlenecks that you are unaware of. I would first check that before anything. The only people who are going to know the bottlenecks are yourself and any programmer that knows PIL and wants to devote the time to get into it.
I'm using PILLOW not PIL.
You must import PIL not Pillow if your using Pillow.
Pillow simd is for rezising not for cropping.

I'm sure there are bottlenecks, because this is my first project with Python.
What is the best way to find the Bottlenecks ?
Quote:What is the best way to find the Bottlenecks ?
Use the profiler (profile module). Here is an introduction with links.