diff --git a/SLM script.py b/SLM script.py new file mode 100644 index 0000000..f6c323c --- /dev/null +++ b/SLM script.py @@ -0,0 +1,90 @@ +# python script for Controlling the Thorlabs EXULUS-HD2 Spatial Light Modulator +# K Kesinbora 01.19.21 +# +# https://github.com/wavefrontshaping/slmPy +# +# One has to install the wxPython first as slmPy depends on that module pip install -U wxPython +# Download the slmpy.py to the same folder as the script. +# + +from IPython import get_ipython +get_ipython().magic('reset -sf') + +# Import all the dependencies + +import wx #wxPython module https://wxpython.org/pages/downloads/index.html +import slmpy #slmPy module from https://github.com/wavefrontshaping/slmPy Thanks to wavefrontshaping + +import numpy as np +import time + +import urllib +from PIL import Image +import requests +from io import BytesIO +import matplotlib.pyplot as plt + +# Define an SLM object + +x = 0 # Monitor ID 0 for primary screen, 1 for monitor (default). x = 0 is basically testing in your own computer screen. + +slm = slmpy.SLMdisplay(monitor = x) + +# Retreive the size of the SLM + +resX, resY = slm.getSize() + +# Generate a meshgrid support + +X,Y = np.meshgrid(np.linspace(0,resX,resX),np.linspace(0,resY,resY)) + +SizeX,SizeY = (1920,1200) # This sets the size of the image to be pulled from the picsum library + +sleeptime = 5 # How long the image is displayed on the SLM screen + +# Display a static test image on the SLM screen + +# Generate a test image + +#test = np.round((2**8-1)*(0.5+0.5*np.sin(2*np.pi*X/50))).astype('uint8') + +#slm.updateArray(test) + +#time.sleep(sleeptime) + +#slm.close() + +# Display a static image from an url + +#url = 'https://picsum.photos/'+str(SizeX)+'/'+str(SizeY) + +#response = requests.get(url) +#img = np.asarray(Image.open(BytesIO(response.content)).convert('L')) + +#imgplot = plt.imshow(img) + +#slm.updateArray(img) + +#time.sleep(sleeptime) + +#slm.close() + +# Display a series of random images from an url + +maximageno = 10 # Total number of images to be displayed on the SLM +counter = 1 + +while(counter < maximageno): + url = 'https://picsum.photos/'+str(SizeX)+'/'+str(SizeY) + response = requests.get(url) + img = np.asarray(Image.open(BytesIO(response.content)).convert('L')) # Convert to image into an 8bit numpy array + + slm.updateArray(img) + time.sleep(sleeptime) + slm.close + + # fig = plt.figure() + # imgplot = plt.imshow(img) # I couln't get this to work simultaneously with the slm update array + counter = counter + 1 + + diff --git a/slmpy.py b/slmpy.py new file mode 100644 index 0000000..7ab68a5 --- /dev/null +++ b/slmpy.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +""" +Created on Sun Dec 06 20:14:02 2015 + +@author: Sebastien Popoff +""" + +try: + import wx +except ImportError: + raise(ImportError,"The wxPython module is required to run this program.") +import threading +import numpy as np +import time + + + + + +EVT_NEW_IMAGE = wx.PyEventBinder(wx.NewEventType(), 0) + +class ImageEvent(wx.PyCommandEvent): + def __init__(self, eventType=EVT_NEW_IMAGE.evtType[0], id=0): + wx.PyCommandEvent.__init__(self, eventType, id) + self.img = None + self.color = False + self.oldImageLock = None + self.eventLock = None + + +class SLMframe(wx.Frame): + """Frame used to display full screen image.""" + def __init__(self, monitor, isImageLock = True): + self.isImageLock = isImageLock + # Create the frame + #wx.Frame.__init__(self,None,-1,'SLM window',pos = (self._x0, self._y0), size = (self._resX, self._resY)) + self.SetMonitor(monitor) + # Set the frame to the position and size of the target monito + wx.Frame.__init__(self,None,-1,'SLM window',pos = (self._x0, self._y0), size = (self._resX, self._resY)) + self.img = wx.EmptyImage(2,2) + self.bmp = self.img.ConvertToBitmap() + self.clientSize = self.GetClientSize() + # Update the image upon receiving an event EVT_NEW_IMAGE + self.Bind(EVT_NEW_IMAGE, self.UpdateImage) + # Set full screen + self.ShowFullScreen(not self.IsFullScreen(), wx.FULLSCREEN_ALL) + self.SetFocus() + + def InitBuffer(self): + self.clientSize = self.GetClientSize() + self.bmp = self.img.Scale(self.clientSize[0], self.clientSize[1]).ConvertToBitmap() + dc = wx.ClientDC(self) + dc.DrawBitmap(self.bmp,0,0) + + + def UpdateImage(self, event): + self.eventLock = event.eventLock + self.img = event.img + self.InitBuffer() + self.ReleaseEventLock() + + def ReleaseEventLock(self): + if self.eventLock: + if self.eventLock.locked(): + self.eventLock.release() + + def SetMonitor(self, monitor): + if (monitor < 0 or monitor > wx.Display.GetCount()-1): + raise ValueError('Invalid monitor (monitor %d).' % monitor) + self._x0, self._y0, self._resX, self._resY = wx.Display(monitor).GetGeometry() + + +class SLMdisplay: + """Interface for sending images to the display frame.""" + def __init__(self ,monitor = 1, isImageLock = False): + self.isImageLock = isImageLock + self.monitor = monitor + # Create the thread in which the window app will run + # It needs its thread to continuously refresh the window + self.vt = videoThread(self) + self.eventLock = threading.Lock() + if (self.isImageLock): + self.eventLock = threading.Lock() + + def getSize(self): + return self.vt.frame._resX, self.vt.frame._resY + + def updateArray(self, array): + """ + Update the SLM monitor with the supplied array. + Note that the array is not the same size as the SLM resolution, + the image will be deformed to fit the screen. + """ + #create a wx.Image from the array + h,w = array.shape[0], array.shape[1] + + if len(array.shape) == 2: + bw_array = array.copy() + bw_array.shape = h, w, 1 + color_array = np.concatenate((bw_array,bw_array,bw_array), axis=2) + data = color_array.tostring() + else : + data = array.tostring() + img = wx.ImageFromBuffer(width=w, height=h, dataBuffer=data) + # Create the event + event = ImageEvent() + event.img = img + event.eventLock = self.eventLock + + # Wait for the lock to be released (if isImageLock = True) + # to be sure that the previous image has been displayed + # before displaying the next one - it avoids skipping images + if (self.isImageLock): + event.eventLock.acquire() + # Wait (can bug when closing/opening the SLM too quickly otherwise) + time.sleep(0.5) + # Trigger the event (update image) + self.vt.frame.AddPendingEvent(event) + + def close(self): + self.vt.frame.Close() + +class videoThread(threading.Thread): + """Run the MainLoop as a thread. Access the frame with self.frame.""" + def __init__(self, parent,autoStart=True): + threading.Thread.__init__(self) + self.parent = parent + # Set as deamon so that it does not prevent the main program from exiting + self.setDaemon(1) + self.start_orig = self.start + self.start = self.start_local + self.frame = None #to be defined in self.run + self.lock = threading.Lock() + self.lock.acquire() #lock until variables are set + if autoStart: + self.start() #automatically start thread on init + def run(self): + app = wx.App() + frame = SLMframe(monitor = self.parent.monitor, isImageLock = self.parent.isImageLock) + frame.Show(True) + self.frame = frame + self.lock.release() + # Start GUI main loop + app.MainLoop() + + def start_local(self): + self.start_orig() + # Use lock to wait for the functions to get defined + self.lock.acquire()