question

HoneyBee-2519 avatar image
0 Votes"
HoneyBee-2519 asked Castorix31 commented

Is there a way to set the Gamma value when changing the WriteableBitmap pixel color?

I'm developing a part that inverts the colors of an image.

I got a lot of advice from MSDN and I ended up with one way to do it very quickly.

It looks like the code below.

The speed is very fast.

However, when the color is converted, the sharpness decreases.
(The text in the image is not clearly visible.)

If you convert an image with black text, the text turns white. At this time, my goal is to make the letters that have changed to white thicker and darker.

Is there any other way other than setting the gamma value?

If the gamma value can be set, which part of my code should I supplement?

I also want to create an option that allows the user to adjust the gamma value.


 public static WriteableBitmap ConvertNagativePage(BitmapImage source)
         {
             FormatConvertedBitmap newFormatedBitmapSource = new FormatConvertedBitmap();
             newFormatedBitmapSource.BeginInit();
             newFormatedBitmapSource.Source = source;
             newFormatedBitmapSource.DestinationFormat = PixelFormats.Bgra32;
             newFormatedBitmapSource.EndInit();
             WriteableBitmap wb = new WriteableBitmap(newFormatedBitmapSource);
             int nWidth = wb.PixelWidth;
             int nHeight = wb.PixelHeight;
             int[] pixelData = new int[nWidth * nHeight];
             int nWidthBytes = nWidth * 4;
             wb.CopyPixels(pixelData, nWidthBytes, 0);
             for (int i = 0; i < pixelData.Length; ++i)
             {
                 pixelData[i] ^= 0x00FFFFFF;
                 pixelData[i] = MakePixelGray(pixelData[i]);
             }
             wb.WritePixels(new Int32Rect(0, 0, nWidth, nHeight), pixelData, nWidthBytes, 0);
    
             return wb;
         }
    
         private static int MakePixelGray(int pixel)
         {
             byte blue = (byte)pixel;
             byte green = (byte)(pixel >> 8);
             byte red = (byte)(pixel >> 16);
             byte alpha = (byte)(pixel >> 24);
             byte gray = (byte)(((red * 77) + (green * 150) + (blue * 29) + 128) / 256);
             return (int)(alpha << 24 | gray << 16 | gray << 8 | gray);
         }
windows-wpf
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi,@ HoneyBee-2519.
Long time no see.
Could you show me your minimal complete code and image resource that reproduce the problem to test?

0 Votes 0 ·

@HuiLiu-MSFT

It's a lot of content, so I've added code and images as an answer.

I'm looking for a way but it's really not easy

0 Votes 0 ·
Castorix31 avatar image
0 Votes"
Castorix31 answered Castorix31 commented

You can test with Gamma formulas found on Google

For example :

 {
     var imgSource = image1.Source;
     FormatConvertedBitmap newFormatedBitmapSource = new FormatConvertedBitmap();
     newFormatedBitmapSource.BeginInit();
     newFormatedBitmapSource.Source = (BitmapSource)imgSource;
     newFormatedBitmapSource.DestinationFormat = PixelFormats.Bgra32;
     newFormatedBitmapSource.EndInit();
    
     WriteableBitmap wb = new WriteableBitmap(newFormatedBitmapSource);
     int nWidth = wb.PixelWidth;
     int nHeight = wb.PixelHeight;
    
     int nPitch = (int)(nWidth << 2);
     int nSize = (int)(nPitch * nHeight);
     byte[] pixelData = new byte[nSize];
    
     int nWidthBytes = nWidth * 4;
     wb.CopyPixels(pixelData, nWidthBytes, 0);
    
     for (int nY = 0; nY < nHeight; nY++)
     {
         for (int nX = 0; nX < nWidth; nX++)
         {
             int nPos = (int)(nY * nPitch + nX * 4);
             byte b = (byte)pixelData[nPos], g = (byte)pixelData[nPos + 1], r = (byte)pixelData[nPos + 2], a = (byte)pixelData[nPos + 3];
    
             //byte nGray = Gray(r, g, b);
             //b = nGray;
             //g = nGray;
             //r = nGray;
    
             // > 1 : lighten, < 1 : darken
             double nGamma = 2;
             double nGammaCorrection = (1 / nGamma);
             b = (byte)(Math.Pow((double)(b / 255.0), nGammaCorrection) * 255.0f);
             g = (byte)(Math.Pow((double)(g / 255.0), nGammaCorrection) * 255.0f);
             r = (byte)(Math.Pow((double)(r / 255.0), nGammaCorrection) * 255.0f);
    
             pixelData[nPos] = b;
             pixelData[nPos + 1] = g;
             pixelData[nPos + 2] = r;
         }
     }
    
     wb.WritePixels(new Int32Rect(0, 0, nWidth, nHeight), pixelData, nWidthBytes, 0);
     image1.Source = wb;
 }
 // Test for gray     
 private byte Gray(byte nRed, byte nGreen, byte nBlue)
 {
     return (byte)((nRed * 299 + nGreen * 587 + nBlue * 114) / 1000);
 }

Test with 2 and 0.3 :

229114-gamma.jpg



gamma.jpg (108.3 KiB)
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

how-to-use-imageattributes-for-bitmapimage.html





Thank you for answer

First of all, the result I want is to adjust the gamma value in the black-and-white inverted result.

Remember the previous answer you gave me?

It worked very well and was very fast.

I'm still using that code.

I want to change only the gamma value of the converted result through that code.

Because it can be processed very quickly.

Testing the answer you provided this time made it very slow T_T
Is there any way to do it faster?

0 Votes 0 ·

Pow is slow
You can replace it with other formulas (pow 1072632447 on Google for example)

0 Votes 0 ·
HoneyBee-2519 avatar image
0 Votes"
HoneyBee-2519 answered HoneyBee-2519 edited

@HuiLiu-MSFT
Remember the advice you gave me at this link?

how-to-use-colormatrix-to-highlight-a-specific-col.html

It's the same project.
228465-000013.png
Your advice was great.

Obviously, the letters are much clearer.

But it was very slow...

So I replaced it with the code attached above and cut the speed in half.

I am attaching my sample image here.

This image is used as the Background ImageSource of the InkCanvas.

I used this image applied to the Background of InkCanvas
I load it as an ImageSource, invert the color, and apply it back to the InkCanvas' Background.





I want to apply the idea you gave me, how to use the SetGamma method, to the code below.

With the sample you helped, it takes about 0.35 seconds.

But the code below takes about 0.17 seconds. Very fast.

Here I want to adjust the Gamma value to improve speed and also increase sharpness.


 public static WriteableBitmap ConvertNagativePage(BitmapImage source)
         {
             System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
             sw.Start();
             FormatConvertedBitmap newFormatedBitmapSource = new FormatConvertedBitmap();
             newFormatedBitmapSource.BeginInit();
             newFormatedBitmapSource.Source = source;
             newFormatedBitmapSource.DestinationFormat = PixelFormats.Bgra32;
             newFormatedBitmapSource.EndInit();
             WriteableBitmap wb = new WriteableBitmap(newFormatedBitmapSource);
             int nWidth = wb.PixelWidth;
             int nHeight = wb.PixelHeight;
                
             int[] pixelData = new int[nWidth * nHeight];
             int nWidthBytes = nWidth * 4;
                
             int[] pixelData = new int[nHeight * nWidthBytes];
    
             wb.CopyPixels(pixelData, nWidthBytes, 0);
    
    
             for (int i = 0; i < pixelData.Length; ++i)
             {
                 pixelData[i] ^= 0x00FFFFFF;
                 pixelData[i] = MakePixelGray(pixelData[i]);
             }
             wb.WritePixels(new Int32Rect(0, 0, nWidth, nHeight), pixelData, nWidthBytes, 0);
             sw.Stop();
             System.Diagnostics.Debug.WriteLine(sw.Elapsed);
             return wb;
         }
    
         private static int MakePixelGray(int pixel)
         {
             byte blue = (byte)pixel;
             byte green = (byte)(pixel >> 8);
             byte red = (byte)(pixel >> 16);
             byte alpha = (byte)(pixel >> 24);
             byte gray = (byte)(((red * 77) + (green * 150) + (blue * 29) + 128) / 256);
             return (int)(alpha << 24 | gray << 16 | gray << 8 | gray);
         }

000013.png (2.9 MiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.