Dotnet 16 bits images


#1

Hey there,

I’m trying some stuff with maxScript and Dotnet GDI+.

Basically, I’m processing png images.
These images are 16 bits images.

Whatever I try, and I tried a lot of different stuff, read the articles here as well.
So if I simply get the image and save it as png again, it’s saved in 8bits and loses a lot of information.

– here is how I get the image :
myImage = dotNetObject “System.Drawing.Bitmap” Imagepath
– or
myImage = (dotNetClass “System.Drawing.Image”).fromFile Imagepath

– and how I save it :
myImage.save MySavePath.png

I tried to create a new DrawingBitmap with a different pixelFormat,
Draw my Source image in it, and save…same problem.

I start to think there is no way to do that correctly in Dotnet, but it seems so simple that I think I miss something.

Any Idea ?


#2

It seems that Dotnet GDI+ can only handle 8bit images.
So that might be a dead end unless someone says differently.

I saw a few external libraries to do that in dotnet, but I have no idea how to install/call them from maxscript.


#3

Hi @Clovis,
perhaps you’ve already solved this but here’s something which works for me

--make a new bitmap
thePixelformat = (dotnetclass "system.drawing.imaging.pixelformat").Format48bppRgb
theBitmap = dotNetObject "System.Drawing.Bitmap" 256 256 thePixelformat

--get an image from disk
inputImage = (dotnetclass "system.drawing.image").FromFile @"input.png"

--paste that image into our new bitmap
theGraphics = (dotnetclass "system.drawing.graphics").fromImage theBitmap
theRect = dotnetobject "system.drawing.rectangle" 0 0 255 255
theGraphics.drawimage inputImage theRect

--save the bitmap
theBitmap.save @"output.png"

#4

hey, thanks a lot for your answer.
I just tried, and it doesn’t really solves it,
at the end it saves a 16bits png, but the image is still modified, way darker, and the gradient altered in 8 bits even if the image is saved in 16bits.

if you have a minute, please try with this image as source image :

then apply the saved image in a displace modifier on a plane to see what it does, you will see what I mean.


#5

Hi @Clovis,
I’m not sure I understand the problem. The image you post here is 8 bits, if I convert it to 16 bits with the methods above I get a darker image. That’s something I’d expect. The brightest value in the 8 bit image is not so bright in a 16 bit one. If I increase the displacement value on the 16 bit image I get almost the same displacement. The weird thing is, it’s not exactly the same.


the image on the left has 8 bit displacement set at 256 strength (2^8), the image on the right has a 16 bit displacement set at 16384 strength (2^14). They look very similar. Though I’d expected I needed to set the strength to 2^16. Somewhere 2 bits are missing…
What’s clearly visible is the stepped nature of the displacement. The 8 bit image has it. Conversion to the 16 bit image doesn’t increase the fidelity of the displacement automatically. (all displacement maps are set at gamma=1.0)


#6

Ah, damn, the forum automatically converted my png to jpeg with a compression.

here is a wetransfer link to the png file : https://we.tl/t-LuItaUxqZ4

and this is the result in a displace modifier, you see there is no stepped displacement.


#7

I see what you mean. I think the issue is with the source image format. It’s a 16 bit greyscale image. I’ve never been able to work with that format in .net. In my mapping script I need to create 16 bit displacement maps for terrains and I could do with greyscale. But it seems that format isn’t supported, so I create 16 bit rgb images.
Perhaps the same issue happens here: .net can’t convert the 16 bit greyscale data to 16 bit rgb. I’ve tried to convert your image in affinity photo to 16 bit rgb, but failed. Perhaps you can convert the source image to rgb yourself?


#8

try replicating something like this perhaps…

private void BitmapConstructorEx(PaintEventArgs e)
{

    // Create a bitmap.
    Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
    
   // Retrieve the bitmap data from the bitmap.
    System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), 
        ImageLockMode.ReadOnly, bmp.PixelFormat);

    //Create a new bitmap.
    Bitmap newBitmap = new Bitmap(200, 200, bmpData.Stride, bmp.PixelFormat, bmpData.Scan0);

    bmp.UnlockBits(bmpData);

    // Draw the new bitmap.
    e.Graphics.DrawImage(newBitmap, 10, 10);

}

with pixelformat set to Format16bppGrayScale

after playing around it’s not that the format is not supported per se it’s the drawing/handling methods don’t support it, so cloning will throw an out of memory exception for example as will trying to create a drawing context from a 16bpp grayscale image/bitmap.


#9
(
	pixelformat = (dotNetclass "System.Drawing.Imaging.PixelFormat").Format16bppGrayScale;
	lockmode = (dotNetclass "System.Drawing.Imaging.ImageLockMode").ReadOnly;
	bmap = dotnetobject "System.Drawing.Bitmap"	"../Desktop/Terrain_03.png";
	rect = dotnetobject "System.Drawing.Rectangle" 0 0 bmap.width bmap.height

	bmpData = bmap.LockBits rect lockmode bmap.pixelformat;
	newBitmap = dotnetobject "System.Drawing.Bitmap" bmap.width bmap.height bmpData.Stride pixelformat (dotNetObject "System.IntPtr" bmpData.Scan0)

	bmap.UnlockBits bmpData;
	newBitmap.pixelformat.value__;
)

returns 1052676 (Format16bppGrayScale) and not 2498570 (Format32bppArgb)

though of course it doesn’t let you save the new bitmap as png though it will let you as tiff or bmp (not openable in my version of photoshop and considerably smaller than the original so I don’t think it’s working)

you could try writing the raw bmpData to a file and open in photoshop as raw to see what you get, though the loader is the cuiprit it seems