I am sorry if the question in the header is not descriptive enough. But, basically what my problem is the following.
I am taking Bitmap and making it gray scale. It works nice if i do not reduce the number of bits and I still use 8 bits. However, the point of the hw I have is to show how the image changes when I reduce the number of bits holding the information. In the example bellow I am reducing the binary string to 4 bits and then rebuilding the image again. The problem is that the image becomes black. I think is because the image has mostly gray values (in the 80's range) and when I am reducing the binary string I am left with black image only. It seems to me that i heave to check for lower and high gray scale values and then make the more light-gray go to white and dark gray go to black. In the end with 1 bit representation I should only have black and white image.Any idea how can i do that separation?
Thanks
Bitmap bmpIn = (Bitmap)Bitmap.FromFile("c:\\test.jpg");
var grayscaleBmp = MakeGrayscale(bmpIn);
public Bitmap MakeGrayscale(Bitmap original)
{
//make an empty bitmap the same size as original
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
for (int i = 0; i < original.Width; i++)
{
for (int j = 0; j < original.Height; j++)
{
//get the pixel from the original image
Color originalColor = original.GetPixel(i, j);
//create the grayscale version of the pixel
int grayScale = (int)((originalColor.R * .3) + (originalColor.G * .59)
+ (originalColor.B * .11));
//now turn it into binary and reduce the number of bits that hold information
byte test = (byte) grayScale;
string binary = Convert.ToString(test, 2).PadLeft(8, '0');
string cuted = binary.Remove(4);
var converted = Convert.ToInt32(cuted, 2);
//create the color object
Color newColor = Color.FromArgb(converted, converted, converted);
//set the new image's pixel to the grayscale version
newBitmap.SetPixel(i, j, newColor);
}
}
return newBitmap;
}
As mbeckish said, it is easier and much faster to use ImageAttributes.SetThreshold.
One way to do it manually is to get the median value of the grayscale pixels in the image, and use that for the threshold between black and white.
Related
I am trying to take a grayscale bitmap and extract a single line from it and then graph the gray values. I got something to work, but I'm not really happy with it. It just seems slow and tedious. I am sure someone has a better idea
WriteableBitmap someImg; //camera image
int imgWidth = someImg.PixelWidth;
int imgHeight = someImg.PixelHeight;
Int32Rect rectLine = new Int32Rect(0, imgHeight / 2, imgWidth, 1); //horizontal line half way down the image as a rectangle with height 1
//calculate stride and buffer size
int imgStride = (imgWidth * someImg.Format.BitsPerPixel + 7) / 8; // not sure I understand this part
byte[] buffer = new byte[imgStride * rectLine.Height];
//copy pixels to buffer
someImg.CopyPixels(rectLine, buffer, imgStride, 0);
const int xGraphHeight = 256;
WriteableBitmap xgraph = new WriteableBitmap(imgWidth, xGraphHeight, someImg.DpiX, someImg.DpiY, PixelFormats.Gray8, null);
//loop through pixels
for (int i = 0; i < imgWidth; i++)
{
Int32Rect dot = new Int32Rect(i, buffer[i], 1, 1); //1x1 rectangle
byte[] WhiteDotByte = { 255 }; //white
xgraph.WritePixels(dot, WhiteDotByte, imgStride, 0);//write pixel
}
You can see the image and the plot below the green line. I guess I am having some WPF issues that make it look funny but that's a problem for another post.
I assume the goal is to create a plot of the pixel value intensities of the selected line.
The first approach to consider it to use an actual plotting library. I have used oxyplot, it works fine, but is lacking in some aspects. Unless you have specific performance requirements this will likely be the most flexible approach to take.
If you actually want to render to an image you might be better of using unsafe code to access the pixel values directly. For example:
xgraph.Lock();
for (int y = 0; y < imgHeight; y++){
var rowPtr = (byte*)(xgraph.BackBuffer + y * xgraph.BackBufferStride);
for(int x = 0; x < imgWidth; x++){
rowPtr[x] = (byte)(y < buffer[i] ? 0 : 255);
}
}
self.Unlock(); // this should be placed in a finally statement
This should be faster than writing 1x1 rectangles. It should also write columns instead of single pixels, and that should help making the graph more visible. You might also consider allowing arbitrary image height and scale the comparison value.
If you want to plot the pixel values along an arbitrary line, and not just a horizontal one. You can take equidistant samples along the line, and use bilinear interpolation to sample the image.
Hi I want to get the integer values (0-255 range) of a gray scale image ....this code shows me the R,G,B values not one value..how can i get it?
Bitmap temp1 = image1;
for (int i = 0; i < temp1.Height; i++)
{
for (int j = 0; j < temp1.Width; j++)
{
Color cl = new Color();
cl = temp1.GetPixel(i, j);
}
}
just read the properties R or G or B, any of them will have the same value.
var intValue = cl.R;
If your source image is greyscale and you just want the level of greyness, just pick any of the three components. They will be equal.
If your source image is color but you want to get the grey equivalent, you can convert your color to a grey value in the range 0..255 by blending the red, green and blue color components together. The blending factors are different because the human eye has different sensitivity to the three primary colors. For fun, try varying the factors (e.g. use 0.3333 for each) and see what the result looks like.
Color cl = c.GetPixel(i, j); // No need to separately allocate a new Color()
int greyValue = (int)((cl.R * 0.3) + (cl.G * 0.59) + (cl.B * 0.11));
Color grey = Color.FromArgb(cl.A, greyValue, greyValue, greyValue);
Note that it is quite slow to loop through a larger Bitmap, using GetPixel() on each pixel. There are much faster techniques available.
UPDATE
Here's an example image with different scaling factors for R, G, and B applied. The image will always be greyscaled because the same numeric value is used for each RGB component in the modified image, but the relative lightness does change. The middle image uses scaling factors suitable for the human eye. Note how blue areas in the original image seem oversaturated in the rightmost version.
There are multiple ways to get grayscale from RGB.
A common way is to do (R+G+B)/3
Others are computing some luminance Luminance measure (Lab, YUV, HSV)
Please dont quote this How can I check the color depth of a Bitmap? or How to check if a picture is in grayScale
Because an image can be 24/32 bit per pixel even if all unique colors are grey scale or less than 256 and each pixel can be 24 or 32 bit.
How do I find that the pixel in an image Bitmap.GetPixel(x, y) has equal R,G,B vales so that I can find out if all pixels in an image lie in grey scale range. Because the R,G,B value of a grey scale pixel is same. Or is there any better way to find if the image is in grey scale?
I am writing a code to compress the size of a 16/24/32 bit image such that if the image has 256 unique colors then change it to 8 bit image and save it.
First I count unique colors in an image that has higher than 8 its per pixel.
If the unique colors in the image are less than or equal to 256 then
If all the unique colors are in greyscale range then convert it to greyscale
Otherwise if any color is not greyscale then convert image to 8 BPP
uint UniqueColors(Bitmap Bitmap)
{
try
{
List<int> lstColors = new List<int>();
if (null == Bitmap)
return 0;
for (int iCount = 0; iCount < Bitmap.Height; iCount++)
for (int iCounter = 0; iCounter < Bitmap.Width; iCounter++)
if (!lstColors.Contains(Bitmap.GetPixel(iCounter, iCount).ToArgb()))
lstColors.Add(Bitmap.GetPixel(iCounter, iCount).ToArgb());
Bitmap.Dispose();
return Convert.ToUInt32(lstColors.Count);
}
catch (Exception)
{
Bitmap.Dispose();
return 0;
}
}
And then:
if (256 >= UniqueColors(new Bitmap(string ImagePath)))
{
if (Is_Greyscale(new Bitmap(ImagePath))
Convert_To_Greyscale(ImagePath);
else
Convert_To_8Bits(ImagePath);
}
now I am stuck how do I find if each unique color in the image lies in the greyscae rage. By which I mean that each unique color has equal (R, G, B) values. Like R=G=B. How can I find it in my code line
Bitmap.GetPixel(iCounter, iCount).ToArgb()
Bitmap.GetPixel() returns a Color structure which has fields R, G and B so you can just compare them however you want.
Do note that using GetPixel() is very slow, but if you don't need speed it will do.
I'm working with Scintilla (not ScintillaNET), and I'm trying to set the images used in the auto complete list. Scintilla requires a string of bytes/pixels in the RGBA format.
Scintilla's description of this RGBA format: (Quote from documentation)
The RGBA format allows translucency with an alpha value for each
pixel. It is simpler than XPM and more capable.
The data is a sequence of 4 byte pixel values starting with the pixels
for the top line, with the leftmost pixel first, then continuing with
the pixels for subsequent lines. There is no gap between lines for
alignment reasons.
Each pixel consists of, in order, a red byte, a green byte, a blue
byte and an alpha byte. The colour bytes are not premultiplied by the
alpha value. That is, a fully red pixel that is 25% opaque will be
[FF, 00, 00, 3F]
I expect that I'm misunderstanding the format explained above, the documentation is not very clear.
My Conversion Method:
I wrote this function to convert a PNG into this string of bytes:
public static string ConvertFromPNG(Bitmap PNG)
{
string rgba = "";
int pixWidth = PNG.Width;
int pixHeight = PNG.Height;
for (var y = 0; y < pixHeight; y++)
{
for (var x = 0; x < pixWidth; x++)
{
Color pix = PNG.GetPixel(x, y);
rgba += pix.R.ToString("X2") + pix.G.ToString("X2") + pix.B.ToString("X2") + pix.A.ToString("X2");
}
}
return rgba;
}
The Resulting Image:
But Scintilla is just showing the image as a grey box:
.
The image in question is a copy of one of Microsoft's Babel images: .
I know that the set of bytes is correct because interestingly, if I format them in lines and zoom out on them I can see the outline of the image:
Bytes Generated:
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009F000000EC0000001B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000815B0000DF9B0000F8000000B40000002F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B0000007F4E0000FDC80000FFFF0000FF720000FF1D0000B50000003E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001613000085350000FFDE0000FFFF0000FFFF0000FFFF0000FF750000FF230000C4000000240000000000000000000000000000000000000000000000000000000000000000000000000000000000000085400000EADB0000FFF90000FFFF0000FFFF0000FFFF0000FFF60000FFA70000F5000000DA00000000000000000000000000000000000000000000000000000000000000000000000000000091810000EACA0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFBB0000FF460000FF000000FF000000000000000000000000000000000000000000000000000000000000000000000000000000FF7E0000FFE80000FFFF0000FFFF0000FFFF0000FFFB0000FFB30000FF460000FF0C0000FF000000FF000000000000000000000000000000000000000000000000000000000000000000000000000000FF000000FF660000FFFF0000FFFF0000FFFF0000FFC70000FF0D0000FF590000FF7A0000FF000000FF000000000000000000000000000000000000000000000000000000000000000000000000000000FF8C0000FF7D0000FF2E0000FFFF0000FFBB0000FF1C0000FF510000FFB40000FF7B0000FF000000FF000000000000000000000000000000000000000000000000000000000000000000000000000000FFE20000FFE40000FF410000FF8B0000FF510000FF350000FFB60000FF8F0000FF3A0000EA00000091000000000000000000000000000000000000000000000000000000000000000000000000000000DA910000F5F30000FFFF0000FF0C0000FF270000FFC10000FF930000FF220000EA0000008500000000000000000000000000000000000000000000000000000000000000000000000000000000000000241D0000C4870000FFFF0000FF270000FF420000FF9E0000FF230000FF090000860000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003E2D0000B5610000FF120000FF1C0000FF370000FD0000007F0000001B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E000000B4000000F8000000DF00000081000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B000000EC0000009F0000000000000000000000000000000000000000
You are formatting the bytes as hex values and placing them in a string. Scintilla just wants the bytes themselves.
I suspect you want to create a byte array rather than a string, and store into it the values of pix.R, pix.G, pix.B, and pix.A directly, rather than formatting them as strings.
After accepting JasonD's answer I've updated my code and it's working. In case it helps anyone else, here is the working code:
public static byte[] ConvertFromPNG(Bitmap PNG)
{
byte[] rgbaB = new byte[4 * (PNG.Width * PNG.Height)];
int i = 0;
for (var y = 0; y < PNG.Height; y++)
{
for (var x = 0; x < PNG.Width; x++)
{
Color pix = PNG.GetPixel(x, y);
rgbaB[i++] = pix.R;
rgbaB[i++] = pix.G;
rgbaB[i++] = pix.B;
rgbaB[i++] = pix.A;
}
}
return rgbaB;
}
I don't know a better title, but I'll describe the problem.
A piece of hardware we use has the ability to display images.
It can display a black and white image with a resolution of 64 x 256.
The problem is the format of the image we have to send to the device.
It is not a standard bitmap format, but instead it is simply an array of
bytes representing each pixel of the image.
0 = black, 1 = white.
So if we had an image with the size: 4 x 4 the byte array might look something like:
1000 0100 0010 0001
And the image would look like:
Bitmap http://www.mediafire.com/imgbnc.php/6ee6a28148d0170708cb10ec7ce6512e4g.jpg
The problem is that we need to create this image by creating a monochrome bitmap
in C# and then convert it to the file format understood by the device.
For example, one might to display text on the device. In order to do so he would
have to create a bitmap and write text to it:
var bitmap = new Bitmap(256, 64);
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.DrawString("Hello World", new Font("Courier", 10, FontStyle.Regular), new SolidBrush(Color.White), 1, 1);
}
There are 2 problems here:
The generated bitmap isn't monochrome
The generated bitmap has a different binary format
So I need a way to:
Generate a monochrome bitmap in .NET
Read the individual pixel colors for each pixel in the bitmap
I have found that you can set the pixel depth to 16, 24, or 32 bits, but haven't found monochrome and I have no idea how to read the pixel data.
Suggestions are welcome.
UPDATE: I cannot use Win32 PInvokes... has to be platform neutral!
FOLLOW UP: The following code works for me now. (Just in case anybody needs it)
private static byte[] GetLedBytes(Bitmap bitmap)
{
int threshold = 127;
int index = 0;
int dimensions = bitmap.Height * bitmap.Width;
BitArray bits = new BitArray(dimensions);
//Vertically
for (int y = 0; y < bitmap.Height; y++)
{
//Horizontally
for (int x = 0; x < bitmap.Width; x++)
{
Color c = bitmap.GetPixel(x, y);
int luminance = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11);
bits[index] = (luminance > threshold);
index++;
}
}
byte[] data = new byte[dimensions / 8];
bits.CopyTo(data, 0);
return data;
}
I'd compute the luminance of each pixel a then compare it to some threshold value.
y=0.3*R+0.59G*G+0.11*B
Say the threshold value is 127:
const int threshold = 127;
Bitmap bm = { some source bitmap };
byte[,] buffer = new byte[64,256];
for(int y=0;y<bm.Height;y++)
{
for(int x=0;x<bm.Width;x++)
{
Color c=source.GetPixel(x,y);
int luminance = (int)(c.R*0.3 + c.G*0.59+ c.B*0.11);
buffer[x,y] = (luminance > 127) ? 1 : 0;
}
}
I don't know C#. There are possibly many ways to do it. Here is a simple way.
Create a blank black bitmap image of size equal to your device requirement. Draw on it whatever you wish to draw like text, figures etc.
Now threshold the image i.e. set the pixel of image below an intensity value to zero else set it to. e.g. set all intensity values > 0 to 1.
Now convert to the format required by your device. Create a byte array of the size (64 * 256)/8. Set the corresponding bits to 1 where the corresponding pixel values in earlier bitmap are 1, else reset them to 0.
Edit: Step 3. Use bitwise operators to set the bits.
You shouldn't use GetPixel method of your bitmap to convert entire bitmap from one format to another! This will be ineffective. Instead you should use LockBits method to get access to a copy of image buffer and convert it into desired format. I'm not completely sure about converting it to monochrome but there is Format1bppIndexed value in PixelFormat enumeration which may help you.
You may try to supply a pixelformat in the constructor:
var bitmap = new Bitmap(256, 64, PixelFormat.Format1bppIndexed);
When I did draw monochrome bitmaps on other platforms I sometimes had
to disable antialiasing or the rendered text would not show up:
graphics.SmoothingMode=SmoothingMode.None;
YMMV.
Bitmap has a GetPixel method that you can use. This will let you draw on the Bitmap and later convert it to the format that you need.
Bitmaps in Windows forms (ie, accessed through Graphics.FromImage) are 24 bpp (maybe 32? It's too early and I honestly forget). Nonetheless, GetPixel returns a Color object, so the bit depth of the bitmap is immaterial. I suggest you write your code like this:
MyBitmapFormat ToMyBitmap(Bitmap b)
{
MyBitmapFormat mine = new MyBitmapFormat(b.Width, b.Height);
for (int y=0; y < b.Height; y++) {
for (int x=0; x < b.Width; x++) {
mine.SetPixel(x, y, ColorIsBlackish(b.GetPixel(x, y)));
}
}
}
bool ColorIsBlackish(Color c)
{
return Luminance(c) < 128; // 128 is midline
}
int Luminance(c)
{
return (int)(0.299 * Color.Red + 0.587 * Color.Green + 0.114 * Color.Blue);
}
This process is called simple thresholding. It's braindead, but it will work as a first cut.
thanks for the above code - I'm trying to convert a monochrome image into a 2d array where 1-black 0-white however I'm having some trouble - I used your code to load an 8x8 bmp image, and am outputting its contents to a textbox by using
myGrid =GetLedBytes(myBmp);
for (int x = 1; x < 8; x++)
{
textBox1.Text = textBox1.Text + Convert.ToString(myGrid[x])+ " ";
}
however I get this as a result in the textbox:
225 231 231 231 231 129 255
how do I get it so it's 0's and 1's?
This chap has some code that creates a mono bitmap. The SaveImage sample is the one of interest.