Get all pixel information of an image efficiently - c#

I made a program to get all image pixel RGB color codes from picture. Basically, it sets y position on constant and changes x position zero to width and also y by looping.
Ok it's work, but the problem is it take more than 20 minutes to get all pixel from even (1000*604 height width) image. Please anyone help?
I'm surprised if this process takes so long, then how can we make a program like bar-code reader from image. I want to get all pixel value from image, here is my C# code are below.
I also uploaded my program here, check it if you don't agree.
void myimage() {
mypic = new Bitmap(pathname);
int imwid = mypic.Width;
int imhei = mypic.Height;
int total=imwid*imhei;
for (int z = 0; z <imhei;z++ )
{
for (int i = 0; i < imwid; i++)
{
Color pixelColor = mypic.GetPixel(i, z);
textBox2.AppendText(" " + pixelColor.R +
" " + pixelColor.G +
" " + pixelColor.B + " " +
pixelColor.A +
Environment.NewLine);
}
}
}

Take a look at this:
var data = mypic.LockBits(
new Rectangle(Point.Empty, mypic.Size),
ImageLockMode.ReadWrite, mypic.PixelFormat);
var pixelSize = data.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 3; // only works with 32 or 24 pixel-size bitmap!
var padding = data.Stride - (data.Width * pixelSize);
var bytes = new byte[data.Height * data.Stride];
// copy the bytes from bitmap to array
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
var index = 0;
var builder = new StringBuilder();
for (var y = 0; y < data.Height; y++)
{
for (var x = 0; x < data.Width; x++)
{
Color pixelColor = Color.FromArgb(
pixelSize == 3 ? 255 : bytes[index + 3], // A component if present
bytes[index + 2], // R component
bytes[index + 1], // G component
bytes[index] // B component
);
builder
.Append(" ")
.Append(pixelColor.R)
.Append(" ")
.Append(pixelColor.G)
.Append(" ")
.Append(pixelColor.B)
.Append(" ")
.Append(pixelColor.A)
.AppendLine();
index += pixelSize;
}
index += padding;
}
// copy back the bytes from array to the bitmap
Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
textBox2.Text = builder.ToString();
Is just an example, read some good tutorials about LockBits and imaging in general to understand clearly what happens.

Getting pixel information shouldn't take that long. Can you log the time it takes myimage() to run? The slowness might be somewhere else. Also try removing the line that begins with textBox2.AppendText in myimage() and see how fast it runs then.

Related

C# - Padding image bytes with white bytes to fill 512 x 512

I'm using Digital Persona SDK to scan fingerprints in wsq format, for requeriment I need 512 x 512 image, the SDK only export 357 x 392 image.
The sdk provide a method to compress captured image from device in wsq format and return a byte array that I can write to disk.
-I've tried to allocate a buffer of 262144 for 512 x 512 image.
-Fill the new buffer with white pixel data each byte to value 255.
-Copy the original image buffer into the new image buffer. The original image doesn’t need to be centered but it's important to make sure to copy without corrupting the image data.
To summarize I've tried to copy the old image into the upper right corner of the new image.
DPUruNet.Compression.Start();
DPUruNet.Compression.SetWsqBitrate(95, 0);
Fid capturedImage = captureResult.Data;
//Fill the new buffer with white pixel data each byte to value 255.
byte[] bytesWSQ512 = new byte[262144];
for (int i = 0; i < bytesWSQ512.Length; i++)
{
bytesWSQ512[i] = 255;
}
//Compress capturedImage and get bytes (357 x 392)
byte[] bytesWSQ = DPUruNet.Compression.CompressRaw(capturedImage.Views[0].Width, capturedImage.Views[0].Height, 500, 8, capturedImage.Views[0].RawImage, CompressionAlgorithm.COMPRESSION_WSQ_NIST);
//Copy the original image buffer into the new image buffer
for (int i = 0; i < capturedImage.Views[0].Height; i++)
{
for (int j = 0; j < capturedImage.Views[0].Width; j++)
{
bytesWSQ512[i * bytesWSQ512.Length + j ] = bytesWSQ[i * capturedImage.Views[0].Width + j];
}
}
//Write bytes to disk
File.WriteAllBytes(#"C:\Users\Admin\Desktop\bytesWSQ512.wsq", bytesWSQ512);
DPUruNet.Compression.Finish();
When running that snippet I get IndexOutOfRangeException, I don't know if the loop or the calculation of indexes for new array are right.
Here is a representation of what I'm trying to do.
If someone is trying to achieve something like this or padding a raw image, I hope this will help.
DPUruNet.Compression.
DPUruNet.Compression.SetWsqBitrate(75, 0);
Fid ISOFid = captureResult.Data;
byte[] paddedImage = PadImage8BPP(captureResult.Data.Views[0].RawImage, captureResult.Data.Views[0].Width, captureResult.Data.Views[0].Height, 512, 512, 255);
byte[] bytesWSQ512 = Compression.CompressRaw(512, 512, 500, 8, paddedImage, CompressionAlgorithm.COMPRESSION_WSQ_NIST);
And the method to resize (pad) the image is:
public byte[] PadImage8BPP(byte[] original, int original_width, int original_height, int desired_width, int desired_height, byte pad_color)
{
byte[] canvas_8bpp = new byte[desired_width * desired_height];
for (int i = 0; i < canvas_8bpp.Length; i++)
canvas_8bpp[i] = pad_color; //Fill background. Note this type of fill will fail histogram checks.
int clamp_y_begin = 0;
int clamp_y_end = original_height;
int clamp_x_begin = 0;
int clamp_x_end = original_width;
int pad_y = 0;
int pad_x = 0;
if (original_height > desired_height)
{
int crop_distance = (int)Math.Ceiling((original_height - desired_height) / 2.0);
clamp_y_begin = crop_distance;
clamp_y_end = original_height - crop_distance;
}
else
{
pad_y = (desired_height - original_height) / 2;
}
if (original_width > desired_width)
{
int crop_distance = (int)Math.Ceiling((original_width - desired_width) / 2.0);
clamp_x_begin = crop_distance;
clamp_x_end = original_width - crop_distance;
}
else
{
pad_x = (desired_width - original_width) / 2;
}
//We traverse the captured image (either whole image or subset)
for (int y = clamp_y_begin; y < clamp_y_end; y++)
{
for (int x = clamp_x_begin; x < clamp_x_end; x++)
{
byte image_pixel = original[y * original_width + x];
canvas_8bpp[(pad_y + y - clamp_y_begin) * desired_width + pad_x + x - clamp_x_begin] = image_pixel;
}
}
return canvas_8bpp;
}

C# and Kinect v2: Get RGB values that fit to depth-pixel

I played a bit around with the Kinect v2 and C# and tried to get a 512x424 pixel-sized image array that contains depth data aswell as the regarding color information (RGBA).
Therefore I used the MultiSourceFrameReader class to receive a MultiSourceFrame from which I got the ColorFrame and DepthFrame. With the methods ColorFrame.CopyConvertedFrameDataToArray() and DepthFrame.CopyFrameDataToArray() I received the arrays that hold color and depth information:
// Contains 4*1920*1080 entries of color-info: BGRA|BGRA|BGRA..
byte[] cFrameData = new byte[4 * cWidth * cHeight];
cFrame.CopyConvertedFrameDataToArray(cFrameData, ColorImageFormat.Bgra);
// Has 512*424 entries with depth information
ushort[] dFrameData = new ushort[dWidth* dHeight];
dFrame.CopyFrameDataToArray(dFrameData);
Now I would have to map the color-quadruples that live within the ColorFrame-data-array cFrameData to each of the entries of the DepthFrame-data-array dFrameData but that's where I'm stuck. Output should be an array that is 4 times (RGBA/BGRA) the size of the dFrameData array and contains the color information to each pixel of the depth-frame:
// Create the array that contains the color information for every depth-pixel
byte[] dColors = new byte[4 * dFrameData.Length];
for (int i = 0, j = 0; i < cFrameData.Length; ++i)
{
// The mapped color index. ---> I'm stuck here:
int colIx = ?;
dColors[j] = cFrameData[colIx]; // B
dColors[j + 1] = cFrameData[colIx + 1]; // G
dColors[j + 2] = cFrameData[colIx + 2]; // R
dColors[j + 3] = cFrameData[colIx + 3]; // A
j += 4;
}
Does anyone have any suggestions?
I also took a look at the Kinect-SDK's CoordinateMappingBasics example but they did it vice versa for the 1920x1080 pixel-sized image which I already got to work.
Edit
I recognized that I should be able to get the mapped color information by using the ColorSpacePoint-struct which contains the X and Y coordinates to the specific color pixel. Therefore I set up the points like..
// Lookup table for color-point information
ColorSpacePoint[] cSpacePoints = new ColorSpacePoint[dWidth * dHeight];
this.kinectSensor.CoordinateMapper.MapDepthFrameToColorSpace(dFrameData, cSpacePoints);
.. and tried to access the color information like ..
int x = (int)(cSpacePoints[i].X + 0.5f);
int y = (int)(cSpacePoints[i].Y + 0.5f);
int ix = x * cWidth + y;
byte r = cFrameData[ix + 2];
byte g = cFrameData[ix + 1];
byte b = cFrameData[ix];
byte a = cFrameData[ix + 3];
.. but I'm still getting the wrong colors. Mostly white ones.
Well, I figured it out by myself. The error was trivial. As the array is not a pixel-array where one entry contains RGBA information but a byte array where each entry represents either R, G, B or A I had to multiply the index by the bytes-per-pixel value which in this case is 4. So the solution looks like:
int ix = (x * cWidth + y) * 4;
byte r = cFrameData[ix + 2];
byte g = cFrameData[ix + 1];
byte b = cFrameData[ix];
byte a = cFrameData[ix + 3];

Attempted to read or write protected memory - image manipulation

I have this code;
BitmapData bdBackground = Background.LockBits(new Rectangle(0, 0, Background.Width,
Background.Height), ImageLockMode.ReadWrite, Background.PixelFormat);
BitmapData bdForeground = videoFrame.LockBits(new Rectangle(0, 0, videoFrame.Width,
videoFrame.Height), ImageLockMode.ReadWrite, videoFrame.PixelFormat);
unsafe
{
for (int x = 0; x < videoFrame.Width; x++)
{
byte* columnBackground = (byte*)bdBackground.Scan0 + (x * bdBackground.Stride);
byte* columnForeground = (byte*)bdForeground.Scan0 + (x * bdForeground.Stride);
for (int y = 0; y < videoFrame.Height; y++)
{
if (columnBackground[x * pixelSize] == columnForeground[x * pixelSize])
{
columnForeground[x] = 0;
}
}
}
}
Background.UnlockBits(bdBackground);
videoFrame.UnlockBits(bdForeground);
it gives me error;
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
in if (columnBackground[x * pixelSize] == columnForeground[x * pixelSize])
what is the reason for that? I take this code from here
First, you need to understand how an image is stored in an array.
Images "usually in most APIs" are row major, meaning they are stored row by row (usually in a one dimensional array).
To loop through a row major image (walk the pixels), the outer loop is usually from 0 to height, and the inner from 0 to width. This makes the loops easier to read, and increases cache hits.
Stride is a very important concept, it represents the number of bytes needed for each row, and is not necessarily equal to the width*bytes per pixel, as padding for alignment reasons is usually present.
Stride is used to access a new row, for example, if I want to access the third row:
third_Row = 3 * image_stride;
If you want to access the 10th pixel of the third row, you just add (10 * bytes per pixel) to third_Row:
third_Row_Tenth_Pixel = 3 * image_stride + 10 * Bytes_per_pixel
NOTE: please mark the above does not apply to any image where bits per pixel are lower than 8, usually 4, 2, or 1 are used.
What you are doing is the reverse, you are multiplying the column number by the stride, instead of the row number, effectively, stepping out of the range of the image.
In short, reverse the x and y loops, making the y one contain the x one(for reasons of increasing cache hits):
unsafe
{
for (int y = 0; y < videoFrame.Height; y++)
{
byte* columnBackground = (byte*)bdBackground.Scan0 + (y * bdBackground.Stride);
byte* columnForeground = (byte*)bdForeground.Scan0 + (y * bdForeground.Stride);
for (int x = 0; x < videoFrame.Width; x++)
{
if (columnBackground[x * pixelSize] == columnForeground[x * pixelSize])
{
columnForeground[x] = 0;
}
}
}
}
Background.UnlockBits(bdBackground);
videoFrame.UnlockBits(bdForeground);
You never use the y variable when accessing the bitmap array. You should be multiplying y by the Stride instead of x. Then add x * pixelSize like you are doing.

Faster way to fill a 160x43 byte array from colors of Bitmap class

What's the faster way to effectively fill an array of bytes where each byte represents a pixel (black or white: < 125 = black, > 125 = white) from a Bitmap class?
I used this for colored images: Better/faster way to fill a big array in C#
However now I'm looking for something different (I can even use a single color like Red to fill this, it doesn't matter is just something I should choose), because the array format changed.
Any suggestion? Actually I'm using this code, which is obviusly not the best idea
for (int x = 0; x < LgLcd.NativeConstants.LGLCD_BMP_WIDTH; ++x)
{
for (int y = 0; y < LgLcd.NativeConstants.LGLCD_BMP_HEIGHT; ++y)
{
tmp = bmp.GetPixel(x, y);
array[y * LgLcd.NativeConstants.LGLCD_BMP_WIDTH + x] = (byte)((tmp.R == 255 && tmp.G == 255 && tmp.B == 255) ? 0 : 255);
//array[y * x] = (byte)0;
}
}
My idea was parallelizing everything (yea, 1 thread per line maybe? (or per column)), it should help I think.
EDIT:
Ok, first, I need a way to have the possibility to access different bytes of the image at the same time, Brandon Moretz is suggesting maybe the correct way to access bytes with lockbits. I would like to avoid, however, unsafe code. Does Lockbits involves necessarily unsafe code?
Second, my idea of parallelization was to use Parallel.For. This method should use the ThreadPool class, which will use an amount of threads not greater than cores of your cpu, and they are pre-allocated.
This method will be called a lot of times, so I think it's not a big trouble, because the threadpool will be used a lot after first call.
Is what I'm saying correct?
Is using "unsafe" code blocks an option? You can use LockBits on a Bitmap to get it's BitmapData, then use Scan0 & Stride properties to iterate over it.
If it's 255 colors I'm assuming a byte per pixel, so so something like:
*( ( ( byte* )bmpData.Scan0 ) + ( y * bmpData.Stride ) + x ) = (byte)((tmp.R == 255 && tmp.G == 255 && tmp.B == 255) ? 0 : 255);
General approach is to divide the image into regions then process. i.e. you can use:
Thread 1) for (int x = 0; x < LGLCD_BMP_WIDTH /2; ++x) { ... }
Thread 2) for (int x = LGLCD_BMP_WIDTH / 2; x < LGLCD_BMP_WIDTH; ++x) { ... }
where you would have two halves of the image be processed by different threads. You can divide further into 4, 8, etc. pieces as you wish. A thread per line would be excess, as thread creation overhead would overwhelm the benefits by a large margin.
I found the answer by myself, working with lockbits and Marshal.ReadByte with a really nice and fast result:
public void SetPixels(Bitmap image)
{
byte[] array = Pixels;
var data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Parallel.For(0, data.Height, new Action<int>(i =>
{
byte tmp;
int pixel4bpp, pixelPerbpp;
pixelPerbpp = data.Stride / data.Width;
for (pixel4bpp = 0; pixel4bpp < data.Stride; pixel4bpp += pixelPerbpp)
{
tmp = (byte)((
Marshal.ReadByte(data.Scan0, 0 + (data.Stride * i) + pixel4bpp)
+ Marshal.ReadByte(data.Scan0, 1 + (data.Stride * i) + pixel4bpp)
+ Marshal.ReadByte(data.Scan0, 2 + (data.Stride * i) + pixel4bpp)
+ Marshal.ReadByte(data.Scan0, 3 + (data.Stride * i) + pixel4bpp)
) / pixelPerbpp);
array[i * data.Width + (pixel4bpp / pixelPerbpp)] = tmp;
}
}));
image.UnlockBits(data);
}

Inserting bytes in the middle of binary file

I want to add some string in the middle of image metadata block. Under some specific marker. I have to do it on bytes level since .NET has no support for custom metadata fields.
The block is built like 1C 02 XX YY YY ZZ ZZ ZZ ... where XX is the ID of the field I need to append and YY YY is the size of it, ZZ = data.
I imagine it should be more or less possible to read all the image data up to this marker (1C 02 XX) then increase the size bytes (YY YY), add data at the end of ZZ and then add the rest of the original file? Is this correct?
How should I go on with it? It needs to work as fast as possible with 4-5 MB JPEG files.
In general there is no way to speed up this operation. You have to read at least portion that needs to be moved and write it again in updated file. Creating new file and copying content to it may be faster if you can parallelize read and write operations.
Note: In you particular case it may not be possible to just insert content in the middle of the file as most of file formats are not designed with such modifcations in mind. Often there are offsets to portions of the file that will be invalid when you shift part of the file. Specifying what file format you trying to work with may help other people to provide better approaches.
Solved the problem with this code:
List<byte> dataNew = new List<byte>();
byte[] data = File.ReadAllBytes(jpegFilePath);
int j = 0;
for (int i = 1; i < data.Length; i++)
{
if (data[i - 1] == (byte)0x1C) // 1C IPTC
{
if (data[i] == (byte)0x02) // 02 IPTC
{
if (data[i + 1] == (byte)fileByte) // IPTC field_number, i.e. 0x78 = IPTC_120
{
j = i;
break;
}
}
}
}
for (int i = 0; i < j + 2; i++) // add data from file before this field
dataNew.Add(data[i]);
int countOld = (data[j + 2] & 255) << 8 | (data[j + 3] & 255); // curr field length
int countNew = valueToAdd.Length; // new string length
int newfullSize = countOld + countNew; // sum
byte[] newSize = BitConverter.GetBytes((Int16)newfullSize); // Int16 on 2 bytes (to use 2 bytes as size)
Array.Reverse(newSize); // changes order 10 00 to 00 10
for (int i = 0; i < newSize.Length; i++) // add changed size
dataNew.Add(newSize[i]);
for (int i = j + 4; i < j + 4 + countOld; i++) // add old field value
dataNew.Add(data[i]);
byte[] newString = ASCIIEncoding.ASCII.GetBytes(valueToAdd);
for (int i = 0; i < newString.Length; i++) // append with new field value
dataNew.Add(newString[i]);
for (int i = j + 4 + newfullSize; i < data.Length; i++) // add rest of the file
dataNew.Add(data[i]);
byte[] finalArray = dataNew.ToArray();
File.WriteAllBytes(Path.Combine(Path.GetDirectoryName(jpegFilePath), "newfile.jpg"), finalArray);
Here is an easy and quite fast solution. It moves all bytes after given offset to their new position according to given extraBytes, so you can insert your data.
public void ExpandFile(FileStream stream, long offset, int extraBytes)
{
// http://stackoverflow.com/questions/3033771/file-io-with-streams-best-memory-buffer-size
const int SIZE = 4096;
var buffer = new byte[SIZE];
var length = stream.Length;
// Expand file
stream.SetLength(length + extraBytes);
var pos = length;
int to_read;
while (pos > offset)
{
to_read = pos - SIZE >= offset ? SIZE : (int)(pos - offset);
pos -= to_read;
stream.Position = pos;
stream.Read(buffer, 0, to_read);
stream.Position = pos + extraBytes;
stream.Write(buffer, 0, to_read);
}
Need to be checked, though...

Categories