How would I go about taking a BMP which is 24 bits and converting it to a Monochrome Bitmap? I am not concerned about losing colors, etc. and I do not want to use another file format.
There are basically two methods. You can use Interop methods to read the raw BMP data and write raw BMP data for a monchrome bitmap. There are googlable functions which will do this.
Or, better, you can use ImageMagickObject to convert the image using a stoichastic dither.
Do the second one.
If you do the first one, you should still use a stocichastic dither, but you will have to implement it by hand.
Edit: You asked "what do the following RGB values become"... the answer is they become what you want them to become. YOU DECIDE.
The obvious choices are to either use a strict threshold, where anything less than X becomes black, anything more becomes white, or you can use a stoichastic dither. Select two thresholds, black Threshold bt and white threshold wt, such that 0 < bt < wt < 255. Then for each point choose a random number q between 0.0. and 1.0. Compare the pixel brightness ((r+g+b)/3) to (q*(wt-bt)+bt). If it is greater or equal, it is white, if less, black. This will give you a nice dithered greyscale. For most purposes 31 and 224 are good values for bt and wt, but for photographic images 0 and 255 might be better.
Related
Following up on this question.
I've managed to extract pixel informations off a bitmap instance using Bitmap.LockBits. PixelFormat is Format48bppRbg which, based on my understanding and Peter's answer on the aforementioned question, should store each pixel's RGB color channels using two bytes for storage. The bytes's combined value should be equal to a number between 0 and 8192, representing an RBG channel intensity value. That value is obtained by passing both bytes to the BitConverter.ToUInt16 method.
So upon extracting pixel informations for the following 3 x 3 bitmap (magnified here for clarity):
I'm having first row of pixel channels going like this:
Pixel color
Red intensity
Green intensity
Blue intensity
RED
8192
0
0
GREEN
0
8192
0
BLUE
0
0
8192
So far so good.
On the second row, however, it goes like this:
Pixel color
Red intensity
Green intensity
Blue intensity
WHITE
8192
8192
8192
GRAY (!)
1768 (!)
1768 (!)
1768 (!)
BLACK
0
0
0
The white and black pixel channel values make sense to me.
The gray, however, doesn't.
If you use any color picker on the gray pixel above, you should get a perfectly medium gray. In 24 bits color it should be the equivalent of (R: 128, G: 128, B: 128), or #808080 in hexadecimal form.
Then how come, in a Format48bppRpg pixel format, the channels intensity is way below the expected, middle 4096 value? Isn't this GDI+ based range of 0-8192 supposed to work like its 8 bits counterpart, with 0 being the lowest intensity and 8192 the highest? What am I missing here?
For reference, here is a screen capture from Visual Studio debugger showing the raw bytes indexes and values, with additional notes on the stride, channels positions and their extracted intensity value, up to the gray pixel:
The part where you state an incorrect assumption is that #808080 is "perfectly medium gray". It is not, at least not if you look at it from a certain way (more about it here).
Many color standards, including sRGB, use gamma compression to make darker colors more spaced out in the range of 256 values normally used to store RGB colors. This roughly means taking a square root (or 2.2-root) of the relative component value before encoding this value. The reason is that the human eye (like other senses) perceives brightness logarithmically, thus it is important to represent even an arithmetically small change in the brightness if it would mean actually doubling it, for example.
The byte value of 128 is actually about 21,95 % (128/255 ^ 2.2) of full brightness, which is what you're seeing in the case of 16-bit components. The space of possible values there is much larger, thus GDI (or the format) doesn't need to store them in a special way anymore.
In case you need an algorithm, taking the 2.2-root of the value works mostly well, but the correct formula is a bit different, see here. The root function normally has an infinite slope near zero, so the specific formula attempts to fix that by making that portion linear. A piece of code can be derived from that quite easily:
static byte TransformComponent(ushort linear)
{
const double a = 0.055;
var c = linear / 8192.0;
c = c <= 0.0031308 ? 12.92 * c : (1 + a) * Math.Pow(c, 1/2.4) - a;
return (byte)Math.Round(c * Byte.MaxValue);
}
This gives 128 for the value of 1768.
In SharpDx BGRA shift for red is 16:
(color >> 16) & 255
see here.
But in .NET, ARGB shift for red is also 16:
private const int ARGBRedShift = 16;
see here and here and here.
I'm confused, what's right?
this (like .NET):
public int PackeColorToArgb()
{
int value = B;
value |= G << 8;
value |= R << 16;
value |= A << 24;
return (int)value;
}
or this (like SharpDx):
public int PackeColorToArgb()
{
int value = A;
value |= B << 8;
value |= G << 16;
value |= R << 24;
return (int)value;
}
For .Net 0xFFFF0000 is Argb Red, but for SharpDx this is Bgra Red. what's right?
The right way to pack BGRA and ARGB color to int
That depends on where you're going to use the int. But for the two examples you've provided, you'll pack the byte values into the integer value in exactly the same way. They are both "right". It's only the name that's different, and you can have many different names for the same thing.
Importantly, your second code example — supposedly "correct" for SharpDx — is not correct for the color format you're asking about. You can see right in the source code you reference, while you are packing the bytes in the order A, B, G, and R, LSB to MSB, the correct order of component is in fact BGRA (again, LSB to MSB).
Your second code example should look just like the first.
Long version…
As you can see from the two source code references you've noted, the actual formats are identical. That is, the format from each API, stores the byte values for each color component in the same order, within a single 32-bit integer: blue in the lowest 8 bits, then green, then red, then alpha in the highest 8 bits.
The problem is, there's no uniform standard for naming such formats. In the context of .NET, they list the color components in big-endian order (which could be thought of as a little ironic, since so much of the Windows ecosystem is based on little-endian hardware…but, see below). I.e. the most-significant byte is listed first: "ARGB". I call this name "big-endian order" simply because that name is consistent with a scenario in which one stores the 32-bit integer in a sequence of 4 bytes in memory on a computer running in big-endian mode. The order of component initials in the name is the same order they'd appear in that context.
On the other hand, in the context of SharpDx, the name is consistent with the order of bytes you'd see on little-endian hardware. The blue byte would come first in memory, then green, red, and finally alpha.
Fact is, both of these are somewhat arbitrary. While most mainstream PCs are running in little-endian mode now, which would argue in favor of the SharpDx naming scheme (which is inherited from the DirectX environment), these APIs both also can be found on big-endian hardware as well, especially as .NET Core is gaining traction. And in a lot of cases, the programmer using the API doesn't even really care what order the bytes are in. For code that has to deal with the individual bytes, it's still important, but a lot of the time it's more about just knowing what format the tools you're using is writing bitmaps in, and then making sure you've specified the correct format to the API.
All that said, I suspect that the main reason for the discrepancy has less to do with big-endian vs little-endian and more to do with underlying philosophical differences between the people responsible for the API. The fact is, even on big-endian hardware, the SharpDx format for a pixel where the components show up in memory in BGRA order will be "BGRA". Because, bitmap formats don't change just because the byte-order mode of the hardware is different. A pixel is not an integer. It's just a sequence of bytes. What will be different is that the shift values will have to be different, i.e. reversed, so that for code that does treat the pixel as a single 32-bit integer, it can access the individual components correctly.
Instead, it seems to me that the .NET designers recognized that the users of their API will most of the time be dealing with colors at a high level (e.g. setting the color of a pen or brush), not at the pixel level of bitmaps, and so naming the pixel format according to conventional order makes more sense. On the other hand, in SharpDx people are much more often dealing with the low-level pixel data, and having a name that reflects the actual byte-wise sequence of components for a pixel makes more sense in that context.
Indeed, the .NET code you've referenced doesn't involve bitmap data. The Color struct is only ever dealing with single int values at a time, with respect to the "ARGB" nomenclature. Since conceptually, we imagine numbers in big-endian format (even for decimal, i.e. with the most-significant digits first), ARGB is more human-readable. On the other hand, in the areas of .NET that do involve byte order in pixel formats, you'll find that the naming goes back to being representative of the actual byte order, e.g. the list of PixelFormats introduced with WPF.
Clear as mud, right? :)
I have a sample wpf app here and wondering, why BMP is loading faster than PNG. Here is the exact setup:
- Windows 7
- Visual Studio 2013
- landscape.png, 1920x1080, 2.4mb
- landscape.bmp, 1920x1080, 5.6mb
- wpf-app, class MainWindow.xaml.cs constructor, no other code before
Code:
var sw = Stopwatch.StartNew();
var Bitmap1 = new Bitmap("landscape.bmp");
long t1 = sw.ElapsedMilliseconds;
sw.Restart();
var Bitmap2 = new Bitmap("landscape.png");
long t2 = sw.ElapsedMilliseconds;
So the BMP loads with around 6ms, the PNG needs 40ms.
Why is that so?
First, we need to understand how digital images are stored and shown, a digital image is represented as a matrix where each element of the matrix is the color of the pixel, if you have a grayscale image then each element is a uint8 (unsigned 8-bit integer) number between 0 and 255 and in some cases, it's an int8 (signed 8-bit integer) number between -128 and 127. if the element is 0 (or -128 in int8 version) the color is solid black and if the element is 255 (or 127 in int8 version) the color is solid white.
For RGB images each element of the said matrix takes 24 bit or 3 Byte to store (one Byte for each color), a very common resolution for digital cameras and smartphones is 3264 x 2448 for an 8-megapixel camera, now imagine we want to save a 3264 row matrix where each row has 2448 element and each element is 3 Byte, we need about 24 MegaByte to store that image which is not very efficient for posting on the internet or transferring or most of the other purposes. That is why we should compress the image, we can go for JPEG which is a lossy compression method and that means we do lose some quality or we can choose a lossless compression method like PNG which will give us less compression ratio but instead we are not gonna lose quality.
Whether we chose to compress the image or not, when we want to see the image, we can only show the uncompressed version of the image, if the image is not compressed at all, there is no problem, we show exactly what it is, but if it's compressed, we have to decode it first (uncompress it).
With all that being said, let's answer the question. BMP is a format for somewhat raw images, there is either no compression at all or much fewer compression techniques are used than PNG or JPEG but the file size is bigger. When you want to show a BMP image, because it's bigger, there is more data to read into memory, but when it is read, you can show it very faster because there is either no need for decoding or much less decoding is required, on the other hand when you want to show a PNG image, the image will be read into memory much faster but compared to BMP the decoding is going take more time.
If you have a very slow storage, BMP images will be shown slow.
If you have a very slow CPU or your decoding software is not efficient PNG images will be shown slow.
here is my scenario:
I'm using color.fromargb in C# to get a color out of an int upto 800 (which can be reversed by toargb).
I'm setting a certain pixel( eg: 0,0) in a bitmap to that color and saving it as jpg and when using getpixel to get that color and that int back, I receive a negative value which has puzzled me.
any thoughts and suggestion ?
I suspect two things are at play here.
Firstly JPEG is a lossy format so the number you put in might not be the exact number you get out, a true black is likely to become a gray.
Secondly, why do you get a negative when you start with a positive number? Well this is all down to the way int and uint and colours are represented in binary.
In RGB notation black has hex value #ffffff in ARGB it is #ffffffff.
0xffffffff in hex is 4,294,967,295 in decimal.
However an int is a signed type, meaning it is represented in two's complement representation
If the highest bit in the bit in the number is set then you need to deduct -2,147,483,648; that is, hexadecimal 0x80000000.
So 0xffffffff becomes 2,147,483,647 - 2,147,483,648 = -1.
If your black became a slight gray like #ffeeeeee then its decimal value in two's complement notation would be -2,146,365,166.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
convert image to Black-White or Sepia in c#
I'm writing a C# application, that opens an image, and clicking on a button, displays it in only black and white!
I was sure to find a lot of information on the net, but my searches didn't give me a lot of understandable and useful information.
I have no idea of how to proceed. Does any one have any advice ? Know a tutorial on the net?
I once found a function that converts a bitmap to grayscale
public void ToGrayScale(Bitmap Bmp)
{
int rgb;
Color c;
for (int y = 0; y < Bmp.Height; y++)
for (int x = 0; x < Bmp.Width; x++)
{
c = Bmp.GetPixel(x, y);
rgb = (int)Math.Round(.299 * c.R + .587 * c.G + .114 * c.B);
Bmp.SetPixel(x, y, Color.FromArgb(rgb, rgb, rgb));
}
}
The function accepts a bitmap as a parameter, and changes it to its grayscale version.
I hope this helps.
Edit See fuller answer here: convert image to Black-White or Sepia in c#.
There are many way to desaturate a color image. In fact, there is probably no one "true" or "correct" way to do it, though some way are more correct than others.
I assume that your image is in RGB (Red-Green-Blue) format (though BGR is also common).
The simplest way, which should work for most photos (but less so for synthetic images), is to just use the Green channel of the 3 RGB channels. Humans are most sensitive to variations in the green part of the spectrum, so the green channel covers most of the visible range and is a good approximation to the grayscale image you want.
A better way to generate a grayscale image is to use a weighted average of the 3 RGB channels. Choosing equal weights (0.33*R+0.33*G+0.33*B) will give a pretty good grayscale image. Other weight will give different results some of which may be considered more aesthetically pleasing, and some may take into consideration perceptual parameters.
You could always convert the image to another color space which has only a single grayscale channel (and 2 "color" channels), such as HSV (V is the grayscale), YUV (Y is the grayscale) or Lab (L is the grayscale). The differences should not be very big.
The term "de-saturation" comes from the HSV space. If you convert you image to HSV, set the S channel (Saturation) to be all zeros, and render the image, you will get a 3-channel desaturated "color" image.