Example #1
0
    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)
Example #2
0
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
Example #3
0
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))
Example #4
0
    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)
Example #5
0
    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
Example #6
0
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))
Example #7
0
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