Source code for fastplotlib.graphics._features._image
fromitertoolsimportproductfrommathimportceilimportnumpyasnpimportpygfxfrom._baseimportGraphicFeature,FeatureEvent,block_reentrancefrom...utilsimport(make_colors,get_cmap_texture,)# manages an array of 8192x8192 Textures representing chunks of an image
[docs]classTextureArray(GraphicFeature):def__init__(self,data,isolated_buffer:bool=True):super().__init__()data=self._fix_data(data)shared=pygfx.renderers.wgpu.get_shared()self._texture_limit_2d=shared.device.limits["max-texture-dimension-2d"]ifisolated_buffer:# useful if data is read-only, example: memmapsself._value=np.zeros(data.shape,dtype=data.dtype)self.value[:]=data[:]else:# user's input array is used as the bufferself._value=data# data start indices for each Textureself._row_indices=np.arange(0,ceil(self.value.shape[0]/self._texture_limit_2d)*self._texture_limit_2d,self._texture_limit_2d,)self._col_indices=np.arange(0,ceil(self.value.shape[1]/self._texture_limit_2d)*self._texture_limit_2d,self._texture_limit_2d,)# buffer will be an array of texturesself._buffer:np.ndarray[pygfx.Texture]=np.empty(shape=(self.row_indices.size,self.col_indices.size),dtype=object)self._iter=None# iterate through each chunk of passed `data`# create a pygfx.Texture from this chunkfor_,buffer_index,data_sliceinself:texture=pygfx.Texture(self.value[data_slice],dim=2)self.buffer[buffer_index]=textureself._shared:int=0@propertydefvalue(self)->np.ndarray:returnself._value
@propertydefbuffer(self)->np.ndarray[pygfx.Texture]:returnself._buffer@propertydefrow_indices(self)->np.ndarray:""" row indices that are used to chunk the big data array into individual Textures on the GPU """returnself._row_indices@propertydefcol_indices(self)->np.ndarray:""" column indices that are used to chunk the big data array into individual Textures on the GPU """returnself._col_indices@propertydefshared(self)->int:returnself._shareddef_fix_data(self,data):ifdata.ndimnotin(2,3):raiseValueError("image data must be 2D with or without an RGB(A) dimension, i.e. ""it must be of shape [rows, cols], [rows, cols, 3] or [rows, cols, 4]")# let's just cast to float32 alwaysreturndata.astype(np.float32)def__iter__(self):self._iter=product(enumerate(self.row_indices),enumerate(self.col_indices))returnselfdef__next__(self)->tuple[pygfx.Texture,tuple[int,int],tuple[slice,slice]]:""" Iterate through each Texture within the texture array Returns ------- Texture, tuple[int, int], tuple[slice, slice] | Texture: pygfx.Texture | tuple[int, int]: chunk index, i.e corresponding index of ``self.buffer`` array | tuple[slice, slice]: data slice of big array in this chunk and Texture """(chunk_row,data_row_start),(chunk_col,data_col_start)=next(self._iter)# indices for to self.buffer for this chunkchunk_index=(chunk_row,chunk_col)# stop indices of big data array for this chunkrow_stop=min(self.value.shape[0],data_row_start+self._texture_limit_2d)col_stop=min(self.value.shape[1],data_col_start+self._texture_limit_2d)# row and column slices that slice the data for this chunk from the big data arraydata_slice=(slice(data_row_start,row_stop),slice(data_col_start,col_stop))# texture for this chunktexture=self.buffer[chunk_index]returntexture,chunk_index,data_slicedef__getitem__(self,item):returnself.value[item]@block_reentrancedef__setitem__(self,key,value):self.value[key]=valuefortextureinself.buffer.ravel():texture.update_range((0,0,0),texture.size)event=FeatureEvent("data",info={"key":key,"value":value})self._call_event_handlers(event)def__len__(self):returnself.buffer.size
[docs]classImageCmap(GraphicFeature):"""colormap for texture"""def__init__(self,value:str):self._value=valueself.texture=get_cmap_texture(value)super().__init__()@propertydefvalue(self)->str:returnself._value@block_reentrancedefset_value(self,graphic,value:str):new_colors=make_colors(256,value)graphic._material.map.texture.data[:]=new_colorsgraphic._material.map.texture.update_range((0,0,0),size=(256,1,1))self._value=valueevent=FeatureEvent(type="cmap",info={"value":value})self._call_event_handlers(event)
[docs]classImageInterpolation(GraphicFeature):"""Image interpolation method"""def__init__(self,value:str):self._validate(value)self._value=valuesuper().__init__()def_validate(self,value):ifvaluenotin["nearest","linear"]:raiseValueError("`interpolation` must be one of 'nearest' or 'linear'")@propertydefvalue(self)->str:returnself._value@block_reentrancedefset_value(self,graphic,value:str):self._validate(value)graphic._material.interpolation=valueself._value=valueevent=FeatureEvent(type="interpolation",info={"value":value})self._call_event_handlers(event)
[docs]classImageCmapInterpolation(GraphicFeature):"""Image cmap interpolation method"""def__init__(self,value:str):self._validate(value)self._value=valuesuper().__init__()def_validate(self,value):ifvaluenotin["nearest","linear"]:raiseValueError("`cmap_interpolation` must be one of 'nearest' or 'linear'")@propertydefvalue(self)->str:returnself._value@block_reentrancedefset_value(self,graphic,value:str):self._validate(value)# common material for all image tilesgraphic._material.map.min_filter=valuegraphic._material.map.mag_filter=valueself._value=valueevent=FeatureEvent(type="cmap_interpolation",info={"value":value})self._call_event_handlers(event)