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.
Related
So I want to grab a partial image from a byte array of colors. The image is a unity logo that is 64x64 pixels. I want to grab a third of the image (Unity Logo). How would I traverse the byte array to get this image?
Unity Byte Array
assuming each byte is a single pixel (which is only true for 8-bit depth images), the bytes 0-63 are the first row, 64-127 are the second row, etc etc.
meaning that to find out the position of a pixel in the one-dimensional array, based on its two-dimensional coordinates in the image itself, you do
int oneDimPos = (y*64) + x;
if each pixel were 3 bytes (24-bit color depth), the conversion from 2dimensional to 1dimensional coordinates would be:
int oneDimPos = (y * 64 * 3) + (x * 3);
(so the most generic equation is:
int oneDimPos = (y * imageWidth * colorDepth) + (x * colorDepth);
and you need to keep this in mind and adjust the code accordingly. or even better, use this most generic version, and actually read the image width and its color depth from the asset you're using as source.
BEWARE: if the image is anything else than 8bits per pixel, this equation will, naturally, only give you the first, starting bit belonging to that pixel, and you still need to take care to actually also read the other ones that belong to that pixel
i'm gonna finish the answer assuming 8bit color depth, for simplicity, as well as so that you can't just copypaste the answer, but also have to understand it and re-shape it according to your specific needs ;)
)
meaning you can now do classic two nested loops for x and y:
List<byte> result = new List(); //i'm going to use list so i can just .Add each byte instead of having to calculate and allocate the final size in advance, and having to mess around with recalculating the index from the source array into the destination one, because i'm lazy
for(int x=0; x < 22; x++){ //no way for you to grab precise third since that boundary is in the middle of a pixel for an image 64pixels wide
for(int y = 0; y < 64; y++){ //we go all the way to the bottom
result.Add(sourceAsset.bytes[(y*64) + x]);
}
}
//now just convert the list to actual byte array
byte[] resultBytes = result.ToArray();
The original issue that I was having was not exactly the same as the question. I wanted to simplify it by having a byte array that everyone could take a look at. The byte array from Unity's website wasn't exactly what I was getting.
So I have 3 x 1080p portrait screen (1080 x 1920 pixels) with RGBA channels. I grabbed a screenshot from this and got a 24,883,200 size byte array.
Note, 3 * width(1080) * height(1920) * channels(4) = 24,883,200.
byte[] colors = new byte[24883200]; // Screenshot of 3x1080p screen.
byte[] leftThird = new byte[colors.Length / 3];
Array.Copy(colors, 0, leftThird, 0, colors.Length / 3); // Grab the first third of array
This is an issue because the colors array is read from top to bottom, left to right. So instead, you should read a portion of the 3 x 1080 x 4 channels.
int width = 1080 * 4; // 4 channels of colors (RGBA)
int fullWidth = width * 3; // Three screens
int height = 1920;
byte[] leftScreen = new byte[screenShotByteArray.Length / 3];
for(int i = 0; i < height; i++)
{
Array.Copy(screenShotByteArray, (i * fullWidth) + (offset * 4), leftScreen, i * width, width);
}
How can I split an image into multiple sub images? Like, would I have to read the pixels and convert it to an image somehow?
For example:
If an image's dimension is 100px (width) and 180px (height) and I wanted to, say, split it as a 4x4, would I read the first 25px for the width and the first 45 px for the height and then just increment it correctly?
If so, what would I store the pixels to? More specifically, would it be saved as an array of bytes, images, etc?
You may try the following code sample (taken from https://stackoverflow.com/a/4118195/);
for (int i = 0; i < 4; i++)
{
for (int y = 0; y < 4; y++)
{
Rectangle r = new Rectangle(i*(pictureBox1.Image.Width / 4),
y*(pictureBox1.Image.Height / 4),
pictureBox1.Image.Width / 4,
pictureBox1.Image.Height / 4);
g.DrawRectangle(pen,r );
list.Add(cropImage(pictureBox1.Image, r));
}
}
The other alternative is using BitMap.Clone, you may find an example in the following link.
Use the Bitmap class to hold the image and its Clone method to cut out your rectangles of arbitrary size. As a Bitmap it comes with several convenience methods such as Save this overload will save to a stream another allows you to save it to a file.
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.
I'm trying to crop a rectangular region of a video RGB. First i found the coordinates of head joint and with this coordinates i drew a rectangle over the RGB video. Now i want to show in another video just the image that is inside of the rentangle in the first image. Any help would be great.
video RGB is displayed in a "RGBvideo" Image Control.
cropped image i want to display in a "faceImage" Image Control
I've search online but couldn't find a solution to it. I am confuse.
thank you so much
Welcome to Stack Overflow, please don't ask the same question multiple times. With less popular tags like Kinect, it can take some time for people to answer (the tag only has 79 followers).
For simplicity, I'm going to assume you want to crop out a set size image (for example, 60x60 pixels out of the original 800x600). In your VideoFrameReady method, you get the PlanarImage from the event args. This PlanarImage has the bits field, which contains all of the RGB data for the image. With a little math, you can cut out a small chunk of that data and use it as a smaller image.
// update video feeds
void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
{
PlanarImage image = e.ImageFrame.Image;
// Large video feed
video.Source = BitmapSource.Create(image.Width, image.Height, 96, 96, PixelFormats.Bgr32, null, image.Bits, image.Width * image.BytesPerPixel);
// X and Y coordinates of the smaller image, and the width and height of smaller image
int xCoord = 100, yCoord = 150, width = 60, height = 60;
// Create an array to copy data into
byte[] bytes = new byte[width * height * image.BytesPerPixel];
// Copy over the relevant bytes
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width * image.BytesPerPixel; j++)
{
bytes[i * (width * image.BytesPerPixel) + j] = image.Bits[(i + yCoord) * (image.Width * image.BytesPerPixel) + (j + xCoord * image.BytesPerPixel)];
}
}
// Create the smaller image
smallVideo.Source = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgr32, null, bytes, width * image.BytesPerPixel);
}
Please make sure you understand the code, instead of just copy/pasting it. The two for loops are for basic array copying, with the number of bytes per pixel taken into account (4 for BGR32). Then you use that small subset of the original data to create a new BitmapSource. You'll need to you can change the width/height as you see fit, and determine the X and Y coordinates from the head tracking.
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.