Writing/Reading Rendered Image to and from Text



  • Hi,

    @blastframe said in Writing/Reading Rendered Image to and from Text:

    ... because, as I mentioned in the original post, I don't know how to write to text or read it back without saving it as an image (in this case) with base64.

    You can not, because BaseBitmap is only being serialiseable via the offered common image formats, and this serialisation is, depending on the chosen format, also lossy (a JPEG cannot have layers for example). BaseBitmap is a complex data type, think of it as a diagram, that has no inherent information about how to be expressed as an ordered tuple of values, i.e. how to be serialised. base64 is also just a number system, so you are just expressing the existing data in a slightly more convenient way (you could also save binary, i.e. base 2, as text, it just would be very long).

    Could I get the compressed .jpg data without saving the image first, perhaps, or is there really no way to get this data from BaseBitmap? Thanks!

    The lossy image formats are quite efficient at what they do, so no, you won't get things as small without using something specialised like a wavelet compression for example. Things you can do:

    1. Move away from string serialisation, as it is quite inefficient when it comes to most performance metrics (space, time, etc.). The purpose of string serialisation is to be human readable and to be as barrier-free as possible for data exchange. There is nothing wrong with string serialisations, I like them too, you just have to pay the costs.
    2. If you do not want to implement a more complex image compression algorithm or use one of the existing 3rd party Python modules, you could employ one of the all purpose compression algorithms, like for example LZW. It won't be as effective a wavelet compression, but in some cases, it will cut down the file size significantly. Python has the zlib module, which implements an algorithm that is very similar to LZW. The file formats GIF and TIF use LZW-compression internally.

    edit: Yes, SetPixel is probably quite slow (never tested the performance myself, but array insertion operations are expensive, so this method is most likely quite expensive too. I mentioned in my first post the method SetPixelCnt to write consecutive blocks of pixels, there is even an example for it in the docs).

    Cheers,
    zipit



  • Hi sorry to come a bit late to the party, but you are not forced to rely on 3rd part solution you can export your BaseBitmap to a bitsequence then it's up to you to convert this bitsequence in a way that can be represented in a file.

    Find an example in read_write_memory_file_bitmap_r13.

    Cheers,
    Maxime.



  • @zipit Thank you again for the thorough explanation.

    The existing 3rd party Python modules to which you are referring are pickle and marshal?



  • Hi again ;)

    no, pickle and marshal are 'bultin' modules of Python and both general purpose serialisation modules (pickling and marshling are both just odd synonyms for serialisation). A common candidate for such 3rd party image module would be pil or its successor pillow. Both libraries accept byte objects when invoking their save functionality, which would allow you to serialise something into the image format of your choice without having to go the route of saving it to disk. It would go something like that:

    import io
    from PIL import Image
    
    # The source of the bitmap would obviously different, I am just not
    # in the mood to install pillow in Cinema. You can create Image objects
    # from a byte buffer, which would allow you to instiate an Image object
    # from a raw pixel array fed by Cinemas BaseBitmap.
    image = Image.open("test.png")
    # We need to get rid of the alpha channel first, or pillow will complain when
    # we try to save the image as a JPEG.
    image = image.convert("RGB")
    # The buffer object.
    buffer = io.BytesIO()
    # We just save the image with the buffer object in place of the file path.
    image.save(buffer, format="JPEG")
    # The first 10 bytes of the image in JPEG format.
    print (buffer.getvalue()[:9])
    

    Cheers,
    zipit



  • Hi @zipit ! Thank you again.

    Yes, I saw the pil module, but didn't pursue it because I don't know the recommended way for including a 3rd party module with my plugins. How would you include the module? Using Niklas Rosenstein's localimport or is there another way to do it?



  • Hi,

    AFAIK there is no recommended way. Due to the fact the MAXON decided to rip out pip and not deliver a package manager with Cinema's Python, it would be quite some work to install pillow in the first place, as it has a long list of dependencies. With that I mean in Cinema's fake site-packages folder that is meant for third party modules. You could also try to include pillow locally with your plugin, but that would probably require a decent amount of monkey patching to get things to work. If you only import the module locally in the interpreter and then clean up after yourself (i.e. use Nikklas importer thingy), is probably only a detail in these considerations.

    If you want to do this in a plugin intended for distribution, I would look for another solution.

    PS: And just to be clear. Although it says PIL in my example code, this is actually pillow code that ran on Python 3.9. The naming history of the package is not the smartest, to put it mildly ;)

    Cheers,
    zipit



  • Hi @zipit ,
    Thank you for the clarification and sorry for all the questions.

    What do you mean, clean up after myself? Would I need to remove something after importing it?

    Yes, the plugin is meant for distribution...so you suggest I look for a solution other than pillow & Niklas' localimport?

    Thank you.

    P.S. It seems like C4D is recognizing modules in this folder for me...
    C:\Program Files\Maxon Cinema 4D R22\resource\modules\python\libs\win64\python27.vs2008.framework\lib\site-packages
    I'm now trying to create Image objects from a byte buffer 😱



  • Hoi again,

    1. With cleaning up I was just referring to what these local import modules / hacks usually do. I haven't looked at Nikklas' code, but I was implying that it was doing it for you.
    2. Yes, that is the path I meant. site-packages is normally the directory where you place 3rd party packages in the library of a Python installation. I called it fake, because Maxon moved the path out of the Python installation that comes with Cinema. Probably not the best choice of words on my end.
    3. Yes, I would not go that route due to the fact that I seems like a lot of work. But that does not mean that it is impossible. I would be confident to find another solution, but if you are not, there is nothing inherently wrong with installing pillow in Cinema's Python. It will probably be just a lot of tinkering until you have come up with a custom installer, since there is no pip in Cinema's Python. The first thing to check would be if you can drag and drop install pillow and its dependencies you need. Python packages come sometimes with an installer that is run by pip when you install the package via the command line and sometimes the execution of that installer is actually required for the package to work properly.

    PS: Also, when you install pillow globally in site-packages, you won't need Nikklas' package, since it is already sitting in the global module directory then, so it does not make sense to import it locally then.

    Cheers,
    zipit



  • @zipit Thank you for all of your guidance. You've been very helpful.



  • Hi sorry to come a bit late to the party, but you are not forced to rely on 3rd part solution you can export your BaseBitmap to a bitsequence then it's up to you to convert this bitsequence in a way that can be represented in a file.

    Find an example in read_write_memory_file_bitmap_r13.

    Cheers,
    Maxime.



  • @m_adam Thank you, Maxime.

    I still learned a lot from your help, @zipit , thank you too.


Log in to reply