Skip to content

Commit bdf6c1e

Browse files
committed
updated rgb slice
1 parent 12d1b40 commit bdf6c1e

6 files changed

Lines changed: 80 additions & 60 deletions

File tree

src/pymapmanager/interface/stackWidgets/base/stacktoolbar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ def plotMenuChange(self, action):
354354
def setCurrentChannel(self, channelIdx):
355355
""" set current channel selected
356356
"""
357-
logger.info(f"setCurrentChannel {channelIdx}")
357+
logger.info(f"setCurrentChannel {channelIdx} {type(channelIdx)}")
358358
self._currentChannel = channelIdx
359359

360360
def getCurrentChannel(self):

src/pymapmanager/interface/stackWidgets/histogramWidget2.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ def _spinBoxValueChanged(self):
259259
def _updateContrast(self, theMin, theMax):
260260
# set contrast in metadata
261261
if self.isRgb:
262-
self._myStack.getChannelMetadata(self._channelIdx).setValue('minAutoContrast-rgb', theMin)
263-
self._myStack.getChannelMetadata(self._channelIdx).setValue('maxAutoContrast-rgb', theMax)
262+
self._myStack.getChannelMetadata(self._channelIdx).setValue('minAutoContrast_rgb', theMin)
263+
self._myStack.getChannelMetadata(self._channelIdx).setValue('maxAutoContrast_rgb', theMax)
264264
else:
265265
self._myStack.getChannelMetadata(self._channelIdx).setUserContrast(theMin, theMax)
266266

@@ -322,8 +322,8 @@ def _refreshContrast(self):
322322

323323
globalMin = 0
324324
if self.isRgb:
325-
minContrast = _channelMetadata.getValue('minAutoContrast-rgb')
326-
maxContrast = _channelMetadata.getValue('maxAutoContrast-rgb')
325+
minContrast = _channelMetadata.getValue('minAutoContrast_rgb')
326+
maxContrast = _channelMetadata.getValue('maxAutoContrast_rgb')
327327
globalMax = 256
328328
else:
329329
minContrast, maxContrast = _channelMetadata.getUserContrast()

src/pymapmanager/interface/stackWidgets/imagePlotWidget2.py

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def __init__(self, stackWidget):
6464
"""
6565
super().__init__(stackWidget)
6666

67-
self._myStack = stackWidget.getStack()
67+
self._myStack: pymapmanager.stack = stackWidget.getStack()
6868
self._displayOptionsDict = stackWidget._displayOptionsDict
6969

7070
self._currentSlice = 0
@@ -634,10 +634,11 @@ def _setColorLut(self, update=False):
634634

635635
def _setContrast(self):
636636
if self._channelIsRGB():
637+
logger.warning(f'TODO: hard coding min/max for rgb -->> fix')
637638
tmpLevelList = [] # list of [min,max]
638639
for channelIdx in self._myStack.getChannelKeys():
639-
# oneMinContrast = self._myStack.contrast.getValue(channelIdx, 'minAutoContrast-rgb')
640-
# oneMaxContrast = self._myStack.contrast.getValue(channelIdx, 'maxAutoContrast-rgb')
640+
# oneMinContrast = self._myStack.contrast.getValue(channelIdx, 'minAutoContrast_rgb')
641+
# oneMaxContrast = self._myStack.contrast.getValue(channelIdx, 'maxAutoContrast_rgb')
641642
oneMinContrast = 0
642643
oneMaxContrast = 200
643644

@@ -659,8 +660,8 @@ def _setContrast(self):
659660
self._myImage.setLevels(levelList, update=True)
660661

661662
else:
662-
logger.warning('abb turned off contrast, using auto contrast')
663-
return
663+
# logger.warning('abb turned off contrast, using auto contrast')
664+
# return
664665

665666
# one channel
666667
minUserContrast, maxUserContrast = \
@@ -694,7 +695,7 @@ def _setSlice(self, sliceNumber : int, doEmit = True):
694695
TODO: get rid of doEmit, use _blockSlots
695696
"""
696697

697-
logger.warning(f'xxx EXPENSIVE ONLY CALL ONCE sliceNumber:{sliceNumber} doEmit:{doEmit}')
698+
# logger.warning(f'xxx EXPENSIVE ONLY CALL ONCE sliceNumber:{sliceNumber} doEmit:{doEmit}')
698699

699700
if isinstance(sliceNumber, float):
700701
sliceNumber = int(sliceNumber)
@@ -707,58 +708,69 @@ def _setSlice(self, sliceNumber : int, doEmit = True):
707708
upDownSlices = self._displayOptionsDict['windowState']['zPlusMinus']
708709

709710
if self._channelIsRGB():
710-
logger.warning('TODO: remove hard coded two channel assumption for rgb')
711-
logger.warning(' use core loader channel metadata to get actual channel keys.')
711+
logger.info(f'-->> setSlice rgb sliceNumber:{sliceNumber}')
712+
# logger.warning('TODO: remove hard coded two channel assumption for rgb')
713+
# logger.warning(' use core loader channel metadata to get actual channel keys.')
712714

713-
# ch1_image = self._myStack.getImageSlice(imageSlice=sliceNumber, channelIdx=0)
714-
# ch2_image = self._myStack.getImageSlice(imageSlice=sliceNumber, channelIdx=1)
715-
ch0_image = self._myStack.getMaxProjectSlice(sliceNumber,
716-
channelIdx=1,
717-
upSlices=upDownSlices, downSlices=upDownSlices,
718-
func=np.max)
719-
ch1_image = self._myStack.getMaxProjectSlice(sliceNumber,
720-
channelIdx=2,
721-
upSlices=upDownSlices, downSlices=upDownSlices,
722-
func=np.max)
715+
channelKeys = self._myStack.getChannelKeys()
723716

724-
# logger.info(f"check ch0_image min {ch1_image.min()} max {ch1_image.max()}")
717+
if len(channelKeys) < 2:
718+
logger.error(f'Expected at least 2 channels for RGB, found {len(channelKeys)}')
719+
return
725720

726-
# rgb requires 8-bit images
727-
ch0_image = ch0_image/ch0_image.max() * 2**8
728-
ch1_image = ch1_image/ch1_image.max() * 2**8
729-
730-
ch0_image = ch0_image.astype(np.uint8)
731-
ch1_image = ch1_image.astype(np.uint8)
721+
_shape = self._myStack.getMetadata().shape # (z, y, x)
722+
_xShape = _shape[2]
723+
_yShape = _shape[1]
732724

733-
# print('2) ch1_image:', ch1_image.shape, ch1_image.dtype)
725+
# make the 3d slice we will return
726+
sliceImage = np.zeros((_xShape,_yShape,3), dtype=np.uint8)
727+
728+
_redIdx = 0
729+
_greenIdx = 1
730+
_blueIdx = 2
731+
732+
maxImageDict = {}
733+
channelColorDict = {}
734+
for sliceChannel, channelKey in enumerate(channelKeys):
735+
ch_image = self._myStack.getMaxProjectSlice(sliceNumber,
736+
channelIdx=channelKey,
737+
upSlices=upDownSlices, downSlices=upDownSlices,
738+
func=np.max)
739+
# rgb requires 8-bit images
740+
ch_image = ch_image/ch_image.max() * 2**8
741+
ch_image = ch_image.astype(np.uint8)
742+
743+
# here is where we need a user option for channel color
744+
sliceImage[:,:,sliceChannel] = ch_image
734745

735-
_xShape = ch0_image.shape[0]
736-
_yShape = ch0_image.shape[1]
737-
sliceImage = np.ndarray((_xShape,_yShape,3))
746+
maxImageDict[channelKey] = ch_image
747+
748+
# redundant, all channels have same shape
749+
# _xShape = ch_image.shape[0]
750+
# _yShape = ch_image.shape[1]
751+
752+
_color = self._myStack.getChannelColor(channelKey)
753+
_color = self.colorToHex(_color)
754+
_color = to_rgb(_color) # for ch0_image
755+
channelColorDict[channelKey] = _color
738756

739757
# # magenta is blue + red
740758
# sliceImage[:,:,0] = ch1_image # red
741759
# sliceImage[:,:,1] = ch0_image # green
742760
# sliceImage[:,:,2] = ch1_image # blue
743761

744-
# Get first two channel colors
745-
color0 = self._myStack.getChannelColor(1)
746-
color1 = self._myStack.getChannelColor(2)
747-
748-
color0 = self.colorToHex(color0)
749-
color1 = self.colorToHex(color1)
750-
logger.info(f"color0 {color0} color1 {color1}")
751-
752-
# Convert hex color to RGB arrays
753-
rgb0 = to_rgb(color0) # for ch0_image
754-
rgb1 = to_rgb(color1) # for ch1_image
755-
756762
brightness_factor = 2
757763
for i in range(3): # R, G, B
764+
# channelKeys is a list (we don't care if it is [int] or [str])
765+
if i == 2:
766+
_channelKey = channelKeys[1]
767+
else:
768+
_channelKey = channelKeys[i]
769+
758770
sliceImage[:,:,i] = (
759771
brightness_factor *
760-
(ch0_image * (rgb0[i])) +
761-
(ch1_image * (rgb1[i])) * i
772+
(maxImageDict[_channelKey] * (channelColorDict[_channelKey][0])) +
773+
(maxImageDict[_channelKey] * (channelColorDict[_channelKey][1])) * i
762774
).clip(0, 255).astype(np.uint8)
763775

764776
else:

src/pymapmanager/interface/stackWidgets/stackWidget.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,10 +1346,12 @@ def loadInNewChannel(self, path:Optional[str] = None):
13461346
# refresh stackToolBar
13471347
self._topToolbar._setStack(theStack=self._stack)
13481348

1349-
_pmmEvent = pmmEvent(pmmEventType.setColorChannel, self)
1350-
_pmmEvent.setColorChannel(newChannelNum)
1351-
self.setColorChannelEvent(_pmmEvent) # chooses it in toptool bar
1352-
self.slot_setChannel(newChannelNum) # actually changes image in imageplotwidget
1349+
# abb 20250808
1350+
if newChannelNum is not None:
1351+
_pmmEvent = pmmEvent(pmmEventType.setColorChannel, self)
1352+
_pmmEvent.setColorChannel(newChannelNum)
1353+
self.setColorChannelEvent(_pmmEvent) # chooses it in toptool bar
1354+
self.slot_setChannel(newChannelNum) # actually changes image in imageplotwidget
13531355

13541356
def deleteChannel(self, channelIdx):
13551357
""" Delete channel in backend
@@ -1366,7 +1368,8 @@ def deleteChannel(self, channelIdx):
13661368
_imagePlotWidget = self._widgetDict[self._imagePlotName]
13671369
_imagePlotWidget.hide()
13681370

1369-
currentChannel = self._topToolbar.getCurrentChannel() # top tool bar is 1 based, incoming channelIdx is 0
1371+
# top tool bar is 1 based, incoming channelIdx is 0
1372+
currentChannel = self._topToolbar.getCurrentChannel()
13701373
logger.info(f"currentChannel {currentChannel}")
13711374
logger.info(f"channelIdx {channelIdx}")
13721375

src/pymapmanager/stack.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def __init__(self,
4343
self.getImageSlice(0, defaultChannelIdx)
4444

4545
def importChannels(self, importPath: str):
46-
"""Import channels from a .mmap file.
46+
"""Import channels from a file.
4747
4848
Parameters
4949
----------
@@ -54,7 +54,7 @@ def importChannels(self, importPath: str):
5454

5555
# import channels from another .mmap file
5656
# this will add channels to the current timepoint
57-
self._fullMap.importChannels(importPath, self)
57+
self._fullMap.importChannels(importPath, self.timepoint)
5858

5959
# important (this refreshes timepoint for new aggregate columns)
6060
self.getPointAnnotations().updateChannel()
@@ -105,7 +105,7 @@ def updateChannelName(self, channelIdx: int, newChannelName: str):
105105
def getTimeSeriesCore(self) -> pymapmanager.TimeSeriesCore:
106106
return self._fullMap
107107

108-
def getMetadata(self) -> TimepointMetadata: #Metadata:
108+
def getMetadata(self) -> TimepointMetadata:
109109
"""Get metadata from the core map.
110110
"""
111111
return self._fullMap.getTimepointMetadata(self.timepoint)
@@ -191,7 +191,7 @@ def getImageSlice(self,
191191

192192
def getMaxProjectSlice(self,
193193
imageSlice : int,
194-
channelIdx : int = 1,
194+
channelIdx : int,
195195
upSlices : int = 1,
196196
downSlices : int = 1,
197197
func = np.max
@@ -205,7 +205,12 @@ def getMaxProjectSlice(self,
205205
downSlices:
206206
func: Reference to np funtion to use like np.max
207207
"""
208-
208+
# make sure we have channelId in metadata
209+
if channelIdx not in self.getChannelKeys():
210+
logger.error(f'channelIdx {channelIdx} not in channel keys {self.getChannelKeys()}')
211+
return None
212+
213+
# make sure imageSlice is an integer
209214
if not isinstance(imageSlice, int):
210215
#logger.warning('not an integer, converting')
211216
imageSlice = int(imageSlice)
@@ -294,7 +299,7 @@ def saveAs(self, path):
294299
def getLastSaveTime(self):
295300
return self._fullMap.getLastSaveTime()
296301

297-
def getChannelKeys(self) -> List[int]:
302+
def getChannelKeys(self) -> list[int]:
298303
"""Get list of channel keys.
299304
"""
300305
return self.getMetadata().channelKeys

src/pymapmanager/timeseriesCore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ def importChannels(self, importPath:str, time:int):
347347
# elif isinstance(self._fullMap._images, MultiImageLoader):
348348
# self._fullMap.loader.appendChannels(importPath, time)
349349

350-
logger.warning('abb imageImport')
350+
logger.warning(f'abb imageImport importPath {importPath} time:{time}')
351351
channelNum = self._fullMap.loader.importChannel(importPath, time)
352352
if channelNum is not None:
353353
return channelNum

0 commit comments

Comments
 (0)