converting byte array to Mat - c#

I have a CCD driver which returns IntPtr to me. I used Marshal.Copy to byte array (bytearray_Image), each element inside bytearray_Image stores 8bit R/G/B value which the sequence is byte[0] = R value, byte[1] = G value, byte[2] = B value...and so on. I have successfully converted to 3 Channels Mat using below code snippet:
var src = new Mat(rows: nHeight, cols: nWidth, type: MatType.CV_8UC3);
var indexer = src.GetGenericIndexer();
int x = 0;
int y = 0;
for (int z = 0; z < (bytearray_Image.Length - 3); z += 3)
{
byte blue = bytearray_Image[(z + 2)];
byte green = bytearray_Image[(z + 1)];
byte red = bytearray_Image[(z + 0)];
Vec3b newValue = new Vec3b(blue, green, red);
indexer[y, x] = newValue;
x += 1;
if (x == nWidth)
{
x = 0;
y += 1;
}
}
Since the image is very large, this method seems to be too slow to convert the image. Is there any ways to do such conversion efficiently?

This code works for me:
var image = new Mat(nHeight, nWidth, MatType.CV_8UC3);
int length = nHeight * nWidth * 3; // or image.Height * image.Step;
Marshal.Copy(bytearray_Image, 0, image.ImageData, length);
But it will work for you only if byte[] data's step length is equal to the Mat's

Related

bitmap min and max values in c#

I have a bitmap object in C# which is created as follows:
Bitmap bmp = new Bitmap(_currentImage.Width, _currentImage.Height, PixelFormat.Format48bppRgb);
The bitmap gets filled by a third party function call and it is loaded with the correct image.
Now, I want to do some simple image statistics on it. Is there a convenient way to query the minimum and maximum value in the image, say in the RED channel.
Here's a simple version that reads all the 48bpp pixels and does something with Red as an example (not tested)
unsafe static ushort MaxRed(Bitmap bm)
{
var bd = bm.LockBits(new Rectangle(Point.Empty, bm.Size), ImageLockMode.ReadOnly, PixelFormat.Format48bppRgb);
ushort maxRed = 0;
for (int y = 0; y < bm.Height; y++)
{
ushort* ptr = (ushort*)(bd.Scan0 + y * bd.Stride);
for (int x = 0; x < bm.Width; x++)
{
ushort b = *ptr++;
ushort g = *ptr++;
ushort r = *ptr++;
maxRed = Math.Max(maxRed, r);
}
}
bm.UnlockBits(bd);
return maxRed;
}
Unsafe because it's easier than using Marshal, but you can convert it to that, for example using ReadInt16(IntPtr, Int32) or by copying the whole image into an array (which of course doubles the space requirements).
As pointed out by #harold the image format you are using prevents you from using GetPixel as this method returns a Color which internaly stores its rgb values as byte's. And as you are using an image with 48 bits per pixel (16 bit = 2 byte per color) a byte is to small.
So you need to work with the LockBits method which returns an BitmapData object. The property Scan0 of this return object represents a pointer to the first byte in the data of the locked range.
I came up with the following method to get the maximum r value. It will work with the two specified formats in the PixelFormats property and more formats can easily be added.
public class PixelFormatData
{
// example => rgb are three values,
// => argb are four values
public int ValueCount { get; set; }
public int BitsPerPixel { get; set; }
public PixelFormatData(int valueCount, int bitsPerPixel)
{
ValueCount = valueCount;
BitsPerPixel = bitsPerPixel;
}
}
public static readonly Dictionary<PixelFormat, PixelFormatData> PixelFormats = new Dictionary<PixelFormat, PixelFormatData>
{
{ PixelFormat.Format24bppRgb, new PixelFormatData(3, 24) },
{ PixelFormat.Format48bppRgb, new PixelFormatData(3, 48) }
};
public static IEnumerable<byte[]> GetBytes(Bitmap image, int bytesPerPixel)
{
var imageData = image.LockBits(new Rectangle(Point.Empty, image.Size), ImageLockMode.ReadOnly, image.PixelFormat);
var ptr = imageData.Scan0;
var imageSize = image.Width * image.Height;
for (int x = 0; x < imageSize; x++)
{
yield return ptr.CopyAndMove(bytesPerPixel);
}
image.UnlockBits(imageData);
}
public static IEnumerable<int> GetValues(Bitmap image, int valueIndex)
{
if (!PixelFormats.ContainsKey(image.PixelFormat))
throw new ArgumentException(nameof(image.PixelFormat));
var pixelFormatData = PixelFormats[image.PixelFormat];
if (valueIndex < 0 || valueIndex >= pixelFormatData.ValueCount)
throw new ArgumentException(nameof(valueIndex));
int bytesPerPixel = pixelFormatData.BitsPerPixel / 8,
bytesPerValue = bytesPerPixel / pixelFormatData.ValueCount;
return GetBytes(image, bytesPerPixel)
.Select(bytes =>
bytes.Skip(bytesPerValue * valueIndex)
.Take(bytesPerValue)
.RightPad(4))
.Select(valueData => BitConverter.ToInt32(valueData.ToArray(), 0));
}
Those two extension methods are required to use the code.
public static class EnumerableExtensions
{
public static List<T> RightPad<T>(this IEnumerable<T> collection, int total)
{
var list = collection.ToList();
while (list.Count < 8)
list.Add(default(T));
return list;
}
}
public static class IntPtrExtensions
{
public static byte[] CopyAndMove(this IntPtr ptr, int count)
{
byte[] bytes = new byte[count];
Marshal.Copy(ptr, bytes, 0, count);
ptr += count;
return bytes;
}
}
And this is how it is used.
using (var file = new FileStream(#"C:\mypath\myPicture.png", FileMode.Open))
{
Bitmap image = new Bitmap(file);
// the color is saved in the followig format (gbr) so the
// red color is index 2
Console.WriteLine(GetValues(image, 2).Max());
}
I've tested it with an Format24bppRgb image.
If the bits per pixel are 8 and bellow you can also use GetPixel to check for every pixel. It is just about 3 times slower then the method above.
byte highestRed = 0;
using (var file = new FileStream(#"C:\mypath\mypicture.jpg", FileMode.Open))
{
Bitmap image = new Bitmap(file);
for (int x = 0; x < image.Width; x++)
{
for (int y = 0; y < image.Height; y++)
{
var color = image.GetPixel(x, y);
if(highestRed < color.R)
highestRed = color.R;
}
}
}

Convert RGB array to image in C#

I know the rgb value of every pixel, and how can I create the picture by these values in C#? I've seen some examples like this:
public Bitmap GetDataPicture(int w, int h, byte[] data)
{
Bitmap pic = new Bitmap(this.width, this.height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Color c;
for (int i = 0; i < data.length; i++)
{
c = Color.FromArgb(data[i]);
pic.SetPixel(i%w, i/w, c);
}
return pic;
}
But it does not works.
I have a two-dimensional array like this:
1 3 1 2 4 1 3 ...2 3 4 2 4 1 3 ...4 3 1 2 4 1 3 ......
Each number correspond to a rgb value, for example, 1 => {244,166,89}
2=>{54,68,125}.
I'd try the following code, which uses an array of 256 Color entries for the palette (you have to create and fill this in advance):
public Bitmap GetDataPicture(int w, int h, byte[] data)
{
Bitmap pic = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
int arrayIndex = y * w + x;
Color c = Color.FromArgb(
data[arrayIndex],
data[arrayIndex + 1],
data[arrayIndex + 2],
data[arrayIndex + 3]
);
pic.SetPixel(x, y, c);
}
}
return pic;
}
I tend to iterate over the pixels, not the array, as I find it easier to read the double loop than the single loop and the modulo/division operation.
Your solution very near to working code. Just you need the "palette" - i.e. set of 3-elements byte array, where each 3-bytes element contains {R, G, B} values.
//palette is a 256x3 table
public static Bitmap GetPictureFromData(int w, int h, byte[] data, byte[][] palette)
{
Bitmap pic = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Color c;
for (int i = 0; i < data.Length; i++)
{
byte[] color_bytes = palette[data[i]];
c = Color.FromArgb(color_bytes[0], color_bytes[1], color_bytes[2]);
pic.SetPixel(i % w, i / w, c);
}
return pic;
}
This code works for me, but it very slow.
If you create in-memory "image" of BMP-file and then use Image.FromStream(MemoryStream("image")), it code will be more faster, but it more complex solution.

32-bit Grayscale Tiff with floating point pixel values to array using LibTIFF.NET C#

I just started using LibTIFF.NET in my c# application to read Tiff images as heightmaps obtained from ArcGIS servers. All I need is to populate an array with image's pixel values for terrain generation based on smooth gradients. The image is a LZW compressed 32-bit Grayscale Tiff with floating point pixel values representing elevaion in meters.
It's been some days now that I struggle to return right values but all I get is just "0" values assuming it's a total black or white image!
Here's the code so far: (Updated - Read Update 1)
using (Tiff inputImage = Tiff.Open(fileName, "r"))
{
int width = inputImage.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
int height = inputImage.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
int bytesPerPixel = 4;
int count = (int)inputImage.RawTileSize(0); //Has to be: "width * height * bytesPerPixel" ?
int resolution = (int)Math.Sqrt(count);
byte[] inputImageData = new byte[count]; //Has to be: byte[] inputImageData = new byte[width * height * bytesPerPixel];
int offset = 0;
for (int i = 0; i < inputImage.NumberOfTiles(); i++)
{
offset += inputImage.ReadEncodedTile(i, inputImageData, offset, (int)inputImage.RawTileSize(i));
}
float[,] outputImageData = new float[resolution, resolution]; //Has to be: float[,] outputImageData = new float[width * height];
int length = inputImageData.Length;
Buffer.BlockCopy(inputImageData, 0, outputImageData, 0, length);
using (StreamWriter sr = new StreamWriter(fileName.Replace(".tif", ".txt"))) {
string row = "";
for(int i = 0; i < resolution; i++) { //Change "resolution" to "width" in order to have correct array size
for(int j = 0; j < resolution; j++) { //Change "resolution" to "height" in order to have correct array size
row += outputImageData[i, j] + " ";
}
sr.Write(row.Remove(row.Length - 1) + Environment.NewLine);
row = "";
}
}
}
Sample Files & Results: http://terraunity.com/SampleElevationTiff_Results.zip
Already searched everywhere on internet and couldn't find the solution for this specific issue. So I really appreciate the help which makes it useful for others too.
Update 1:
Changed the code based on Antti Leppänen's answer but got weird results which seems to be a bug or am I missing something? Please see uploaded zip file to see the results with new 32x32 tiff images here:
http://terraunity.com/SampleElevationTiff_Results.zip
Results:
LZW Compressed: RawStripSize = ArraySize = 3081 = 55x55 grid
Unompressed: RawStripSize = ArraySize = 65536 = 256x256 grid
Has to be: RawStripSize = ArraySize = 4096 = 32x32 grid
As you see the results, LibTIFF skips some rows and gives irrelevant orderings and it even gets worse if the image size is not power of 2!
Your example file seems to be tiled tiff and not stripped. Console says:
ElevationMap.tif: Can not read scanlines from a tiled image
I changed your code to read tiles. This way it seems to be reading data.
for (int i = 0; i < inputImage.NumberOfTiles(); i++)
{
offset += inputImage.ReadEncodedTile(i, inputImageData, offset, (int)inputImage.RawTileSize(i));
}
I know it could be late, but I had the same mistake recently and I found the solution, so it could be helpful. The mistake is in the parameter count of the function Tiff.ReadEncodedTile(tile, buffer, offset, count). It must be the decompressed bytes size, not the compressed bytes size. That's the reason why you have not all the information, because you are not saving the whole data in your buffer. See how-to-translate-tiff-readencodedtile-to-elevation-terrain-matrix-from-height.
A fast method to read a floating point tiff.
public static unsafe float[,] ReadTiff(Tiff image)
{
const int pixelStride = 4; // bytes per pixel
int imageWidth = image.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
int imageHeight = image.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
float[,] result = new float[imageWidth, imageHeight];
int tileCount = image.NumberOfTiles();
int tileWidth = image.GetField(TiffTag.TILEWIDTH)[0].ToInt();
int tileHeight = image.GetField(TiffTag.TILELENGTH)[0].ToInt();
int tileStride = (imageWidth + tileWidth - 1) / tileWidth;
int bufferSize = tileWidth * tileHeight * pixelStride;
byte[] buffer = new byte[bufferSize];
fixed (byte* bufferPtr = buffer)
{
float* array = (float*)bufferPtr;
for (int t = 0; t < tileCount; t++)
{
image.ReadEncodedTile(t, buffer, 0, buffer.Length);
int x = tileWidth * (t % tileStride);
int y = tileHeight * (t / tileStride);
var copyWidth = Math.Min(tileWidth, imageWidth - x);
var copyHeight = Math.Min(tileHeight, imageHeight - y);
for (int j = 0; j < copyHeight; j++)
{
for (int i = 0; i < copyWidth; i++)
{
result[x + i, y + j] = array[j * tileWidth + i];
}
}
}
}
return result;
}

how to I deal with NaN results from FFT?

I am trying to implement a function which takes an wav file, runs a 100th of a second worth of audio through the FFT by AForge. When I change the offset to alter where in the audio I am computing through the FFT, sometimes I will get results in which I can show in my graph but most of the time I get a complex array of NaN's. Why could this be?
Here is my code.
public double[] test()
{
OpenFileDialog file = new OpenFileDialog();
file.ShowDialog();
WaveFileReader reader = new WaveFileReader(file.FileName);
byte[] data = new byte[reader.Length];
reader.Read(data, 0, data.Length);
samepleRate = reader.WaveFormat.SampleRate;
bitDepth = reader.WaveFormat.BitsPerSample;
channels = reader.WaveFormat.Channels;
Console.WriteLine("audio has " + channels + " channels, a sample rate of " + samepleRate + " and bitdepth of " + bitDepth + ".");
float[] floats = new float[data.Length / sizeof(float)];
Buffer.BlockCopy(data, 0, floats, 0, data.Length);
size = 2048;
int inputSamples = samepleRate / 100;
int offset = samepleRate * 15 * channels;
int y = 0;
Complex[] complexData = new Complex[size];
float[] window = CalcWindowFunction(inputSamples);
for (int i = 0; i < inputSamples; i++)
{
complexData[y] = new Complex(floats[i * channels + offset] * window[i], 0);
y++;
}
while (y < size)
{
complexData[y] = new Complex(0, 0);
y++;
}
FourierTransform.FFT(complexData, FourierTransform.Direction.Forward);
double[] arr = new double[complexData.Length];
for (int i = 0; i < complexData.Length; i++)
{
arr[i] = complexData[i].Magnitude;
}
Console.Write("complete, ");
return arr;
}
private float[] CalcWindowFunction(int inputSamples)
{
float[] arr = new float[size];
for(int i =0; i<size;i++){
arr[i] = 1;
}
return arr;
}
A complex array of NaNs is usually the result of one of the inputs to the FFT being a NaN. To debug, you might check all the values in the input array before the FFT to make sure they are within some valid range, given the audio input scaling.

Moving through each pixel for 1bpp Pixel Format

I had a question related to the use of Lockbits method in C#..
I have a 1bpp Image and I'm trying to access all the pixels of the image but some are still left out.
public Bitmap Pixels(Bitmap original)
{
Rectangle rect = new Rectangle(0, 0, original.Width, original.Height);
BitmapData bimgData = original.LockBits(rect, ImageLockMode.ReadWrite, original.PixelFormat);
IntPtr ptr = bimgData.Scan0;
int bytes = bimgData.Stride * bimg.Height;
byte[] Values = new byte[bytes];
Marshal.Copy(ptr, Values, 0, bytes);
int Val;
int stride = bimgData.Stride;
for (int column = 0; column < bimgData.Height; column = column + 1)
for (int row = 0; row < bimgData.Width; row = row +1)
{
c = column;
r = row;
for (int t = 0; t < 8; t++)
{
Val = Values[((c) * stride) + ((r) / 8)] & 2 ^ t;
if (Val == 0)
Values[((c) * stride) + ((r) / 8)] = (byte)(Values[((c) * stride) + ((r) / 8)] + 2 ^ t);
}
}
Marshal.Copy(Values, 0, ptr, bytes);
original.UnlockBits(bimgData);
return original;
}
This code should turn all the pixels white
Val = Values[((c) * stride) + ((r) / 8)] & 2 ^ t;
2 ^ t doesn't do what you hope it does, that's Visual Basic syntax. In the C# language, ^ is the XOR operator. Use the << operator instead and use parentheses to take care of operator precedence. Fix:
Val = Values[((c) * stride) + ((r) / 8)] & (1 << t);
And fix it again when you set the bit.
Do note that turning the entire image to White doesn't require this kind of code at all. Just set all the bytes to 0xff.

Categories

Resources