ImageMagick - Set levels for grayscaled image - c#

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 ))

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.

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:
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
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:
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
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

Related

How to generate random positive floating point numbers with normal distribution from 0 up to a given ceiling?

I need to generate a number of float numbers with approximately normal distribution over a range from 0 to a specific ceiling.
I've searched on stack overflow and found similar questions for other languages, but none for .net core.
internal List<float> function(int ceiling, int repetitions)
{
List<float> list = new List<float>();
for (int i = 0; i<= repetitions;i++)
{
list.Add(Random.nextFloat() * ceiling);
}
return list;
}
I expect the function to return a list of random positive floatnumbers, in range from 0 to a given ceiling with at least approximately normal distribution.
If you're seeking something "at least approximately normal" with bounds at 0 and ceiling, summing three uniforms will yield a result which is symmetric, bell-shaped, and bounded, and can subsequently be rescaled to any range you wish. I'm not a C# programmer, but if you have a PRNG named prng:
(prng.NextDouble() + prng.NextDouble() + prng.NextDouble()) * ceiling / 3.0
will yield a result in the range [0, ceiling]. Here's what 100,000 observations look like with ceiling set to 3:
You can generalize this to sum k uniforms and replace the 3 by k in the divisor for the rescaling. The larger k is, the closer this will get to normality by the central limit theorem, but since you don't seem to be asking for actual normals (which don't have a bounded range anyway) that quickly gets into diminishing returns.
Note that while this approach uses multiple uniforms, it is computationally relatively efficient because it avoids transcendental functions.
Well, you could use truncated normal together with taking absolute values to make result positive.
Along the lines
double R = 10.0; // upper value of the truncated normal
var seed = 31234567;
Random rng = new Random( seed );
double u1 = rng.NextDouble();
double u2 = rng.NextDouble();
double phi = 2.0*Math.PI*u2;
double r = Math.Sqrt(-2.0*Math.Log(1.0 - u1*(1.0 - Math.Exp(-R*R/2.0))));
return new Tuple<double,double>(Math.Abs(r*Math.Cos(phi)), Math.Abs(r*Math.Sin(phi)));
Code above shall return couple of sampled values in the interval from 0 to R which looks like truncated gaussian. You could compare with Box-Muller for standard gaussain sampling

How to use setpixel for negative pixel value in c#?

When I am doing to get image from pixel by doing this and got error as:
Additional information:value of "-13" isn't valid.
So please help me.
bmp.Setpixel(x,y,Color.FromArgb(100,-12,100,100);
What to do for negative pixel value as above?
You should do one of two things.
Option 1 is what you appear to be doing now, but with a limit
int clampedRed = Math.Max(0, red - average);
// Repeat for Blue, Green
bmp.SetPixel(x,y,Color.FromArgb(100, clampedRed, ...)
Better however would be to not use the average pixel values, as that is going to drive half your image into black. Probably better to "normalise" the image. That means you need to find the MIN and MAX for each channel in the image (or a quartile) and then scale all pixels.
int minRed = // Get min in image
int maxRed = // get max in image
int rangeRed = maxRed - minRed
float scaling = 255 / rangeRed;
foreach (pixel in image){
int normalisedRed = (int)((pixelRed - minRed) * scaling)
int clampedRed = Math.Max(0, Math.Min(255, normalisedRed));
// And then use that...
}

Logarithmic track bar for zoom values of 1% to 1000%

I have a trackbar associated with a picture box where I am drawing an image based on the selected zoom factor. The range is from 1% to 1,000% so the lower you slide it, the faster it appears to zoom out.
This is expected but not desired. Is there a way to scale interpret the slider values so that zooming appears more natural to the user, specially in the < 50% range.
This is easily done:
myTrackBar.Minimum = 0;
myTrackBar.Maximim = 3000;
...
public double RealValue
{
get
{
var trackPos = myTrackBar.Value;
return Math.Pow(10.0, trackPos / 1000.0);
}
set
{
var logValue = Math.Log10(value) * 1000;
myTrackBar.Value = (int) logValue;
}
}
To understand how this works, consider your range - 1 to 1000, or expressed as powers of 10 it is 1e0 to 1e3. Hence if we give the track bar a range from 0 to 3 and raise 10 to the value, we get a nice exponential set of values, just like you want.
But if we set the range to 0..3 we could only select from 4 different values: 0, 1, 2, 3 which would translate into 1, 10, 100 and 100 respectively.
To give us values inbetween, we simply multiply the range by a thousand, giving us 3001 different values that the track bar can keep track off, and then divide the trackbar's value by a thousand.

OpenGL Gl.glColor3f How to color with one parametr?

I have some question about visualization in OpenGL. I have points in 3D space, each point also have one extraValue, which represent diffrent values, eg temperature, pressure and so on. User chooses one of this and other method sets extraValue to each point.
First problem is, that this values have diffrent ranges, eg:
temperature: <80; 2000>
preassure: <-500; 400>
gamma: <0,5; 1,8>
...
Now I want to visualize it to look for reliable, for example the temperature: 80 C is cold, so blue color, 2000 is hot so red. Similarry for others, preassure, gamma and so on.
The second problem is that Gl.glColor3f accepts 3 parameters: red, green, blue. I have only ONE parameter to each ponit.
Range of RGB is <0;1>, my values have diffrent ranges.
Does anybody have an idea, or some algorithm that could help mi with this ?
Firstly, remap your value into a range of 0-1, like so:
double t = ( value - min ) / ( max - min ); // i.e. min could be 80 and max 2000
// it might be a good idea to limit t to 0-1 here, in case
// your original value could be outside the valid range
Then, do a linear interpolation between the colors, like so (pseudocode):
Color a = Blue, b = Red
double inv = 1.0 - t
Color result = Color( inv * a.R + t * b.R,
inv * a.G + t * b.G,
inv * a.B + t * b.B )
That should get you started!

Conditional formatting -- percentage to color conversion

What's the easiest way to convert a percentage to a color ranging from Green (100%) to Red (0%), with Yellow for 50%?
I'm using plain 32bit RGB - so each component is an integer between 0 and 255. I'm doing this in C#, but I guess for a problem like this the language doesn't really matter that much.
Based on Marius and Andy's answers I'm using the following solution:
double red = (percent < 50) ? 255 : 256 - (percent - 50) * 5.12;
double green = (percent > 50) ? 255 : percent * 5.12;
var color = Color.FromArgb(255, (byte)red, (byte)green, 0);
Works perfectly - Only adjustment I had to make from Marius solution was to use 256, as (255 - (percent - 50) * 5.12 yield -1 when 100%, resulting in Yellow for some reason in Silverlight (-1, 255, 0) -> Yellow ...
I made this function in JavaScript. It returns the color is a css string. It takes the percentage as a variable, with a range from 0 to 100. The algorithm could be made in any language:
function setColor(p){
var red = p<50 ? 255 : Math.round(256 - (p-50)*5.12);
var green = p>50 ? 255 : Math.round((p)*5.12);
return "rgb(" + red + "," + green + ",0)";
}
What you probably want to do is to assign your 0% to 100% some points in a HSV or HSL color-space. From there you can interpolate colors (and yellow just happens to be between red and green :) and convert them to RGB. That will give you a nice looking gradient between the two.
Assuming that you will use the color as a status indicator and from a user-interface perspective, however, that is probably not such a good idea, since we're quite bad at seeing small changes in color. So dividing the value into, for example, three to seven buckets would give you more noticeable differences when things change, at the cost of some precision (which you most likely would not be able to appreciate anyway).
So, all the math aside, in the end I'd recommend a lookup table with the following colors with v being the input value:
#e7241d for v <= 12%
#ef832c for v > 12% and v <= 36%
#fffd46 for v > 36% and v <= 60%
#9cfa40 for v > 60% and v <= 84%
#60f83d for v > 84%
These have been very naïvely converted from HSL values (0.0, 1.0, 1.0), (30.0, 1.0, 1.0), (60.0, 1.0, 1.0), (90.0, 1.0, 1.0), (120.0, 1.0, 1.0), and you might want to adjust the colors somewhat to suit your purposes (some don't like that red and green aren't 'pure').
Please see:
Using HSL Color (Hue, Saturation, Luminosity) To Create Better-Looking GUIs for some discussion and
RGB and HSL Colour Space Conversions for sample C# source-code.
In pseudocode.
From 0-50% your hex value would be FFxx00 where:
XX = ( Percentage / 50 ) * 255 converted into hex.
From 50-100 your hex value would be xxFF00 where:
XX = ((100-Percentage) / 50) * 255 converted into hex.
Hope that helps and is understandable.
This is a nice clean solution that improves on the currently accepted answer in three ways:
Removes the magic number (5.12), therefore making the code easier to follow.
Won't produce the rounding error that's giving you -1 when the percentage is 100%.
Allows you to customise the minimum and maximum RGB values you use, so you can produce a lighter or darker range than simple rgb(255, 0, 0) - rgb(0, 255, 0).
The code shown is C# but it's trivial to adapt the algorithm to any other language.
private const int RGB_MAX = 255; // Reduce this for a darker range
private const int RGB_MIN = 0; // Increase this for a lighter range
private Color getColorFromPercentage(int percentage)
{
// Work out the percentage of red and green to use (i.e. a percentage
// of the range from RGB_MIN to RGB_MAX)
var redPercent = Math.Min(200 - (percentage * 2), 100) / 100f;
var greenPercent = Math.Min(percentage * 2, 100) / 100f;
// Now convert those percentages to actual RGB values in the range
// RGB_MIN - RGB_MAX
var red = RGB_MIN + ((RGB_MAX - RGB_MIN) * redPercent);
var green = RGB_MIN + ((RGB_MAX - RGB_MIN) * greenPercent);
return Color.FromArgb(red, green, RGB_MIN);
}
Notes
Here's a simple table showing some percentage values, and the corresponding red and green proportions we want to produce:
VALUE GREEN RED RESULTING COLOUR
100% 100% 0% green
75% 100% 50% yellowy green
50% 100% 100% yellow
25% 50% 100% orange
0% 0% 100% red
Hopefully you can see pretty clearly that
the green value is 2x the percentage value (but capped at 100)
the red is the inverse: 2x (100 - percentage) (but capped at 100)
So my algorithm calculates the values from a table looking something like this...
VALUE GREEN RED
100% 200% 0%
75% 150% 50%
50% 100% 100%
25% 50% 150%
0% 0% 200%
...and then uses Math.Min() to cap them to 100%.
I wrote this python function based on the javascript code. it takes a percentage as a decimal. also i have squared the value to keep the colours redder for longer down the percentage scale. I also narrowed the range of colours from 255 to 180 to give a darker red and green at each end. these can be played with to give nice colours. I'd like to add a touch of orange in the middle, but i gotta get on with proper work, boo.
def getBarColour(value):
red = int( (1 - (value*value) ) * 180 )
green = int( (value * value )* 180 )
red = "%02X" % red
green = "%02X" % green
return '#' + red + green +'00'
As yellow is a mix of red and green, you can probably start with #F00 and then slide green up until you hit #FF0, then slide red down to #0F0:
for (int i = 0; i < 100; i++) {
var red = i < 50
? 255
: 255 - (256.0 / 100 * ((i - 50) * 2));
var green = i < 50
? 256.0 / 100 * (i * 2)
: 255;
var col = Color.FromArgb((int) red, (int) green, 0);
}
I use the following Python routines to blend between colours:
def blendRGBHex(hex1, hex2, fraction):
return RGBDecToHex(blendRGB(RGBHexToDec(hex1),
RGBHexToDec(hex2), fraction))
def blendRGB(dec1, dec2, fraction):
return [int(v1 + (v2-v1)*fraction)
for (v1, v2) in zip(dec1, dec2)]
def RGBHexToDec(hex):
return [int(hex[n:n+2],16) for n in range(0,len(hex),2)]
def RGBDecToHex(dec):
return "".join(["%02x"%d for d in dec])
For example:
>>> blendRGBHex("FF8080", "80FF80", 0.5)
"BFBF80"
Another routine wraps this to blend nicely between numerical values for "conditional formatting":
def colourRange(minV, minC, avgV, avgC, maxV, maxC, v):
if v < minV: return minC
if v > maxV: return maxC
if v < avgV:
return blendRGBHex(minC, avgC, (v - minV)/(avgV-minV))
elif v > avgV:
return blendRGBHex(avgC, maxC, (v - avgV)/(maxV-avgV))
else:
return avgC
So, in Jonas' case:
>>> colourRange(0, "FF0000", 50, "FFFF00", 100, "00FF00", 25)
"FF7F00"
My solution for ActionScript 3:
var red:Number = (percentage <= 50) ? 255 : 256 - (percentage - 50) * 5.12;
var green:Number = (percentage >= 50) ? 255 : percentage * 5.12;
var redHex:Number = Math.round(red) * 0x10000;
var greenHex:Number = Math.round(green) * 0x100;
var colorToReturn:uint = redHex + greenHex;
Because it's R-G-B, the colors go from integer values of -1 (white), to -16777216 for black. with red green and yellow somewhere in the middle that. Yellow is actually -256, while red is -65536 and green is -16744448. So yellow actually isn't between red and green in the RGB notation. I know that in terms of wavelenghts, green is on one side, and red is on the other side of the spectrum, but I've never seen this type of notation used in computers, as the spectrum doesn't represent all visible colours.

Categories