def tex(self, value): """For BufferImageStim this method is not called by the user """ self.__dict__['tex'] = tex = value id = self._texID pixFormat = GL.GL_RGB useShaders = self.useShaders interpolate = self.interpolate im = tex.transpose(Image.FLIP_TOP_BOTTOM) self._origSize=im.size #im = im.convert("RGBA") # should be RGBA because win._getRegionOfFrame() returns RGBA intensity = numpy.array(im).astype(numpy.float32)*0.0078431372549019607 - 1 # same as *2/255-1, but much faster if useShaders:#pixFormat==GL.GL_RGB and not wasLum internalFormat = GL.GL_RGB32F_ARB dataType = GL.GL_FLOAT data = intensity else: #pixFormat==GL.GL_RGB:# not wasLum, not useShaders - an RGB bitmap with no shader options internalFormat = GL.GL_RGB dataType = GL.GL_UNSIGNED_BYTE data = float_uint8(intensity) pixFormat=GL.GL_RGBA # because win._getRegionOfFrame() returns RGBA internalFormat=GL.GL_RGBA32F_ARB if self.win.winType=='pygame': texture = data.tostring()#serialise else:#pyglet on linux needs ctypes instead of string object!? texture = data.ctypes#serialise #bind the texture in openGL GL.glEnable(GL.GL_TEXTURE_2D) GL.glBindTexture(GL.GL_TEXTURE_2D, id) #bind that name to the target GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_REPEAT) #makes the texture map wrap (this is actually default anyway) #important if using bits++ because GL_LINEAR #sometimes extrapolates to pixel vals outside range if interpolate: GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR) if useShaders:#GL_GENERATE_MIPMAP was only available from OpenGL 1.4 GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, data.shape[1], data.shape[0], 0, pixFormat, dataType, texture) else:#use glu GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST) GL.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, internalFormat, data.shape[1], data.shape[0], pixFormat, dataType, texture) else: GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, data.shape[1], data.shape[0], 0, pixFormat, dataType, texture)
def eyetracker_setup(win, et_config_file=None): """ loads the eyetracker configuration file see https://www.psychopy.org/api/iohub/device/eyetracker_interface/SR_Research_Implementation_Notes.html """ if et_config_file is None: eyetracker_config = { 'eyetracker.hw.sr_research.eyelink.EyeTracker': { 'calibration': dict(), 'simulation_mode': True, 'enable_interface_without_connection': True } } else: eyetracker_config = utils.load_init(et_config_file) # load eyelink settings bkgcolor = [int(float_uint8(c)) for c in win.color] k = list(eyetracker_config.keys())[0] # this is the hw specification path eyetracker_config[k]['calibration'].update(dict(screen_background_color=bkgcolor)) return eyetracker_config
def makeImageAuto(inarray): """Combines float_uint8 and image2array operations ie. scales a numeric array from -1:1 to 0:255 and converts to PIL image format""" return image2array(float_uint8(inarray))
def tex(self, value): """For BufferImageStim this method is not called by the user """ self.__dict__['tex'] = tex = value id = self._texID pixFormat = GL.GL_RGB useShaders = self.useShaders interpolate = self.interpolate im = tex.transpose(Image.FLIP_TOP_BOTTOM) self._origSize = im.size #im = im.convert("RGBA") # should be RGBA because win._getRegionOfFrame() returns RGBA intensity = numpy.array(im).astype( numpy.float32 ) * 0.0078431372549019607 - 1 # same as *2/255-1, but much faster if useShaders: #pixFormat==GL.GL_RGB and not wasLum internalFormat = GL.GL_RGB32F_ARB dataType = GL.GL_FLOAT data = intensity else: #pixFormat==GL.GL_RGB:# not wasLum, not useShaders - an RGB bitmap with no shader options internalFormat = GL.GL_RGB dataType = GL.GL_UNSIGNED_BYTE data = float_uint8(intensity) pixFormat = GL.GL_RGBA # because win._getRegionOfFrame() returns RGBA internalFormat = GL.GL_RGBA32F_ARB if self.win.winType == 'pygame': texture = data.tostring() #serialise else: #pyglet on linux needs ctypes instead of string object!? texture = data.ctypes #serialise #bind the texture in openGL GL.glEnable(GL.GL_TEXTURE_2D) GL.glBindTexture(GL.GL_TEXTURE_2D, id) #bind that name to the target GL.glTexParameteri( GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT ) #makes the texture map wrap (this is actually default anyway) #important if using bits++ because GL_LINEAR #sometimes extrapolates to pixel vals outside range if interpolate: GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR) if useShaders: #GL_GENERATE_MIPMAP was only available from OpenGL 1.4 GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, data.shape[1], data.shape[0], 0, pixFormat, dataType, texture) else: #use glu GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST) GL.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, internalFormat, data.shape[1], data.shape[0], pixFormat, dataType, texture) else: GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, data.shape[1], data.shape[0], 0, pixFormat, dataType, texture)
def _createTexture(self, tex, id, pixFormat, stim, res=128, maskParams=None, forcePOW2=True, dataType=None): """ :params: id: is the texture ID pixFormat: GL.GL_ALPHA, GL.GL_RGB useShaders: bool interpolate: bool (determines whether texture will use GL_LINEAR or GL_NEAREST res: the resolution of the texture (unless a bitmap image is used) dataType: None, GL.GL_UNSIGNED_BYTE, GL_FLOAT. Only affects image files (numpy arrays will be float) For grating stimuli (anything that needs multiple cycles) forcePOW2 should be set to be True. Otherwise the wrapping of the texture will not work. """ """ Create an intensity texture, ranging -1:1.0 """ notSqr=False #most of the options will be creating a sqr texture wasImage=False #change this if image loading works useShaders = stim.useShaders interpolate = stim.interpolate if dataType==None: if useShaders and pixFormat==GL.GL_RGB: dataType = GL.GL_FLOAT else: dataType = GL.GL_UNSIGNED_BYTE if type(tex) == numpy.ndarray: #handle a numpy array #for now this needs to be an NxN intensity array intensity = tex.astype(numpy.float32) if intensity.max()>1 or intensity.min()<-1: logging.error('numpy arrays used as textures should be in the range -1(black):1(white)') if len(tex.shape)==3: wasLum=False else: wasLum = True ##is it 1D? if tex.shape[0]==1: stim._tex1D=True res=tex.shape[1] elif len(tex.shape)==1 or tex.shape[1]==1: stim._tex1D=True res=tex.shape[0] else: stim._tex1D=False #check if it's a square power of two maxDim = max(tex.shape) powerOf2 = 2**numpy.ceil(numpy.log2(maxDim)) if forcePOW2 and (tex.shape[0]!=powerOf2 or tex.shape[1]!=powerOf2): logging.error("Requiring a square power of two (e.g. 16x16, 256x256) texture but didn't receive one") core.quit() res=tex.shape[0] elif tex in [None,"none", "None"]: res=1 #4x4 (2x2 is SUPPOSED to be fine but generates wierd colors!) intensity = numpy.ones([res,res],numpy.float32) wasLum = True elif tex == "sin": onePeriodX, onePeriodY = numpy.mgrid[0:res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation intensity = numpy.sin(onePeriodY-pi/2) wasLum = True elif tex == "sqr":#square wave (symmetric duty cycle) onePeriodX, onePeriodY = numpy.mgrid[0:res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation sinusoid = numpy.sin(onePeriodY-pi/2) intensity = numpy.where(sinusoid>0, 1, -1) wasLum = True elif tex == "saw": intensity = numpy.linspace(-1.0,1.0,res,endpoint=True)*numpy.ones([res,1]) wasLum = True elif tex == "tri": intensity = numpy.linspace(-1.0,3.0,res,endpoint=True)#-1:3 means the middle is at +1 intensity[int(res/2.0+1):] = 2.0-intensity[int(res/2.0+1):]#remove from 3 to get back down to -1 intensity = intensity*numpy.ones([res,1])#make 2D wasLum = True elif tex == "sinXsin": onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:1j*res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation intensity = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2) wasLum = True elif tex == "sqrXsqr": onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:1j*res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation sinusoid = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2) intensity = numpy.where(sinusoid>0, 1, -1) wasLum = True elif tex == "circle": rad=makeRadialMatrix(res) intensity = (rad<=1)*2-1 wasLum=True elif tex == "gauss": rad=makeRadialMatrix(res) # Set SD if specified if maskParams == None: sigma = 1.0 / 3 else: sigma = 1.0 / maskParams['sd'] intensity = numpy.exp( -rad**2.0 / (2.0*sigma**2.0) )*2-1 #3sd.s by the edge of the stimulus wasLum=True elif tex == "cross": X, Y = numpy.mgrid[-1:1:1j*res, -1:1:1j*res] tf_neg_cross = ((X < -0.2) & (Y < -0.2)) | ((X < -0.2) & (Y > 0.2)) | ((X > 0.2) & (Y < -0.2)) | ((X > 0.2) & (Y > 0.2)) #tf_neg_cross == True at places where the cross is transparent, i.e. the four corners intensity = numpy.where(tf_neg_cross, -1, 1) wasLum = True elif tex == "radRamp":#a radial ramp rad=makeRadialMatrix(res) intensity = 1-2*rad intensity = numpy.where(rad<-1, intensity, -1)#clip off the corners (circular) wasLum=True elif tex == "raisedCos": # A raised cosine wasLum=True hamming_len = 1000 # This affects the 'granularity' of the raised cos # If no user input was provided: if maskParams is None: fringe_proportion = 0.2 # This one affects the proportion of the # stimulus diameter that is devoted to the # raised cosine. # Users can provide the fringe proportion through a dict, maskParams # input: else: fringe_proportion = maskParams['fringeWidth'] rad = makeRadialMatrix(res) intensity = numpy.zeros_like(rad) intensity[numpy.where(rad < 1)] = 1 raised_cos_idx = numpy.where( [numpy.logical_and(rad <= 1, rad >= 1-fringe_proportion)])[1:] # Make a raised_cos (half a hamming window): raised_cos = numpy.hamming(hamming_len)[:hamming_len/2] raised_cos -= numpy.min(raised_cos) raised_cos /= numpy.max(raised_cos) # Measure the distance from the edge - this is your index into the hamming window: d_from_edge = numpy.abs((1 - fringe_proportion)- rad[raised_cos_idx]) d_from_edge /= numpy.max(d_from_edge) d_from_edge *= numpy.round(hamming_len/2) # This is the indices into the hamming (larger for small distances from the edge!): portion_idx = (-1 * d_from_edge).astype(int) # Apply the raised cos to this portion: intensity[raised_cos_idx] = raised_cos[portion_idx] # Scale it into the interval -1:1: intensity = intensity - 0.5 intensity = intensity / numpy.max(intensity) #Sometimes there are some remaining artifacts from this process, get rid of them: artifact_idx = numpy.where(numpy.logical_and(intensity == -1, rad < 0.99)) intensity[artifact_idx] = 1 artifact_idx = numpy.where(numpy.logical_and(intensity == 1, rad > 0.99)) intensity[artifact_idx] = 0 else: if type(tex) in [str, unicode, numpy.string_]: # maybe tex is the name of a file: if not os.path.isfile(tex): logging.error("Couldn't find image file '%s'; check path?" %(tex)); logging.flush() raise OSError, "Couldn't find image file '%s'; check path? (tried: %s)" \ % (tex, os.path.abspath(tex))#ensure we quit try: im = Image.open(tex) im = im.transpose(Image.FLIP_TOP_BOTTOM) except IOError: logging.error("Found file '%s' but failed to load as an image" %(tex)); logging.flush() raise IOError, "Found file '%s' [= %s] but it failed to load as an image" \ % (tex, os.path.abspath(tex))#ensure we quit else: # can't be a file; maybe its an image already in memory? try: im = tex.copy().transpose(Image.FLIP_TOP_BOTTOM) # ? need to flip if in mem? except AttributeError: # nope, not an image in memory logging.error("Couldn't make sense of requested image."); logging.flush() raise AttributeError, "Couldn't make sense of requested image."#ensure we quit # at this point we have a valid im stim._origSize=im.size wasImage=True #is it 1D? if im.size[0]==1 or im.size[1]==1: logging.error("Only 2D textures are supported at the moment") else: maxDim = max(im.size) powerOf2 = int(2**numpy.ceil(numpy.log2(maxDim))) if im.size[0]!=powerOf2 or im.size[1]!=powerOf2: if not forcePOW2: notSqr=True elif glob_vars.nImageResizes<reportNImageResizes: logging.warning("Image '%s' was not a square power-of-two image. Linearly interpolating to be %ix%i" %(tex, powerOf2, powerOf2)) glob_vars.nImageResizes+=1 im=im.resize([powerOf2,powerOf2],Image.BILINEAR) elif glob_vars.nImageResizes==reportNImageResizes: logging.warning("Multiple images have needed resizing - I'll stop bothering you!") im=im.resize([powerOf2,powerOf2],Image.BILINEAR) #is it Luminance or RGB? if pixFormat==GL.GL_ALPHA and im.mode!='L':#we have RGB and need Lum wasLum = True im = im.convert("L")#force to intensity (in case it was rgb) elif im.mode=='L': #we have lum and no need to change wasLum = True elif pixFormat==GL.GL_RGB: #we want RGB and might need to convert from CMYK or Lm #texture = im.tostring("raw", "RGB", 0, -1) im = im.convert("RGBA") wasLum=False if dataType==GL.GL_FLOAT: #convert from ubyte to float intensity = numpy.array(im).astype(numpy.float32)*0.0078431372549019607-1.0 # much faster to avoid division 2/255 else: intensity = numpy.array(im) if pixFormat==GL.GL_RGB and wasLum and dataType==GL.GL_FLOAT: #grating stim on good machine #keep as float32 -1:1 if sys.platform!='darwin' and stim.win.glVendor.startswith('nvidia'): #nvidia under win/linux might not support 32bit float internalFormat = GL.GL_RGB16F_ARB #could use GL_LUMINANCE32F_ARB here but check shader code? else:#we've got a mac or an ATI card and can handle 32bit float textures internalFormat = GL.GL_RGB32F_ARB #could use GL_LUMINANCE32F_ARB here but check shader code? data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float data[:,:,0] = intensity#R data[:,:,1] = intensity#G data[:,:,2] = intensity#B elif pixFormat==GL.GL_RGB and wasLum and dataType!=GL.GL_FLOAT and stim.useShaders: #was a lum image: stick with ubyte for speed internalFormat = GL.GL_RGB data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.ubyte)#initialise data array as a float data[:,:,0] = intensity#R data[:,:,1] = intensity#G data[:,:,2] = intensity#B elif pixFormat==GL.GL_RGB and wasLum and not stim.useShaders: #Grating on legacy hardware, or ImageStim with wasLum=True #scale by rgb and convert to ubyte internalFormat = GL.GL_RGB if stim.colorSpace in ['rgb', 'dkl', 'lms','hsv']: rgb=stim.rgb else: rgb=stim.rgb/127.5-1.0#colour is not a float - convert to float to do the scaling # if wasImage it will also have ubyte values for the intensity if wasImage: intensity = intensity/127.5-1.0 #scale by rgb data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float data[:,:,0] = intensity*rgb[0] + stim.rgbPedestal[0]#R data[:,:,1] = intensity*rgb[1] + stim.rgbPedestal[1]#G data[:,:,2] = intensity*rgb[2] + stim.rgbPedestal[2]#B #convert to ubyte data = float_uint8(stim.contrast*data) elif pixFormat==GL.GL_RGB and dataType==GL.GL_FLOAT: #probably a custom rgb array or rgb image internalFormat = GL.GL_RGB32F_ARB data = intensity elif pixFormat==GL.GL_RGB:# not wasLum, not useShaders - an RGB bitmap with no shader options internalFormat = GL.GL_RGB data = intensity #float_uint8(intensity) elif pixFormat==GL.GL_ALPHA: internalFormat = GL.GL_ALPHA if wasImage: data = intensity else: data = float_uint8(intensity) #check for RGBA textures if len(intensity.shape)>2 and intensity.shape[2] == 4: if pixFormat==GL.GL_RGB: pixFormat=GL.GL_RGBA if internalFormat==GL.GL_RGB: internalFormat=GL.GL_RGBA elif internalFormat==GL.GL_RGB32F_ARB: internalFormat=GL.GL_RGBA32F_ARB texture = data.ctypes#serialise #bind the texture in openGL GL.glEnable(GL.GL_TEXTURE_2D) GL.glBindTexture(GL.GL_TEXTURE_2D, id)#bind that name to the target GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_REPEAT) #makes the texture map wrap (this is actually default anyway) #important if using bits++ because GL_LINEAR #sometimes extrapolates to pixel vals outside range if interpolate: GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR) if useShaders:#GL_GENERATE_MIPMAP was only available from OpenGL 1.4 GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, data.shape[1],data.shape[0], 0, # [JRG] for non-square, want data.shape[1], data.shape[0] pixFormat, dataType, texture) else:#use glu GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST) GL.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, internalFormat, data.shape[1],data.shape[0], pixFormat, dataType, texture) # [JRG] for non-square, want data.shape[1], data.shape[0] else: GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_NEAREST) GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_NEAREST) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, data.shape[1],data.shape[0], 0, # [JRG] for non-square, want data.shape[1], data.shape[0] pixFormat, dataType, texture) GL.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE)#?? do we need this - think not! return wasLum
def createTexture(tex, id, pixFormat, stim, res=128, maskParams=None, forcePOW2=True, dataType=None): """ :params: id: is the texture ID pixFormat: GL.GL_ALPHA, GL.GL_RGB useShaders: bool interpolate: bool (determines whether texture will use GL_LINEAR or GL_NEAREST res: the resolution of the texture (unless a bitmap image is used) dataType: None, GL.GL_UNSIGNED_BYTE, GL_FLOAT. Only affects image files (numpy arrays will be float) For grating stimuli (anything that needs multiple cycles) forcePOW2 should be set to be True. Otherwise the wrapping of the texture will not work. """ """ Create an intensity texture, ranging -1:1.0 """ global _nImageResizes notSqr=False #most of the options will be creating a sqr texture wasImage=False #change this if image loading works useShaders = stim.useShaders interpolate = stim.interpolate if dataType==None: if useShaders and pixFormat==GL.GL_RGB: dataType = GL.GL_FLOAT else: dataType = GL.GL_UNSIGNED_BYTE if type(tex) == numpy.ndarray: #handle a numpy array #for now this needs to be an NxN intensity array intensity = tex.astype(numpy.float32) if intensity.max()>1 or intensity.min()<-1: logging.error('numpy arrays used as textures should be in the range -1(black):1(white)') if len(tex.shape)==3: wasLum=False else: wasLum = True ##is it 1D? if tex.shape[0]==1: stim._tex1D=True res=tex.shape[1] elif len(tex.shape)==1 or tex.shape[1]==1: stim._tex1D=True res=tex.shape[0] else: stim._tex1D=False #check if it's a square power of two maxDim = max(tex.shape) powerOf2 = 2**numpy.ceil(numpy.log2(maxDim)) if forcePOW2 and (tex.shape[0]!=powerOf2 or tex.shape[1]!=powerOf2): logging.error("Requiring a square power of two (e.g. 16x16, 256x256) texture but didn't receive one") core.quit() res=tex.shape[0] elif tex in [None,"none", "None"]: res=1 #4x4 (2x2 is SUPPOSED to be fine but generates wierd colors!) intensity = numpy.ones([res,res],numpy.float32) wasLum = True elif tex == "sin": onePeriodX, onePeriodY = numpy.mgrid[0:res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation intensity = numpy.sin(onePeriodY-pi/2) wasLum = True elif tex == "sqr":#square wave (symmetric duty cycle) onePeriodX, onePeriodY = numpy.mgrid[0:res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation sinusoid = numpy.sin(onePeriodY-pi/2) intensity = numpy.where(sinusoid>0, 1, -1) wasLum = True elif tex == "saw": intensity = numpy.linspace(-1.0,1.0,res,endpoint=True)*numpy.ones([res,1]) wasLum = True elif tex == "tri": intensity = numpy.linspace(-1.0,3.0,res,endpoint=True)#-1:3 means the middle is at +1 intensity[int(res/2.0+1):] = 2.0-intensity[int(res/2.0+1):]#remove from 3 to get back down to -1 intensity = intensity*numpy.ones([res,1])#make 2D wasLum = True elif tex == "sinXsin": onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:1j*res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation intensity = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2) wasLum = True elif tex == "sqrXsqr": onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:1j*res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation sinusoid = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2) intensity = numpy.where(sinusoid>0, 1, -1) wasLum = True elif tex == "circle": rad=makeRadialMatrix(res) intensity = (rad<=1)*2-1 fromFile=0 wasLum=True elif tex == "gauss": rad=makeRadialMatrix(res) sigma = 1/3.0; intensity = numpy.exp( -rad**2.0 / (2.0*sigma**2.0) )*2-1 #3sd.s by the edge of the stimulus fromFile=0 wasLum=True elif tex == "radRamp":#a radial ramp rad=makeRadialMatrix(res) intensity = 1-2*rad intensity = numpy.where(rad<-1, intensity, -1)#clip off the corners (circular) fromFile=0 wasLum=True elif tex == "raisedCos": # A raised cosine wasLum=True hamming_len = 1000 # This affects the 'granularity' of the raised cos # If no user input was provided: if maskParams is None: fringe_proportion = 0.2 # This one affects the proportion of the # stimulus diameter that is devoted to the # raised cosine. # Users can provide the fringe proportion through a dict, maskParams # input: else: fringe_proportion = maskParams['fringeWidth'] rad = makeRadialMatrix(res) intensity = numpy.zeros_like(rad) intensity[numpy.where(rad < 1)] = 1 raised_cos_idx = numpy.where( [numpy.logical_and(rad <= 1, rad >= 1-fringe_proportion)])[1:] # Make a raised_cos (half a hamming window): raised_cos = numpy.hamming(hamming_len)[:hamming_len/2] raised_cos -= numpy.min(raised_cos) raised_cos /= numpy.max(raised_cos) # Measure the distance from the edge - this is your index into the hamming window: d_from_edge = numpy.abs((1 - fringe_proportion)- rad[raised_cos_idx]) d_from_edge /= numpy.max(d_from_edge) d_from_edge *= numpy.round(hamming_len/2) # This is the indices into the hamming (larger for small distances from the edge!): portion_idx = (-1 * d_from_edge).astype(int) # Apply the raised cos to this portion: intensity[raised_cos_idx] = raised_cos[portion_idx] # Scale it into the interval -1:1: intensity = intensity - 0.5 intensity = intensity / numpy.max(intensity) #Sometimes there are some remaining artifacts from this process, get rid of them: artifact_idx = numpy.where(numpy.logical_and(intensity == -1, rad < 0.99)) intensity[artifact_idx] = 1 artifact_idx = numpy.where(numpy.logical_and(intensity == 1, rad > 0.99)) intensity[artifact_idx] = 0 else: if type(tex) in [str, unicode, numpy.string_]: # maybe tex is the name of a file: if not os.path.isfile(tex): logging.error("Couldn't find image file '%s'; check path?" %(tex)); logging.flush() raise OSError, "Couldn't find image file '%s'; check path? (tried: %s)" \ % (tex, os.path.abspath(tex))#ensure we quit try: im = Image.open(tex) im = im.transpose(Image.FLIP_TOP_BOTTOM) except IOError: logging.error("Found file '%s' but failed to load as an image" %(tex)); logging.flush() raise IOError, "Found file '%s' [= %s] but it failed to load as an image" \ % (tex, os.path.abspath(tex))#ensure we quit else: # can't be a file; maybe its an image already in memory? try: im = tex.copy().transpose(Image.FLIP_TOP_BOTTOM) # ? need to flip if in mem? except AttributeError: # nope, not an image in memory logging.error("Couldn't make sense of requested image."); logging.flush() raise AttributeError, "Couldn't make sense of requested image."#ensure we quit # at this point we have a valid im stim._origSize=im.size wasImage=True #is it 1D? if im.size[0]==1 or im.size[1]==1: logging.error("Only 2D textures are supported at the moment") else: maxDim = max(im.size) powerOf2 = int(2**numpy.ceil(numpy.log2(maxDim))) if im.size[0]!=powerOf2 or im.size[1]!=powerOf2: if not forcePOW2: notSqr=True elif _nImageResizes<reportNImageResizes: logging.warning("Image '%s' was not a square power-of-two image. Linearly interpolating to be %ix%i" %(tex, powerOf2, powerOf2)) _nImageResizes+=1 im=im.resize([powerOf2,powerOf2],Image.BILINEAR) elif _nImageResizes==reportNImageResizes: logging.warning("Multiple images have needed resizing - I'll stop bothering you!") im=im.resize([powerOf2,powerOf2],Image.BILINEAR) #is it Luminance or RGB? if im.mode=='L' and pixFormat==GL.GL_ALPHA: wasLum = True elif pixFormat==GL.GL_ALPHA:#we have RGB and need Lum wasLum = True im = im.convert("L")#force to intensity (in case it was rgb) elif pixFormat==GL.GL_RGB:#we have RGB and keep it that way #texture = im.tostring("raw", "RGB", 0, -1) im = im.convert("RGBA")#force to rgb (in case it was CMYK or L) wasLum=False if dataType==GL.GL_FLOAT: #convert from ubyte to float intensity = numpy.array(im).astype(numpy.float32)*0.0078431372549019607-1.0 # much faster to avoid division 2/255 else: intensity = numpy.array(im) if wasLum and intensity.shape!=im.size: intensity.shape=im.size if pixFormat==GL.GL_RGB and wasLum and dataType==GL.GL_FLOAT: #grating stim on good machine #keep as float32 -1:1 if sys.platform!='darwin' and stim.win.glVendor.startswith('nvidia'): #nvidia under win/linux might not support 32bit float internalFormat = GL.GL_RGB16F_ARB #could use GL_LUMINANCE32F_ARB here but check shader code? else:#we've got a mac or an ATI card and can handle 32bit float textures internalFormat = GL.GL_RGB32F_ARB #could use GL_LUMINANCE32F_ARB here but check shader code? data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float data[:,:,0] = intensity#R data[:,:,1] = intensity#G data[:,:,2] = intensity#B elif pixFormat==GL.GL_RGB and wasLum: #Grating on legacy hardware, or ImageStim with wasLum=True #scale by rgb and convert to ubyte internalFormat = GL.GL_RGB if stim.colorSpace in ['rgb', 'dkl', 'lms','hsv']: rgb=stim.rgb else: rgb=stim.rgb/127.5-1.0#colour is not a float - convert to float to do the scaling #scale by rgb data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float data[:,:,0] = intensity*rgb[0] + stim.rgbPedestal[0]#R data[:,:,1] = intensity*rgb[1] + stim.rgbPedestal[1]#G data[:,:,2] = intensity*rgb[2] + stim.rgbPedestal[2]#B #convert to ubyte data = float_uint8(stim.contrast*data) elif pixFormat==GL.GL_RGB and dataType==GL.GL_FLOAT: #probably a custom rgb array or rgb image internalFormat = GL.GL_RGB32F_ARB data = intensity elif pixFormat==GL.GL_RGB:# not wasLum, not useShaders - an RGB bitmap with no shader options internalFormat = GL.GL_RGB data = intensity #float_uint8(intensity) elif pixFormat==GL.GL_ALPHA: internalFormat = GL.GL_ALPHA if wasImage: data = intensity else: data = float_uint8(intensity) #check for RGBA textures if len(intensity.shape)>2 and intensity.shape[2] == 4: if pixFormat==GL.GL_RGB: pixFormat=GL.GL_RGBA if internalFormat==GL.GL_RGB: internalFormat=GL.GL_RGBA elif internalFormat==GL.GL_RGB32F_ARB: internalFormat=GL.GL_RGBA32F_ARB texture = data.ctypes#serialise #bind the texture in openGL GL.glEnable(GL.GL_TEXTURE_2D) GL.glBindTexture(GL.GL_TEXTURE_2D, id)#bind that name to the target GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_REPEAT) #makes the texture map wrap (this is actually default anyway) #important if using bits++ because GL_LINEAR #sometimes extrapolates to pixel vals outside range if interpolate: GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR) if useShaders:#GL_GENERATE_MIPMAP was only available from OpenGL 1.4 GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, data.shape[1],data.shape[0], 0, # [JRG] for non-square, want data.shape[1], data.shape[0] pixFormat, dataType, texture) else:#use glu GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST) GL.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, internalFormat, data.shape[1],data.shape[0], pixFormat, dataType, texture) # [JRG] for non-square, want data.shape[1], data.shape[0] else: GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_NEAREST) GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_NEAREST) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, data.shape[1],data.shape[0], 0, # [JRG] for non-square, want data.shape[1], data.shape[0] pixFormat, dataType, texture) GL.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE)#?? do we need this - think not! return wasLum