Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fail to convert image if block size is smaller than image size on windows platform. #12

Open
Dr-QTDS opened this issue Sep 21, 2024 · 1 comment

Comments

@Dr-QTDS
Copy link

Dr-QTDS commented Sep 21, 2024

I am using Windows 10 to convert images. I have tested the testPy/PyImarisWriterExample.py code and it ran correctly. I edited the code as below:

import PyImarisWriter as PW
import numpy as np

from datetime import datetime

class TestConfiguration:

    def __init__(self, id, title, np_type, imaris_type, color_table):
        self.mId = id
        self.mTitle = title
        self.mNp_type = np_type
        self.mImaris_type = imaris_type
        self.mColor_table = color_table

def get_test_configurations():
    configurations = []

    configurations.append(TestConfiguration(len(configurations), 'uint8 image from uint8 numpy array', np.uint8, 'uint8',
                                            [PW.Color(0.086, 0.608, 0.384, 1), PW.Color(1, 1, 1, 1), PW.Color(1, 0.533, 0.243, 1)]))

    return configurations



class MyCallbackClass(PW.CallbackClass):
    def __init__(self):
        self.mUserDataProgress = 0

    def RecordProgress(self, progress, total_bytes_written):
        progress100 = int(progress * 100)
        if progress100 - self.mUserDataProgress >= 5:
            self.mUserDataProgress = progress100
            print('User Progress {}, Bytes written: {}'.format(self.mUserDataProgress, total_bytes_written))


def run(configuration):
    image_size = PW.ImageSize(x=600, y=400, z=1, c=1, t=1)
    dimension_sequence = PW.DimensionSequence('x', 'y', 'z', 'c', 't')
    block_size = PW.ImageSize(x=300, y=400, z=1, c=1, t=1)
    sample_size = PW.ImageSize(x=1, y=1, z=1, c=1, t=1)
    output_filename = f'PyImarisWriterNumpyExample{configuration.mId}.ims'
    
    options = PW.Options()
    options.mNumberOfThreads = 12
    options.mCompressionAlgorithmType = PW.eCompressionAlgorithmGzipLevel2
    options.mEnableLogProgress = True

    np_data = np.zeros((image_size.y, image_size.x), dtype=configuration.mNp_type)
    np_data[:, 0:100] = 50
    np_data[:, 200:300] = 150
    np_data[:, 400:500] = 250

    application_name = 'PyImarisWriter'
    application_version = '1.0.0'

    callback_class = MyCallbackClass()
    converter = PW.ImageConverter(configuration.mImaris_type, image_size, sample_size, dimension_sequence, block_size,
                                  output_filename, options, application_name, application_version, callback_class)
    
    num_blocks = image_size / block_size

    block_index = PW.ImageSize()
    for c in range(num_blocks.c):
        block_index.c = c
        for t in range(num_blocks.t):
            block_index.t = t
            for z in range(num_blocks.z):
                block_index.z = z
                for y in range(num_blocks.y):
                    block_index.y = y
                    for x in range(num_blocks.x):
                        block_index.x = x
                        if converter.NeedCopyBlock(block_index):
                            if x == 0:
                                converter.CopyBlock(np_data[:, 0:300], block_index)
                            else:
                                converter.CopyBlock(np_data[:, 300:], block_index)

    adjust_color_range = False
    image_extents = PW.ImageExtents(0, 0, 0, image_size.x, image_size.y, image_size.z)
    parameters = PW.Parameters()
    # parameters.set_value('Image', 'ImageSizeInMB', 2400)
    parameters.set_value('Image', 'Info', configuration.mTitle)
    parameters.set_channel_name(0, 'My Channel 1')
    time_infos = [datetime.today()]
    color_infos = [PW.ColorInfo() for _ in range(image_size.c)]
    # color_infos[0].set_color_table(configuration.mColor_table)

    converter.Finish(image_extents, parameters, time_infos, color_infos, adjust_color_range)
    
    converter.Destroy()
    print('Wrote {} to {}'.format(configuration.mTitle, output_filename))


def main():
    configurations = get_test_configurations()
    for test_config in configurations:
            run(test_config)

if __name__ == "__main__":


    main() 

I edited the image_size in line 57, block_size in line 59, changed values between lines 69 and 71, and edited lines between 94 and 97 to test how to paste a small image to a big canvas. Though the code can run successfully, the output image is incorrect. The output image looks like the below picture.

微信截图_20240921150755

But if I edit the configurations.append(TestConfiguration(len(configurations), 'uint8 image from uint8 numpy array', np.uint8, 'uint8', [PW.Color(0.086, 0.608, 0.384, 1), PW.Color(1, 1, 1, 1), PW.Color(1, 0.533, 0.243, 1)])) to configurations.append(TestConfiguration(len(configurations), 'uint8 image from uint8 numpy array', np.uint8, 'uint16', [PW.Color(0.086, 0.608, 0.384, 1), PW.Color(1, 1, 1, 1), PW.Color(1, 0.533, 0.243, 1)])), I just changed 'uint8' to 'uint16', the output image shows correctly as below.

1726902711731

But as in the above code, the dtype of the NumPy array is uint8. So is this a bug or is something wrong in my code. Please help me, thanks!

@imaris
Copy link
Owner

imaris commented Sep 27, 2024

Hello, the problem seems to be happening because of the slicing (np_data[:, 0:300]), which apparently gets lost when passed through the library. In fact, calling converter.CopyBlock(np_data, block_index) for both blocks produces the same result. While this looks like a bug to me as well, allocating arrays larger than a block sort of defeats its purpose. The following produces the expected result (note the block_size instead of image_size):

    np_data = np.zeros((block_size.y, block_size.x), dtype=configuration.mNp_type)
    [...]
                            if x == 0:
                                np_data[:, 0:100] = 50
                                np_data[:, 100:200] = 0
                                np_data[:, 200:300] = 150
                            else:
                                np_data[:, 0:100] = 0
                                np_data[:, 100:200] = 250
                                np_data[:, 200:300] = 0
                            converter.CopyBlock(np_data, block_index)

Mismatching the declared types produces the expected pattern because a non-sliced numpy array of the file type is created by copying the given numpy sliced array data (the library makes sure that an array of the right type is passed to the library, even if creating a temporary copy might be expensive in terms of time and memory).

Hope this helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants