john-p / wsic Goto Github PK
View Code? Open in Web Editor NEWWhole Slide image (WSI) conversion for brightfield histology images
License: MIT License
Whole Slide image (WSI) conversion for brightfield histology images
License: MIT License
I used the wsic tool to convert a jp2 image to tiff. This was a test because I want to use the tool to convert multiple images and upload them to WASABI and the PathLAKE Portal. However, I've noticed that the testing image was not displayed on WASABI. I tried to investigate the issue by reading the image locally and then found out that I was not able to create the thumbnail because it was timing out (taking more than 10misn before crashing). There were no error logs during the conversion of the image, so I expect that it was successful. Also, I managed to convert the image using another tool, therefore the original image is not corrupted and not causing this issue.
I have included all the commands that I have used below. I have also uploaded the original and converted image to OneDrive here. @John-P you should be able to access these images. If you can't please let me know.
Command that I used to convert the jp2 image to tiff
wsic convert -i /input/path/image.jp2 -o /output/path/image.tiff -rt 4096 4096 -c jpeg -w 10 --no-ome --overwrite -to 60
Command that I used to read the image using tiatoolbox (Works fine and runs in 0.1s)
wsi = OpenSlideWSIReader(
input_img='/input/path/to/image.tiff')
wsi_info = wsi.info.as_dict()
# Print one item per line
print(*list(wsi_info.items()), sep='\n')
Command that I used to create and display the thumbnail of the image using tiatoolbox (Times out)
wsi_thumb = wsi.slide_thumbnail()
plt.imshow(wsi_thumb)
plt.axis('off')
plt.show()
Command that I used to read the image using OpenSlide directly (Works fine and runs in 0.1s)
openslide_wsi = openslide.OpenSlide(filename='/input/path/to/image.tiff')
Command that I used to create and display the thumbnail of the image using OpenSlide (Times out)
openslide_thumb = openslide_wsi.get_thumbnail(size=(256, 256))
plt.imshow(openslide_thumb)
plt.axis('off')
plt.show()
note: I think this might be the reason why the image cannot be displayed on WASABI. Link to WASABI collection with the converted images: here. @John-P I have given permissions to your wasabi user for this collection. Let me know if it doesn't work.
Hello,
I attempted to convert a DICOM wsi to svs using the following command and it failed:
wsic convert -i ANONU3UAJH1JJ_Leica/ -o convert_test/test2.svs -c jpeg
I manually verified that the DICOM image I was trying to convert was readable on python using wsidicom.
Any ideas on how to do this? Any help would be much appreciated!
Thanks,
Petros
Reading: 0%| | 0/28272 [00:00<?, ?it/s$
data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/wsic/readers.py:385: UserWarning: Failed to get next tile after 100 attempts. Dumping debug information. | 0/113088 [00:00<?, ?it/s$
warnings.warn(
Reader Shape (58258, 126761, 3)
Read Tile Size (512, 512)
Yield Tile Size (256, 256)
Read Mosaic Shape (114, 248)
Yield Mosaic Shape (228, 496)
Read Index (0, 0)
Yield Index (0, 0)
Remaining Reads (:10) [(0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9), (0, 10), (0, 11), (0, 12)]
Enqueued {(0, 1), (0, 2), (0, 0)}
Reordering Dict (keys) dict_keys([])
Queue Size 0
Intermediate Read slices (slice(0, 256, None), slice(0, 256, None))
Reading: 0%| | 0/28272 [00:10<?, ?it/s$
Writing: 0%| | 0/113088 [00:11<?, ?it/s$
Traceback (most recent call last):
File "/data/home/pliako/anaconda3/envs/wsic/bin/wsic", line 8, in <module>
sys.exit(main())
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1128, in __call__
return self.main(*args, **kwargs)
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1053, in main
rv = self.invoke(ctx)
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1659, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1395, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 754, in invoke
return __callback(*args, **kwargs)
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/wsic/cli.py", line 223, in convert
writer.copy_from_reader(
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/wsic/writers.py", line 942, in copy_from_reader
tif.write(
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/tifffile/tifffile.py", line 2556, in write
chunk = next(dataiter)
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/tqdm/std.py", line 1195, in __iter__
for obj in iterable:
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/wsic/readers.py", line 406, in __next__
raise IOError(f"Tile read timed out at index {self.yield_index}")
OSError: Tile read timed out at index (0, 0)
Exception ignored in: <function MultiProcessTileIterator.__del__ at 0x7f179e266f70>
Traceback (most recent call last):
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/wsic/readers.py", line 509, in __del__
self.close()
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/site-packages/wsic/readers.py", line 501, in close
executor.map(lambda p: p.join(1), self.processes)
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/concurrent/futures/_base.py", line 598, in map
fs = [self.submit(fn, *args) for args in zip(*iterables)]
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/concurrent/futures/_base.py", line 598, in <listcomp>
fs = [self.submit(fn, *args) for args in zip(*iterables)]
File "/data/home/pliako/anaconda3/envs/wsic/lib/python3.9/concurrent/futures/thread.py", line 169, in submit
raise RuntimeError('cannot schedule new futures after '
RuntimeError: cannot schedule new futures after interpreter shutdown
1st Machine
2nd Machine
I was trying to convert a JP2 image (size: 71.3Mb) to tiff. I tested the same command on two different machines
wsic convert -i ${input} -o ${output} -rt 4096 4096 -w 4 --overwrite --no-ome -to 600
/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/wsic/readers.py:265: UserWarning: Failed to get next tile after 100 attempts. Dumping debug information.
warnings.warn(
Reader Shape (161864, 88832, 3)
Read Tile Size (4096, 4096)
Yield Tile Size (256, 256)
Read Mosaic Shape (40, 22)
Yield Mosaic Shape (633, 347)
Read Index (0, 0)
Yield Index (0, 0)
Remaining Reads (:10) [(0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9), (0, 10), (0, 11)]
Enqueued {(0, 1), (0, 0)}
Reordering Dict (keys) dict_keys([])
Queue Size 0
Intermediate Read slices (slice(0, 256, None), slice(0, 256, None))
Traceback (most recent call last):
File "/home/giorgos-tia/.conda/envs/wsic/bin/wsic", line 8, in <module>
sys.exit(main())
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
return self.main(*args, **kwargs)
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/wsic/cli.py", line 126, in convert
writer.copy_from_reader(reader, read_tile_size=read_tile_size, num_workers=workers)
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/wsic/writers.py", line 513, in copy_from_reader
tif.write(
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/tifffile/tifffile.py", line 2660, in write
for tileindex, chunk in enumerate(
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/tifffile/tifffile.py", line 16817, in encode_tiles
tile = next(tileiter)
File "/home/giorgos-tia/.conda/envs/wsic/lib/python3.9/site-packages/wsic/readers.py", line 284, in __next__
raise Exception(f"Failed to yield tile {self.yield_index}")
Exception: Failed to yield tile (0, 0)
When trying to create a pyramid TIFF image from a single-level tiff image, MPP got lost in the output TIFF file.
wsic convert -i in.tiff -o test_out.tiff -rt 4096 4096 -w 20 --no-ome --overwrite -to 180 -c jpeg -d 2 -d 4 -d 8
I debugged a bit and found out this function call caused the error. Inside ppu2mpp function, the actual unit passed was "centimeter" instead of "cm" according to openslide standard. After adding "centimeter": 1e4,
to L238, the output got corrected.
As a user I would like to convert a WSI in Aperio SVS format to Hamamatsu NDPI file. Is this possible?
By inspecting the jp2 metadata I see the Capture X Resolution
is 36364 and the unit is cm. The corresponding MPP is 0.275
However, In the code, the resolution box reads 3636400 and an MPP of approx. 0.00275.
wsic convert -i in.jp2 -o out.tiff -rt 4096 4096 -w 2 --no-ome --overwrite -to 180 -c jpeg -cl 95 -d 2 -d 4 -d 8
I was trying to convert a JP2 image (size: 71.3Mb) to tiff. The wsic library failed because the Queues library threw a NotImplementedError. The creators of the library are aware of this issue since the code that throws the error has this comment
def qsize(self):
# Raises NotImplementedError on Mac OSX because of broken sem_getvalue()
return self._maxsize - self._sem._semlock._get_value()
Command
wsic convert -i ./<input_path>.jp2 -o ./1.tiff -rt 4096 4096 -w 1 --overwrite --no-ome -to 600
Error
Traceback (most recent call last):
File "/opt/miniconda3/envs/wsic/bin/wsic", line 8, in <module>
sys.exit(main())
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
return self.main(*args, **kwargs)
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/wsic/cli.py", line 139, in convert
writer.copy_from_reader(
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/wsic/writers.py", line 527, in copy_from_reader
tif.write(
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/tifffile/tifffile.py", line 2660, in write
for tileindex, chunk in enumerate(
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/tifffile/tifffile.py", line 16817, in encode_tiles
tile = next(tileiter)
File "/opt/miniconda3/envs/wsic/lib/python3.9/site-packages/wsic/readers.py", line 281, in __next__
print(f"Queue Size {self.queue.qsize()}")
File "/opt/miniconda3/envs/wsic/lib/python3.9/multiprocessing/queues.py", line 126, in qsize
return self._maxsize - self._sem._semlock._get_value()
NotImplementedError
The code keeps running after this error is thrown. When I stop it with ctrl + c, it throws this error
Traceback (most recent call last):
File "/opt/miniconda3/envs/wsic/lib/python3.9/multiprocessing/popen_fork.py", line 27, in poll
Process Process-1:
pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt
Traceback (most recent call last):
File "/opt/miniconda3/envs/wsic/lib/python3.9/multiprocessing/process.py", line 318, in _bootstrap
util._exit_function()
File "/opt/miniconda3/envs/wsic/lib/python3.9/multiprocessing/util.py", line 360, in _exit_function
_run_finalizers()
File "/opt/miniconda3/envs/wsic/lib/python3.9/multiprocessing/util.py", line 300, in _run_finalizers
finalizer()
File "/opt/miniconda3/envs/wsic/lib/python3.9/multiprocessing/util.py", line 224, in __call__
res = self._callback(*self._args, **self._kwargs)
File "/opt/miniconda3/envs/wsic/lib/python3.9/multiprocessing/queues.py", line 201, in _finalize_join
thread.join()
File "/opt/miniconda3/envs/wsic/lib/python3.9/threading.py", line 1053, in join
self._wait_for_tstate_lock()
File "/opt/miniconda3/envs/wsic/lib/python3.9/threading.py", line 1073, in _wait_for_tstate_lock
if lock.acquire(block, timeout):
KeyboardInterrupt
(wsic) ghadjigeorgiou@192 jp2tiff % /opt/miniconda3/envs/wsic/lib/python3.9/multiprocessing/resource_tracker.py:216: UserWarning: resource_tracker: There appear to be 1 leaked semaphore objects to clean up at shutdown
warnings.warn('resource_tracker: There appear to be %d '
When converting an image, the program created a crashdump and exited.
/usr/local/bin/python /usr/local/bin/wsic convert -i /xxx.jp2 -o /xxx.tiff -rt 4096 4096 -w 30 -mpp 0.5 0.5 --no-ome --overwrite -to 180 -c jpeg -cl 90 -d 2 -d 4 -d 8
Reading: 1%| | 10/828 [00:23<31:10, 2.29s/it]
Writing: 0%| | 106/204840 [00:23<12:07:59, 4.69it/s]
/usr/local/lib/python3.9/site-packages/wsic/readers.py:385: UserWarning: Failed to get next tile after 100 attempts. Dumping debug information.9, 4.69it/s]
warnings.warn(
Reading: 1%| | 10/828 [03:23<4:37:56, 20.39s/it]
Writing: 0%| | 160/204840 [03:23<72:26:45, 1.27s/it]
Traceback (most recent call last):
File "/usr/local/bin/wsic", line 8, in <module>
Reader Shape (145456, 92000, 3)
Read Tile Size (4096, 4096)
Yield Tile Size (256, 256)
sys.exit(main())
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
Read Mosaic Shape (36, 23)
Yield Mosaic Shape (569, 360)
Read Index (0, 10)
Yield Index (0, 160)
Remaining Reads (:10) [(1, 17), (1, 18), (1, 19), (1, 20), (1, 21), (1, 22), (2, 0), (2, 1), (2, 2), (2, 3)]
Enqueued {(1, 0), (1, 6), (0, 14), (1, 3), (1, 9), (0, 11), (0, 17), (1, 12), (0, 20), (1, 15), (1, 2), (0, 10), (0, 16), (1, 5), (1, 11), (0, 13), (0, 19), (1, 8), (1, 14), (0, 22), (1, 1), (1, 4), (0, 12), (1, 7), (1, 13), (0, 15), (0, 21), (1, 10), (1, 16), (0, 18)}
Reordering Dict (keys) dict_keys([(0, 22), (0, 21), (0, 18), (0, 15), (1, 1), (0, 14), (0, 11), (1, 4), (0, 13), (1, 5), (1, 3), (0, 12), (0, 17), (0, 19), (1, 0), (1, 6), (1, 8), (1, 10), (1, 7), (1, 13), (1, 14), (1, 15), (1, 12), (1, 16), (1, 9), (1, 11)])
Queue Size 0
Intermediate Read slices (slice(0, 256, None), slice(40960, 41216, None))
return self.main(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/wsic/cli.py", line 223, in convert
writer.copy_from_reader(
File "/usr/local/lib/python3.9/site-packages/wsic/writers.py", line 593, in copy_from_reader
tif.write(
File "/usr/local/lib/python3.9/site-packages/tifffile/tifffile.py", line 3116, in write
for tileindex, chunk in enumerate(
File "/usr/local/lib/python3.9/site-packages/tifffile/tifffile.py", line 20438, in encode_tiles
tile = next(tileiter)
File "/usr/local/lib/python3.9/site-packages/tifffile/tifffile.py", line 21536, in newiter
yield from iterator
File "/usr/local/lib/python3.9/site-packages/tqdm/std.py", line 1195, in __iter__
for obj in iterable:
File "/usr/local/lib/python3.9/site-packages/wsic/readers.py", line 406, in __next__
raise IOError(f"Tile read timed out at index {self.yield_index}")
OSError: Tile read timed out at index (0, 160)
Exception ignored in: <function MultiProcessTileIterator.__del__ at 0x7f3a53e61160>
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/wsic/readers.py", line 509, in __del__
self.close()
File "/usr/local/lib/python3.9/site-packages/wsic/readers.py", line 501, in close
executor.map(lambda p: p.join(1), self.processes)
File "/usr/local/lib/python3.9/concurrent/futures/_base.py", line 598, in map
fs = [self.submit(fn, *args) for args in zip(*iterables)]
File "/usr/local/lib/python3.9/concurrent/futures/_base.py", line 598, in <listcomp>
fs = [self.submit(fn, *args) for args in zip(*iterables)]
File "/usr/local/lib/python3.9/concurrent/futures/thread.py", line 169, in submit
raise RuntimeError('cannot schedule new futures after '
RuntimeError: cannot schedule new futures after interpreter shutdown
There is no other I/O intensive job while running the task.
Dear @John-P,
First of all, thank you very much for your excellent work.
I have tried to add a DICOM writer that supports multi-level whole slide images.
Example image: https://drive.google.com/file/d/1hd3EVYNL_1bpHJfMZu6JO3FqC3xNhCOS/view?usp=sharing
from codecs import Codec
from ctypes.wintypes import HCOLORSPACE
from functools import partial
from math import floor
import multiprocessing
from os import PathLike
from pathlib import Path
from typing import Generator, List, Optional, Tuple
import numpy as np
import wsic
from wsic.utils import warn_unused, mosaic_shape
from wsic.readers import Reader
from wsic.writers import ZarrIntermediate, ColorSpace, DICOMWSIWriter, get_level_tile
from wsic.enums import ColorSpace
class DICOMWSIWriterMultiLevel(DICOMWSIWriter):
"""Writer for DICOM WSI images using wsidicom and supports multiple levels.
Notes:
- Supports JPEG and JPEG2000 compression.
- DICOM Whole Slide Imaging: https://dicom.nema.org/Dicom/DICOMWSI/
"""
def __init__(
self,
path: PathLike,
shape: Tuple[int, int],
tile_size: Tuple[int, int] = (256, 256),
dtype: np.dtype = np.uint8,
color_space: Optional[HCOLORSPACE] = None,
codec: Optional[Codec] = None,
compression_level: int = 0,
microns_per_pixel: Tuple[float, float] = None,
pyramid_downsamples: Optional[List[int]] = None,
overwrite: bool = False,
verbose: bool = False,
**kwargs,
) -> None:
if color_space != "rgb":
warn_unused(color_space)
warn_unused(codec)
warn_unused(compression_level, ignore_falsey=True)
warn_unused(microns_per_pixel)
for key, value in kwargs.items():
warn_unused(key, value)
super().__init__(
path=path,
shape=shape,
tile_size=tile_size,
dtype=dtype,
color_space=color_space,
codec=codec,
compression_level=compression_level,
microns_per_pixel=microns_per_pixel,
pyramid_downsamples=pyramid_downsamples,
overwrite=overwrite,
verbose=verbose,
)
def copy_from_reader(
self,
reader: Reader,
num_workers: int = 2,
read_tile_size: Tuple[int, int] = None,
timeout: float = 10,
downsample_method: Optional[str] = None,
) -> None:
from pydicom import FileDataset, dcmwrite
from wsic.dicom import append_frames, create_vl_wsi_dataset
width = self.shape[1]
height = self.shape[0]
photometric_interpretation = (
ColorSpace.YCBCR.to_dicom_photometric_interpretation((4, 2, 2))
)
mpp: Tuple[float, float] = (
self.microns_per_pixel or reader.microns_per_pixel or (1.0, 1.0)
)
self.validate_write_args(microns_per_pixel=mpp)
meta, dataset = create_vl_wsi_dataset(
size=(width, height),
tile_size=self.tile_size,
microns_per_pixel=mpp,
photometric_interpretation=photometric_interpretation,
)
file_dataset = FileDataset(
str(self.path),
dataset=dataset,
preamble=b"\0" * 128,
file_meta=meta,
is_implicit_VR=False,
is_little_endian=True,
)
level_path = str(self.path.parent / f"{self.path.stem}_{0}.dcm")
dcmwrite(
dataset=file_dataset,
filename=level_path,
write_like_original=False,
)
with ZarrIntermediate(
None, reader.shape, zero_after_read=False
) as intermediate:
reader_tile_iterator = self.reader_tile_iterator(
reader=reader,
num_workers=num_workers,
intermediate=intermediate,
read_tile_size=read_tile_size or self.tile_size,
timeout=timeout,
)
def jpeg_generator(tile_iterator) -> Generator[bytes, None, None]:
"""Encodes arrays to JPEG bytes."""
import imagecodecs
for tile in tile_iterator:
yield imagecodecs.jpeg_encode(
tile,
level=self.compression_level,
colorspace=self.color_space,
outcolorspace=ColorSpace.YCBCR,
)
tile_iterator = iter(
self.level_progress(
jpeg_generator(reader_tile_iterator),
total=len(reader_tile_iterator),
)
)
append_frames(level_path, tile_iterator, len(reader_tile_iterator))
# Write pyramid resolutions
with multiprocessing.Pool(1) as pool: # num_workers
for level, downsample in self.pyramid_progress(
enumerate(self.pyramid_downsamples),
total=len(self.pyramid_downsamples),
):
level += 1
level_shape = tuple(
floor(s / downsample) for s in reader.shape[:2]
) + (reader.shape[-1],)
level_tiles_shape = mosaic_shape(
level_shape,
self.tile_size,
)
l_meta, l_dataset = create_vl_wsi_dataset(
size=level_shape[:2],
tile_size=self.tile_size,
microns_per_pixel=[mpp[0] * downsample, mpp[1] * downsample],
photometric_interpretation=photometric_interpretation,
)
l_dataset.InstanceNumber = f"{level + 1}"
l_dataset.StudyInstanceUID = dataset.StudyInstanceUID
l_dataset.SeriesInstanceUID = dataset.SeriesInstanceUID
l_dataset.FrameOfReferenceUID = dataset.FrameOfReferenceUID
level_path = str(self.path.parent / f"{self.path.stem}_{level}.dcm")
file_dataset = FileDataset(
level_path,
dataset=l_dataset,
preamble=b"\0" * 128,
file_meta=l_meta,
is_implicit_VR=False,
is_little_endian=True,
)
dcmwrite(
dataset=file_dataset,
filename=level_path,
write_like_original=False,
)
func = partial(
get_level_tile,
tile_size=self.tile_size,
downsample=downsample,
read_intermediate_path=intermediate.path,
downsample_method=downsample_method,
)
tile_generator = pool.imap(
func=func,
iterable=np.ndindex(level_tiles_shape),
)
tile_generator = self.level_progress(
tile_generator,
total=int(np.product(level_tiles_shape)),
desc=f"Level {level + 1}",
leave=False,
)
tile_iterator = iter(
self.level_progress(
jpeg_generator(tile_generator),
total=len(tile_generator),
)
)
append_frames(level_path, tile_iterator, len(tile_generator))
def main():
root_folder = Path("M01KBB05R-1315")
result_file = root_folder / "Test"
reader = wsic.readers.DICOMWSIReader(root_folder)
#writer = wsic.writers.DICOMWSIWriter(path=result_file,
writer = DICOMWSIWriterMultiLevel(path=result_file,
shape=reader.original_shape[:2],
pyramid_downsamples=[2,4,8,16,32], #[2,4,8,16,32]
verbose=True,
compression_level=80,
app_mag=40.) # , microns_per_pixel=0.241
writer.copy_from_reader(reader, num_workers=10, timeout=120) #num_workers=10,
if __name__ == "__main__":
main()
The dcm files are created and can be opened, but each row's last tile looks wrong.
This is how the last tile in a row looks in a DICOM viewer:
And this is in a whole slide image viewer.
It only affects my implementation. The implementation from level 0 is correct.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.