Graphics.DrawImage creates various image data on x86 and x64

Hey there!

Here is my setup:
I have a C # application that extracts functions from a series of images. Due to the size of the data set (several thousand images), it is highly parallelized, so we have a high-performance machine with ssd that runs on Windows7 x64 (.NET4 runtime) to take off the hard work. I am developing it on a computer running Windows XP SP3 x86 in Visual Studio 2008 (.NET3.5) using Windows Forms - there is no way to move to WPF, by the way.

Edit3: It's weird, but I think I finally found out what was going on. It seems to be a codec for an image format that gives different results on two machines! I do not know exactly what is happening there, but the decoder on the xp machine gives more efficient results than win7. Unfortunately, the best version is still in the x86 XP system :( I think the only solution for this is to change the input image format to something without any loss, for example png or bmp (it's silly for me not to think about the file format in the first place :) )

Edit2: Thanks for your efforts. I think that I will stick to my own converter, this is not quite what I wanted, but I have to somehow solve it :). If someone is reading this, who has ideas for me, please let me know.

Edit: In the comments, I was recommended to use a third-party lib for this. I think I haven’t made myself clear enough, because I don’t want to use the DrawImage approach at all - it’s just a drawback of quickhack to get the actually working new Bitmap(tmp, ... myPixelFormat) , which we hope will use some interpolation . What I want to achieve is solely to convert the incoming image to a common PixelFormat with some standard interpolation.

My problem is as follows. Some source images are in JPG format in Indexed8bpp format, which do not mix well with WinForms visualization materials. Therefore, in my image loading logic, there is a check for indexed images that convert the image to the default format for my applications (e.g. Format16bpp):

 Image GetImageByPath(string path) { Image result = null; using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { Image tmp = Image.FromStream(fs); // Here goes the same image ... if (tmp.PixelFormat == PixelFormat.Format1bppIndexed || tmp.PixelFormat == PixelFormat.Format4bppIndexed || tmp.PixelFormat == PixelFormat.Format8bppIndexed || tmp.PixelFormat == PixelFormat.Indexed) { // Creating a Bitmap container in the application default format result = new Bitmap(tmp.Width, tmp.Height, DefConf.DefaultPixelFormat); Graphics g = Graphics.FromImage(result); g.InterpolationMode = InterpolationMode.HighQualityBicubic; // We need not to scale anything in here Rectangle drawRect = new Rectangle(0, 0, tmp.Width, tmp.Height); // (*) Here is where the strange thing happens - I know I could use // DrawImageUnscaled - that isn't working either g.DrawImage(tmp, drawRect, drawRect, GraphicsUnit.Pixel); g.Dispose(); } else { result = new Bitmap(tmp); // Just copying the input stream } tmp.Dispose(); } // (**) At this stage the x86 XP memory image differs from the // the x64 Win7 image despite having the same settings // on the very same image oO result.GetPixel(0, 0).B; // x86: 102, x64: 102 result.GetPixel(1, 0).B; // x86: 104, x64: 102 result.GetPixel(2, 0).B; // x86: 83, x64: 85 result.GetPixel(3, 0).B; // x86: 117, x64: 121 ... return result; } 

I traced the problem to (*) . I think InterpolationMode has something to do with it, but there is no difference which one I choose, different results in (**) on both systems. I studied the test image data with some dumb lines to copy and paste to make sure that this is not a problem with accessing the data in the wrong way.

The images collectively look as follows Electron scattering diffraction pattern . Actual color values ​​vary subtly, but they contain more information - interpolation even enhances it. It seems that the layout algorithm on the x86 machine uses the InterpolationMode property, while x64 just changes the palette values ​​without taking into account any interpolation.

I never noticed any difference between the output of the two machines until the day when I applied the histogram view function to the data in my application. On the x86 machine, it is balanced, as you would expect from viewing images. On the other hand, an x64 machine would rather suggest some kind of sparse line chart, indicating indexed image data. It even affects the overall output of the entire application - the output differs on both machines with the same data, which is not very good.

For me, this seems like an error in the implementation of x64, but it's just me :-). I just want the images on the x64 machine to have the same values ​​as x86.

If anyone has an idea, I will be very pleased. I've been looking for this kind of behavior on the net for ages, but resistance seems futile :)

Oh look ... a whale!

+4
source share
3 answers

If you want to make sure that this is always done the same way, you will have to write your own code to process it. Fortunately, this is not too complicated.

Your 8bpp image has a palette containing the actual color values. You need to read this palette and convert the color values ​​(which, if I remember correctly, are 24 bits) to 16-bit color values. You’ll lose information in a conversion, but you’re already losing information in your conversion. At least in this way you lose information in a predictable way.

Put the converted color values ​​(there will be no more than 256) in an array that you can use to search. Then...

Create a destination bitmap and call LockBits to get a pointer to the actual bitmap data. Call LockBits to get a pointer to the raster data of the original raster image. Then for each pixel:

 read the source bitmap pixel (8 bytes) get the color value (16 bits) from your converted color array store the color value in the destination bitmap 

You can do this with GetPixel and SetPixel , but it will be very slow.

+1
source

I vaguely remind you that .NET graphics classes rely on GDI +. If this is the case today, then it makes no sense to try to use the application on different 64-bit systems with different video drivers. It is best to either interpolate using the raw GDI operations (P / Invoke), or write your own pixel interpolation procedure in the software. None of the options are particularly attractive.

+1
source

You really should use OpenCV for image processing like this in C # here: OpenCVSharp .

0
source

Source: https://habr.com/ru/post/1383763/


All Articles