cellpose
cellpose copied to clipboard
[BUG] OpenCV does not support resizing of uint32 data types during model evaluation
Description
If cellpose detects over 65,535 masks, images are cast to uint32 and then rescaled using cv2. However, this is interpreted as type CV_32S (signed integer as opposed to unsigned) by opencv which does not support unsigned 32-bit CV_32U (doc). cv2 does not support resizing images with type CV_32S (doc).
This behaviour is caused in dynamics.py line 845, which casts to uint32 and is later resized in line 784.
Reproduce
Steps to reproduce the behavior in python 3.9.16, both on GPU and CPU
import numpy as np
import cv2
a = np.random.normal(size=(100, 200))
# SUCCEEDS: cast as unsigned int 8 bit
print(cv2.resize(a.astype(np.uint8), (50, 100)).shape)
# SUCCEEDS: cast as unsigned int 16 bit
print(cv2.resize(a.astype(np.uint16), (50, 100)).shape)
# FAILS: cast as unsigned int 32 bit
print(cv2.resize(a.astype(np.uint32), (50, 100)).shape)
This outputs the following log and error
(100, 50)
(100, 50)
---------------------------------------------------------------------------
error Traceback (most recent call last)
Cell In[75], line 13
10 print(cv2.resize(a.astype(np.uint16), (50, 100)).shape)
12 # FAILS: cast as unsigned int 32 bit
---> 13 print(cv2.resize(a.astype(np.uint32), (50, 100)).shape)
error: OpenCV(4.9.0) :-1: error: (-5:Bad argument) in function 'resize'
> Overload resolution failed:
> - src data type = uint32 is not supported
> - Expected Ptr<cv::UMat> for argument 'src'
System
- opencv-python==4.9.0.80
- numpy>=1.24.0
Full Error
Below is the complete output when running this code:
masks, flows, styles, diams = model.eval(
img,
diameter=0,
flow_threshold=1,
cellprob_threshold=-1,
channels=[channel_cyt, channel_nuc],
)
This yields the following log and output
2024-05-13 10:46:54 - ** TORCH CUDA version installed and working. **
2024-05-13 10:46:54 - >>>> using GPU
2024-05-13 10:46:54 - >> cyto << model set to be used
2024-05-13 10:46:55 - >>>> model diam_mean = 30.000 (ROIs rescaled to this size during training)
2024-05-13 10:46:55 - Load Mesmer model
2024-05-13 10:46:55 - channels set to [0, 3]
2024-05-13 10:46:55 - ~~~ ESTIMATING CELL DIAMETER(S) ~~~
2024-05-13 10:59:57 - WARNING: image is very large, not using gpu to compute flows from masks for QC step flow_threshold
2024-05-13 10:59:57 - turn off QC step with flow_threshold=0 if too slow
2024-05-13 11:03:41 - more than 65535 masks in image, masks returned as np.uint32
---------------------------------------------------------------------------
error Traceback (most recent call last)
Cell In[26], line 1
----> 1 xt.cellpose.segment_cellpose(
2 path_img=xt.data.get_aligned_img(paths),
3 channel_nuc=3,
4 channel_cyt=0,
5 path_out=paths["cellpose"]
6 )
File /lilac/data/sail/projects/ongoing/triage/xenium_jose/scripts/tobi/xenium_tools/cellpose.py:41, in segment_cellpose(path_img, channel_nuc, channel_cyt, path_out)
38 logging.info("Normalising image")
39 img = xt.utils.norm_uint8(img)
---> 41 # Load CellPose model
42 logging.info("Running Cellpose")
43 model = models.Cellpose(gpu=core.use_gpu(), model_type=model_type)
File /data/sail/krauset/.pyenv/versions/xenium/lib/python3.9/site-packages/cellpose/models.py:164, in Cellpose.eval(self, x, batch_size, channels, channel_axis, invert, normalize, diameter, do_3D, **kwargs)
162 tic = time.time()
163 models_logger.info("~~~ ESTIMATING CELL DIAMETER(S) ~~~")
--> 164 diams, _ = self.sz.eval(x, channels=channels, channel_axis=channel_axis,
165 batch_size=batch_size, normalize=normalize,
166 invert=invert)
167 diameter = None
168 models_logger.info("estimated cell diameter(s) in %0.2f sec" %
169 (time.time() - tic))
File /data/sail/krauset/.pyenv/versions/xenium/lib/python3.9/site-packages/cellpose/models.py:678, in SizeModel.eval(self, x, channels, channel_axis, normalize, invert, augment, tile, batch_size, progress)
674 diam_style = self._size_estimation(np.array(styles))
675 diam_style = self.diam_mean if (diam_style == 0 or
676 np.isnan(diam_style)) else diam_style
--> 678 masks = self.cp.eval(
679 x, compute_masks=True, channels=channels, channel_axis=channel_axis,
680 normalize=normalize, invert=invert, augment=augment, tile=tile,
681 batch_size=batch_size, resample=False,
682 rescale=self.diam_mean / diam_style if self.diam_mean > 0 else 1,
683 diameter=None, interp=False)[0]
685 diam = utils.diameters(masks)[0]
686 diam = self.diam_mean if (diam == 0 or np.isnan(diam)) else diam
File /data/sail/krauset/.pyenv/versions/xenium/lib/python3.9/site-packages/cellpose/models.py:410, in CellposeModel.eval(self, x, batch_size, resample, channels, channel_axis, z_axis, normalize, invert, rescale, diameter, flow_threshold, cellprob_threshold, do_3D, anisotropy, stitch_threshold, min_size, niter, augment, tile, tile_overlap, bsize, interp, compute_masks, progress)
407 diameter = self.diam_labels
408 rescale = self.diam_mean / diameter
--> 410 masks, styles, dP, cellprob, p = self._run_cp(
411 x, compute_masks=compute_masks, normalize=normalize, invert=invert,
412 rescale=rescale, resample=resample, augment=augment, tile=tile,
413 tile_overlap=tile_overlap, bsize=bsize, flow_threshold=flow_threshold,
414 cellprob_threshold=cellprob_threshold, interp=interp, min_size=min_size,
415 do_3D=do_3D, anisotropy=anisotropy, niter=niter,
416 stitch_threshold=stitch_threshold)
418 flows = [plot.dx_to_circ(dP), dP, cellprob, p]
419 return masks, flows, styles
File /data/sail/krauset/.pyenv/versions/xenium/lib/python3.9/site-packages/cellpose/models.py:517, in CellposeModel._run_cp(self, x, compute_masks, normalize, invert, niter, rescale, resample, augment, tile, tile_overlap, cellprob_threshold, bsize, flow_threshold, min_size, interp, anisotropy, do_3D, stitch_threshold)
514 iterator = trange(nimg, file=tqdm_out,
515 mininterval=30) if nimg > 1 else range(nimg)
516 for i in iterator:
--> 517 outputs = dynamics.resize_and_compute_masks(
518 dP[:, i],
519 cellprob[i],
520 niter=niter,
521 cellprob_threshold=cellprob_threshold,
522 flow_threshold=flow_threshold,
523 interp=interp,
524 resize=resize,
525 min_size=min_size if stitch_threshold == 0 or nimg == 1 else
526 -1, # turn off for 3D stitching
527 device=self.device if self.gpu else None)
528 masks.append(outputs[0])
529 p.append(outputs[1])
File /data/sail/krauset/.pyenv/versions/xenium/lib/python3.9/site-packages/cellpose/dynamics.py:785, in resize_and_compute_masks(dP, cellprob, p, niter, cellprob_threshold, flow_threshold, interp, do_3D, min_size, resize, device)
779 mask, p = compute_masks(dP, cellprob, p=p, niter=niter,
780 cellprob_threshold=cellprob_threshold,
781 flow_threshold=flow_threshold, interp=interp, do_3D=do_3D,
782 min_size=min_size, device=device)
784 if resize is not None:
--> 785 mask = transforms.resize_image(mask, resize[0], resize[1],
786 interpolation=cv2.INTER_NEAREST)
787 p = np.array([
788 transforms.resize_image(pi, resize[0], resize[1],
789 interpolation=cv2.INTER_NEAREST) for pi in p
790 ])
792 return mask, p
File /data/sail/krauset/.pyenv/versions/xenium/lib/python3.9/site-packages/cellpose/transforms.py:727, in resize_image(img0, Ly, Lx, rsz, interpolation, no_channels)
725 imgs[i] = cv2.resize(img, (Lx, Ly), interpolation=interpolation)
726 else:
--> 727 imgs = cv2.resize(img0, (Lx, Ly), interpolation=interpolation)
728 return imgs
error: OpenCV(4.9.0) :-1: error: (-5:Bad argument) in function 'resize'
> Overload resolution failed:
> - src data type = uint32 is not supported
> - Expected Ptr<cv::UMat> for argument 'src'