adjusting brightness and contrast

Feb 19, 2015 at 3:32 PM
Looking through the source codes, I added two extension methods to enable brightness and contrast adjustments. I've also used Parallel.For from the task parallel library to speed up the process. Here's the codes. Would the team consider adding this into the official codes? Also, I noticed that the Rotate and RotateFree methods could also benefit from the use of Parallel.For, instead of the sequential for loop now but I am not good with algorithms thus can't get the destination index right. But I believe if it can be expressed as an equation based on the x, y, h and w of the source, it will benefit greatly from the parallel operations.

here's the codes for brightness:
   public static WriteableBitmap adjustBrightness(this WriteableBitmap src, int nLevel)
   {
       WriteableBitmap result = null;          
       using (var context = src.GetBitmapContext())
       {
           int nWidth = context.Width;
           int nHeight = context.Height;
           var px = context.Pixels;
           result = BitmapFactory.New(nWidth, nHeight);

            using (var dest = result.GetBitmapContext())
            {
                var rp = dest.Pixels;
                Parallel.For(0, context.Length, i =>
                {
                    // Extract
                    var c = px[i];  
                    var a = (c >> 24) & 0x000000FF;
                    var r = (c >> 16) & 0x000000FF;
                    r += nLevel;
                    r = TruncateColour(r);
                    var g = (c >> 8) & 0x000000FF;
                    g += nLevel;
                    g = TruncateColour(g);
                    var b = (c) & 0x000000FF;
                    b += nLevel;
                    b = TruncateColour(b);
                    rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
                });
            }
        }


    return result;
  }
and the codes for adjusting contrast:
   public static WriteableBitmap adjustContrast(this WriteableBitmap src, double level)
   {
      WriteableBitmap result = null;
      double Factor = (259.0 * (level + 255.0)) / (255.0 * (259.0 - level));

      using (var context = src.GetBitmapContext())
      {
          int nWidth = context.Width;
          int nHeight = context.Height;
          var px = context.Pixels;

          result = BitmapFactory.New(nWidth, nHeight);

          using (var dest = result.GetBitmapContext())
          {
              var rp = dest.Pixels;
              Parallel.For(0, context.Length, i =>
              {
                  // Extract
                  var c = px[i];  //individual pixel of src
                  var a = (c >> 24) & 0x000000FF;
                  var r = (c >> 16) & 0x000000FF;
                  r = TruncateColour((int)(Factor * (r - 128)) + 128);
                  var g = (c >> 8) & 0x000000FF;
                  g = TruncateColour((int)(Factor * (g - 128)) + 128);
                  var b = (c) & 0x000000FF;
                  b = TruncateColour((int)(Factor * (b - 128)) + 128);
                  rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
              });
          }
      }
      return result;
  }
this is the TruncateColour function:
   static int TruncateColour(int c)
   {
       if (c < 0)
           c = 0;
       else if (c > 255)
           c = 255;
       return c;
   }
Do you guys think it's good enough to be considered as part of the source? :)
Coordinator
Feb 19, 2015 at 4:16 PM
Thanks, this looks good and we can add it for sure. :)
Keep in mind that the lib works on a bunch of platforms so Parallel.For is an option only for those where it's supported but good point!

You should inline the TruncateColour check to avoid 3 method calls in your loop.
Feb 22, 2015 at 6:21 PM
do you mean like this?
r=r<0?0:r>255?255:r;
g=g<0?0:g>255?255:g;
b=b<0?0:b>255?255:b;
Coordinator
Feb 22, 2015 at 6:27 PM
Yep, looks good. :)

Would you mind submitting a Subversion Patch for all the additions you have made. Would make things easier to integrate. :)
  • Rene
Feb 22, 2015 at 6:40 PM
I would need to look at how to create a SVN patch.. do you have any quick primer on that? :P
Also, would submitting the patch go through the code review process by you guys? I won't want to mess up the trunk without you guys giving your expert opinions. :)


On 23/2/2015 3:27 AM, teichgraf wrote:

From: teichgraf

Yep, looks good. :)

Would you mind submitting a Subversion Patch for all the additions you have made. Would make things easier to integrate. :)
  • Rene

Coordinator
Feb 22, 2015 at 6:52 PM
Creating a SVN patch is really easy. Maybe check some online resources as the details depend on the SVN client you're using.
And yes, I'd review the code.
Feb 23, 2015 at 7:49 AM
Hi,
I've generated the patch. Attached here.

Added functionalities:
1) brightness
2) contrast
3) gamma

I could not get the Parallel.For to compile though I've used the System.Threading.Tasks.Parallel directly, thus I did not use the parallel codes. Could you please advice me on how to incorporate the parallel codes in without affecting other platforms that don't support it? It would be nice to parallelize these three functions, as well as the Rotate, flip, Resize, Crop, Invert and rotatefree functions as they are high parallelizable without any dependencies between pixels. I've posted about the parallelized rotate adn flip. With your permission and guidance, I can incorporate it in, at least for WPF. :)

please review my cpdes and let me know of ways to improve them. thank you. :)


On 23/2/2015 3:53 AM, teichgraf wrote:

From: teichgraf

Creating a SVN patch is really easy. Maybe check some online resources as the details depend on the SVN client you're using.
And yes, I'd review the code.

Coordinator
Mar 4, 2015 at 11:15 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Coordinator
Mar 4, 2015 at 11:16 AM
I don't see the attachment here.
I've created a work item from this where you can add an attachment: https://writeablebitmapex.codeplex.com/workitem/21748
Coordinator
Mar 4, 2015 at 11:43 AM
Never mind, found the Patch. You have used the official way. :)
Coordinator
Mar 4, 2015 at 11:56 AM
Added the Gray() method with an optimization where it only uses integer with bit shift and no expensive double floating point. :)

Thanks for your help!
  • Rene
Coordinator
Mar 4, 2015 at 2:48 PM
Also added your other 3 methods, but changed the contrast code to only use integer arithmetic instead of double inside the loop for better performance.
Mar 5, 2015 at 5:33 AM
hmmm.. maybe I'm not doing it right when I was using integers to do it, I noticed that when contrast was set -ve, it went all the way white, thus resorted to use double. Actually I felt that double may incur more performance penalty, but parallelizing the codes could yield better performance, thus mitigating the cost of using FPU.. but I may be wrong. Could you enlighten me on this? :)
Mar 5, 2015 at 5:41 AM
hmmm.. in the history log, didn't see the inclusion of Sepia function.. is it not good enough? how can it be improved? I know there are several algorithms for Sepia adjustments.. would you prefer them? :)
Coordinator
Mar 5, 2015 at 6:15 AM
Integer arithmetics is still the way to go for perf, especially with simple bit shifting and in loops. Please note that WBX supports a wide range of platforms like phone and their ARM chips.

The Sepia is just a color mod. I'd rather prefer a generic approach where one could pass the factors to be used for multiplying the brightness/gray. Anyway, we need to be careful not bloating the API as well. :)

Anyway, thanks for your help.
Mar 5, 2015 at 6:27 AM
roger that.. thanks for enlightenment. :)
Mar 5, 2015 at 6:37 AM
glad to be able to contribute just a tiny little bit to this project. :)
Coordinator
Mar 5, 2015 at 7:22 AM
BTW, do you have a real name and a homepage I can use to add your name to the Credits on the project's homepage? If not, I will just use your name here and link to the profile.
Mar 5, 2015 at 4:28 PM
My real name is John Ng San Ping. I'm from Singapore. Been a Windows C++ developer since 1999, thus you may notice my naming convention is pretty Hungarian (nHeight, nWeight). And recently influenced by Android/Java thus original function name is verb small case, noun camel, like Java. :P

As for putting my name in credits, thanks so much.. I'm very honoured.. but I frankly don't deserve any credits for it. I merely ripped off some image processing algorithms online. Impetus for doing it is mainly because I needed it in my work which involved some simple image processing on raw images which I used WPF's WIC to decode with the Microsoft camera codec. Was looking around for a way to do it without resorting to GDI+ like numerous articles did.. so your project helped to solve this big issue as GDI+ can't decode raw images (Canon's CR2 format) and WPF's BitmapImage can't do image processing like brightness, contrast and gamma. I did use the WPF's transform to do rotation before I came across your project. Just found that standardizing the whole image processing on WBX is neater, though I did recompile it for myself to add parallel for to speed up processing. Hope you don't mind. :)

As said, it's a tiny insignificant contribution. your changes to use integral values instead of floating point is impressive. have much to learn from you guys. :)