0

I write program in C# but hope that C++ and C# in background exactly same. What i want - take grayscaled image and separate colors over 127 and under 17 to separate images. If i simply get "white" colors and programmatically stretch them from range (127-255) to (0-255) like

// pseudocode
int min = 127, max = 255;
for(int x; x< width; x++)
    pixels[x] = pixels[x]/(max-min) * max;

Then here will be not smooth interval.. I mean, that 127 converts to 0 but 128 converts to 2 and colors 1,3,5,... are not exist.

That is original image with alpha:image original

That is image with "extracted white":image original

That is image with "extracted black": snorgg.ru/patchwork/tst_black.png.

I don't clearly understand how it can be realized so exampe code will like:

{
   im.MagickImage image = new im.MagickImage("c:/55/11.png");
   im.MagickImage imageWhite = ExtractWhite(image);
   im.MagickImage imageBlack = ExtractBlack(image);
}

....  

public static im.MagickImage ExtractWhite(im.MagickImage  img){

   im.MagickImage result = new im.MagickImage(img);

   ?????
   ?????

   return result;
}

thankы in advance ))

dlemstra
  • 7,813
  • 2
  • 27
  • 43
user3444737
  • 41
  • 1
  • 7
  • I find your question hard to understand. If you start with a single image that is 1 iuxel high and 256 pixels wide, the first pixel being 0 and then 1, 2, 3 ... 255, how many output images will there be and what will be in each? – Mark Setchell Dec 16 '14 at 19:57
  • @Mark Setchell , there will be two image. If "middle-gray" right in middle, then: First `0,0, 2, 2, 4, 4,... 255, 255`. And same next - `0,0, 2, 2, 4, 4,... 255, 255`. If "middle-gray" in 1/4 of 255 (between 1st quarter and other quarters), then: First `0,0, 0, 0, 4, 4, 4, 4, ... 255, 255`. And next - `0,1, 2, 2, 4, 5,6,6, 8,... 254, 255`. Because of trying stretch 0..63 range to 0..255. – user3444737 Dec 16 '14 at 20:29

2 Answers2

0

I think your calculation is wrong. You are confusing the input range with the output range. The input ranges from min to max and the output ranges from 0 to 255. It is a coincidence that your input max is equal to your output max (255).

If you want to stretch a value in the range of min ... max (= input range) to 0 ... 255 (= output range) then calculate this

int brightness = pixel[x];
if (brightness <= min) {
    pixel[x] = 0;
} else if (brightness >= max) {
    pixel[x] = 255;
} else {
    pixel[x] = 255 * (brightness - min) / (max - min);
}

Where min >= 0 and max <= 255 and min < max.

First you have to make sure the brightness is within the range min ... max, otherwise your result will exceed the range 0 ... 255. You could also limit the range of the output afterwards, but in any case you have to make a range check.

Then subtract min from the brightness. Now you have a value between 0 and (max - min). By dividing by (max - min) you get a value between 0 and 1. Multiply the result by 255 and you get a value in the desired range 0 ... 255.

Also you must be aware of the fact that you are performing integer arithmetic. Therefore multiply by 255 first and then divide. If you start by dividing you get either 0 or 1 as intermediate result (because integer arithmetic does not yield decimals and the final result will either be 0 or 255 and all the gray tones get lost.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • Yes, you right. Value must be moved to zero level: `class Program { static int Calc(int val, int min, int max) { return (int)(((float)val - min )/ (max - min) * max); } static void Main(string[] args) { int min = 127, max = 255; int bgn = 127, mdl = 200, end = 255; Debug.WriteLine("Expected 0, Got - " + Calc(bgn, min, max).ToString()); Debug.WriteLine("Expected ~150, Got - " + Calc(mdl, min, max).ToString()); Debug.WriteLine("Expected 255, Got - " + Calc(end, min, max).ToString()); } }` `Expected 0, Got - 0 Expected ~150, Got - 145 Expected 255, Got - 255` – user3444737 Dec 13 '14 at 16:10
  • But this is not where i'm stack (( – user3444737 Dec 13 '14 at 16:16
  • There seems to be a method doing it `void BrightnessContrast(Percentage brightness, Percentage contrast);`. And there is also a `WritablePixelCollection GetWritablePixels();` method returning pixels that you can change yourself. – Olivier Jacot-Descombes Dec 13 '14 at 18:46
0

The effect you are seeing is called banding or posterisation. It is caused by making contrast stretches to data that is not sampled with sufficient bit-depth. As you only have 8-bit data, you only have 255 grey levels. If you stretch the 50 levels between 100-150 over a range of 255 levels, there will be gaps in your histogram around 5 levels wide. The solution is either to obtain 16-bit data, or make less drastic changes in the contrast.

Alternatively, if like me, you are a photographer, and more interested in the aesthetics of the image than its scientific accuracy, you can add a small amount of random noise to disguise and "smear over" the banding...

There is a nice description here.

I can also show you an example with ImageMagick, first we create two greyscale ramps (gradients), one 8-bit and one 16-bit, both ranging from brightness level 100 to 150 like this:

convert -depth 8 -size 100x500 gradient:"rgb(100,100,100)-rgb(150,150,150)" -rotate 90 gradient8.png

convert -depth 16 -size 100x500 gradient:"rgb(100,100,100)-rgb(150,150,150)" -rotate 90 gradient16.png

They look like this:

enter image description here enter image description here

If I now stretch them both to the full range of 0-255 you will immediately see the banding effect in the 8-bit version, and the smoothness of the 16-bit version - which, incidentally, is the reason for using RAW format (12-14 bit) on your camera rather than shooting 8-bit JPEGs:

convert gradient8.png  -auto-level out8.png
convert gradient16.png -auto-level out16.png

enter image description here enter image description here

I alluded to using noise to redue the visibility of the banding effect, and you can do that using a technique like this:

convert out8.png -attenuate 0.3 +noise gaussian  out.png

which gives you a less marked effect, somewhat similar to film grain:

enter image description here

I am not certain exactly what you are trying to do, but if you just want to spread the brightness levels from 127-255 over the full range of 0-255, you can do that simply at the command-line like this:

convert orig.png -level 50%,100% whites.png

enter image description here

Likewise, if you want the brightness levels from 0-17 spread over the range 0-255, you can do

convert orig.png -level 0,6.66667% blacks.png

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432