C# - Fastest way of Interpolating a large byte array (RGB to RGBA) - c#

I am uploading frames from a camera to a texture on the GPU for processing (using SharpDX). My issue is ATM is that I have the frames coming in as 24bit RGB, but DX11 no longer has the 24bit RGB texture format, only 32bit RGBA. After each 3 bytes I need to add another byte with the value of 255 (no transparency). I've tried this method of iterating thru the byte array to add it but it's too expensive. Using GDI bitmaps to convert is also very expensive.
int count = 0;
for (int i = 0; i < frameDataBGRA.Length - 3; i+=4)
{
frameDataBGRA[i] = frameData[i - count];
frameDataBGRA[i + 1] = frameData[(i + 1) - count];
frameDataBGRA[i + 2] = frameData[(i + 2) - count];
frameDataBGRA[i + 3] = 255;
count++;
}

Assuming you can compile with unsafe, using pointers in that case will give you significant boost.
First create two structs to hold data in a packed way:
[StructLayout(LayoutKind.Sequential)]
public struct RGBA
{
public byte r;
public byte g;
public byte b;
public byte a;
}
[StructLayout(LayoutKind.Sequential)]
public struct RGB
{
public byte r;
public byte g;
public byte b;
}
First version :
static void Process_Pointer_PerChannel(int pixelCount, byte[] rgbData, byte[] rgbaData)
{
fixed (byte* rgbPtr = &rgbData[0])
{
fixed (byte* rgbaPtr = &rgbaData[0])
{
RGB* rgb = (RGB*)rgbPtr;
RGBA* rgba = (RGBA*)rgbaPtr;
for (int i = 0; i < pixelCount; i++)
{
rgba->r = rgb->r;
rgba->g = rgb->g;
rgba->b = rgb->b;
rgba->a = 255;
rgb++;
rgba++;
}
}
}
}
This avoids a lot of indexing, and passes data directly.
Another version which is slightly faster, to box directly:
static void Process_Pointer_Cast(int pixelCount, byte[] rgbData, byte[] rgbaData)
{
fixed (byte* rgbPtr = &rgbData[0])
{
fixed (byte* rgbaPtr = &rgbaData[0])
{
RGB* rgb = (RGB*)rgbPtr;
RGBA* rgba = (RGBA*)rgbaPtr;
for (int i = 0; i < pixelCount; i++)
{
RGB* cp = (RGB*)rgba;
*cp = *rgb;
rgba->a = 255;
rgb++;
rgba++;
}
}
}
}
One small extra optimization (which is marginal), if you keep the same array all the time and reuse it, you can initialize it once with alpha set to 255 eg :
static void InitRGBA_Alpha(int pixelCount, byte[] rgbaData)
{
for (int i = 0; i < pixelCount; i++)
{
rgbaData[i * 4 + 3] = 255;
}
}
Then as you will never change this channel, other functions do not need to write into it anymore:
static void Process_Pointer_Cast_NoAlpha (int pixelCount, byte[] rgbData, byte[] rgbaData)
{
fixed (byte* rgbPtr = &rgbData[0])
{
fixed (byte* rgbaPtr = &rgbaData[0])
{
RGB* rgb = (RGB*)rgbPtr;
RGBA* rgba = (RGBA*)rgbaPtr;
for (int i = 0; i < pixelCount; i++)
{
RGB* cp = (RGB*)rgba;
*cp = *rgb;
rgb++;
rgba++;
}
}
}
}
In my test (running a 1920*1080 image, 100 iterations), I get (i7, x64 release build, average running time)
Your version : 6.81ms
Process_Pointer_PerChannel : 4.3ms
Process_Pointer_Cast : 3.8ms
Process_Pointer_Cast_NoAlpha : 3.5ms
Please note that of course all those functions can as well be easily chunked and parts run in multi threaded versions.
If you need higher performance, you have two options ( a bit out of scope from the question)
upload your image in a byte address buffer (as rgb), and perform the conversion to texture in a compute shader. That involves some bit shifting and a bit of fiddling with formats, but is reasonably straightforward to achieve.
Generally camera images come in Yuv format (with u and v downsampled), so it's mush faster to upload image in that color space and perform conversion to rgba either in pixel shader or compute shader. If your camera sdk allows to get pixel data in that native format, that's the way to go.

#catflier: good work, but it can go a little faster. ;-)
Reproduced times on my hardware:
Base version: 5.48ms
Process_Pointer_PerChannel: 2.84ms
Process_Pointer_Cast: 2.16ms
Process_Pointer_Cast_NoAlpha: 1.60ms
My experiments:
FastConvert: 1.45ms
FastConvert4: 1.13ms (here: count of pixels must be divisible by 4, but is usually no problem)
Things that have improved speed:
your RGB structure must always read 3 single bytes per pixel, but it is faster to read a whole uint (4 bytes) and simply ignore the last byte
the alpha value can then be added directly to a uint bit calculation
modern processors can often address fixed pointers with offset positions faster than pointers that are incremented themselves.
the offset variables in x64 mode should also directly use a 64-bit data value (long instead of int), which reduces the overhead of the accesses
the partial rolling out of the inner loop increases some performance again
The Code:
static void FastConvert(int pixelCount, byte[] rgbData, byte[] rgbaData)
{
fixed (byte* rgbP = &rgbData[0], rgbaP = &rgbaData[0])
{
for (long i = 0, offsetRgb = 0; i < pixelCount; i++, offsetRgb += 3)
{
((uint*)rgbaP)[i] = *(uint*)(rgbP + offsetRgb) | 0xff000000;
}
}
}
static void FastConvert4Loop(long pixelCount, byte* rgbP, byte* rgbaP)
{
for (long i = 0, offsetRgb = 0; i < pixelCount; i += 4, offsetRgb += 12)
{
uint c1 = *(uint*)(rgbP + offsetRgb);
uint c2 = *(uint*)(rgbP + offsetRgb + 3);
uint c3 = *(uint*)(rgbP + offsetRgb + 6);
uint c4 = *(uint*)(rgbP + offsetRgb + 9);
((uint*)rgbaP)[i] = c1 | 0xff000000;
((uint*)rgbaP)[i + 1] = c2 | 0xff000000;
((uint*)rgbaP)[i + 2] = c3 | 0xff000000;
((uint*)rgbaP)[i + 3] = c4 | 0xff000000;
}
}
static void FastConvert4(int pixelCount, byte[] rgbData, byte[] rgbaData)
{
if ((pixelCount & 3) != 0) throw new ArgumentException();
fixed (byte* rgbP = &rgbData[0], rgbaP = &rgbaData[0])
{
FastConvert4Loop(pixelCount, rgbP, rgbaP);
}
}

Related

Mixing 16-bit stereo PCM audio

I'm trying to create a 16-bit PCM version of NAudio's MixingWaveProvider32 that operates on 16-bit PCM samples instead of 32-bit floats.
Each 16-bit stereo sample is packed in a byte array like so...
Byte 0
Byte 1
Byte 2
Byte 3
Channel 1 (Left) Lo
Channel 1 Hi
Channel 2 (Right) Lo
Channel 2 Hi
The two bytes per channel are interpreted as signed integers, so the minimum value is short.MinValue, the max is short.MaxValue. I don't think you can simply add the byte values to each other.
I've written some very long-handed code (see below) but I am convinced there is a more performant way of doing this.
I'd be really grateful for any help :-)
static void Main(string[] args)
{
// setup some input data
byte[] b1 = { 0x1, 0x0, 0x2, 0x0, 0x3, 0x0, 0x4, 0x0 };
byte[] b2 = new byte[b1.Length];
Array.Copy(b1, b2, b1.Length);
byte[] result = new byte[b1.Length];
Console.WriteLine("b1");
b1.DumpPcm();
Console.WriteLine();
Console.WriteLine("b2");
b2.DumpPcm();
for (int i = 0; i < b1.Length; i += 4)
{
short l1 = BitConverter.ToInt16(b1, i);
short r1 = BitConverter.ToInt16(b1, i + 2);
short l2 = BitConverter.ToInt16(b2, i);
short r2 = BitConverter.ToInt16(b2, i + 2);
byte[] resl = BitConverter.GetBytes(l1 + l2);
byte[] resr = BitConverter.GetBytes(r1 + r2);
result[i] = resl[0];
result[i + 1] = resl[1];
result[i + 2] = resr[0];
result[i + 3] = resr[1];
}
Console.WriteLine();
Console.WriteLine("Result...");
result.DumpPcm();
Console.ReadLine();
}
You could always use unsafe code, this should be significantly faster since you save a bunch of method calls and object allocations:
// setup some input data
byte[] b1 = {0x1, 0x0, 0x2, 0x0, 0x3, 0x0, 0x4, 0x0};
byte[] b2 = new byte[b1.Length];
Array.Copy(b1, b2, b1.Length);
byte[] result = new byte[b1.Length];
fixed (byte* b1Ptr = b1)
{
fixed (byte* b2Ptr = b2)
{
fixed (byte* rPtr = result)
{
var s1Ptr = (short*) b1Ptr;
var s2Ptr = (short*) b2Ptr;
var srPtr = (short*) rPtr;
var length = b1.Length / 2;
for (int i = 0; i < length; i++)
{
var v = s1Ptr[i] + s2Ptr[i];
srPtr[i] = (short) v;
Console.WriteLine($"{s1Ptr[i]} + {s2Ptr[i]} -> {srPtr[i]}");
}
}
}
}
Note that summing values might cause overflow. You should probably either average the two samples, or clamp the result to avoid this.

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;
}
}
}

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;
}

Store float as bytes into large array instead of new byte[]

I need to serialize a bunch of floats and convert to little endian if necessary. I'm aware of BitConverter.GetBytes(float), but I'd rather avoid allocating a ton of little 4-byte arrays on the GC heap. How can I do the conversion into an existing large byte[] array with an offset index? I want something like:
float[] theFloats; // filled up somewhere
byte[] theBytes = new byte[theFloats.Length * 4];
int offset = 0;
for (int i = 0; i < numFloats; ++i)
{
MagicClass.CopyFloatToBytes(theFloats[i], theBytes, offset);
offset += 4;
}
You can create a MemoryStream around the array, then create a BinaryWriter and write floats to it.
Why not just use BitConverter.GetBytes?
You can also do this with [StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Explicit)]
public struct Convert32BitType
{
[FieldOffset(0)]
public int Int32Value;
[FieldOffset(0)]
public float FloatValue;
}
// Example:
var tmp = new Convert32BitType();
tmp.FloatValue = 1.1;
int ival = tmp.Int32Value;
byte b1 = (byte)(ival >> 24);
byte b2 = (byte)(ival >> 16);
byte b3 = (byte)(ival >> 8);
byte b4 = (byte)(ival >> 0);
Another possibility is to used the fixed keyword and cast the pointer, but that requires unsafe code.

Why do I get the following output when inverting bits in a byte?

Assumption:
Converting a
byte[] from Little Endian to Big
Endian means inverting the order of the bits in
each byte of the byte[].
Assuming this is correct, I tried the following to understand this:
byte[] data = new byte[] { 1, 2, 3, 4, 5, 15, 24 };
byte[] inverted = ToBig(data);
var little = new BitArray(data);
var big = new BitArray(inverted);
int i = 1;
foreach (bool b in little)
{
Console.Write(b ? "1" : "0");
if (i == 8)
{
i = 0;
Console.Write(" ");
}
i++;
}
Console.WriteLine();
i = 1;
foreach (bool b in big)
{
Console.Write(b ? "1" : "0");
if (i == 8)
{
i = 0;
Console.Write(" ");
}
i++;
}
Console.WriteLine();
Console.WriteLine(BitConverter.ToString(data));
Console.WriteLine(BitConverter.ToString(ToBig(data)));
foreach (byte b in data)
{
Console.Write("{0} ", b);
}
Console.WriteLine();
foreach (byte b in inverted)
{
Console.Write("{0} ", b);
}
The convert method:
private static byte[] ToBig(byte[] data)
{
byte[] inverted = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
var bits = new BitArray(new byte[] { data[i] });
var invertedBits = new BitArray(bits.Count);
int x = 0;
for (int p = bits.Count - 1; p >= 0; p--)
{
invertedBits[x] = bits[p];
x++;
}
invertedBits.CopyTo(inverted, i);
}
return inverted;
}
The output of this little application is different from what I expected:
00000001 00000010 00000011 00000100 00000101 00001111 00011000
00000001 00000010 00000011 00000100 00000101 00001111 00011000
80-40-C0-20-A0-F0-18
01-02-03-04-05-0F-18
1 2 3 4 5 15 24
1 2 3 4 5 15 24
For some reason the data remains the same, unless printed using BitConverter.
What am I not understanding?
Update
New code produces the following output:
10000000 01000000 11000000 00100000 10100000 11110000 00011000
00000001 00000010 00000011 00000100 00000101 00001111 00011000
01-02-03-04-05-0F-18
80-40-C0-20-A0-F0-18
1 2 3 4 5 15 24
128 64 192 32 160 240 24
But as I have been told now, my method is incorrect anyway because I should invert the bytes
and not the bits?
This hardware developer I'm working with told me to invert the bits because he cannot read the data.
Context where I'm using this
The application that will use this does not really work with numbers.
I'm supposed to save a stream of bits to file where
1 = white and 0 = black.
They represent pixels of a bitmap 256x64.
byte 0 to byte 31 represents the first row of pixels
byte 32 to byte 63 the second row of pixels.
I have code that outputs these bits... but the developer is telling
me they are in the wrong order... He says the bytes are fine but the bits are not.
So I'm left confused :p
No. Endianness refers to the order of bytes, not bits. Big endian systems store the most-significant byte first and little-endian systems store the least-significant first. The bits within a byte remain in the same order.
Your ToBig() function is returning the original data rather than the bit-swapped data, it seems.
Your method may be correct at this point. There are different meanings of endianness, and it depends on the hardware.
Typically, it's used for converting between computing platforms. Most CPU vendors (now) use the same bit ordering, but different byte ordering, for different chipsets. This means, that, if you are passing a 2-byte int from one system to another, you leave the bits alone, but swap bytes 1 and 2, ie:
int somenumber -> byte[2]: somenumber[high],somenumber[low] ->
byte[2]: somenumber[low],somenumber[high] -> int newNumber
However, this isn't always true. Some hardware still uses inverted BIT ordering, so what you have may be correct. You'll need to either trust your hardware dev. or look into it further.
I recommend reading up on this on Wikipedia - always a great source of info:
http://en.wikipedia.org/wiki/Endianness
Your ToBig method has a bug.
At the end:
invertedBits.CopyTo(data, i);
}
return data;
You need to change that to:
byte[] newData = new byte[data.Length];
invertedBits.CopyTo(newData, i);
}
return newData;
You're resetting your input data, so you're receiving both arrays inverted. The problem is that arrays are reference types, so you can modify the original data.
As greyfade already said, endianness is not about bit ordering.
The reason that your code doesn't do what you expect, is that the ToBig method changes the array that you send to it. That means that after calling the method the array is inverted, and data and inverted are just two references pointing to the same array.
Here's a corrected version of the method.
private static byte[] ToBig(byte[] data) {
byte[] result = new byte[data.length];
for (int i = 0; i < data.Length; i++) {
var bits = new BitArray(new byte[] { data[i] });
var invertedBits = new BitArray(bits.Count);
int x = 0;
for (int p = bits.Count - 1; p >= 0; p--) {
invertedBits[x] = bits[p];
x++;
}
invertedBits.CopyTo(result, i);
}
return result;
}
Edit:
Here's a method that changes endianness for a byte array:
static byte[] ConvertEndianness(byte[] data, int wordSize) {
if (data.Length % wordSize != 0) throw new ArgumentException("The data length does not divide into an even number of words.");
byte[] result = new byte[data.Length];
int offset = wordSize - 1;
for (int i = 0; i < data.Length; i++) {
result[i + offset] = data[i];
offset -= 2;
if (offset < -wordSize) {
offset += wordSize * 2;
}
}
return result;
}
Example:
byte[] data = { 1,2,3,4,5,6 };
byte[] inverted = ConvertEndianness(data, 2);
Console.WriteLine(BitConverter.ToString(inverted));
Output:
02-01-04-03-06-05
The second parameter is the word size. As endianness is the ordering of bytes in a word, you have to specify how large the words are.
Edit 2:
Here is a more efficient method for reversing the bits:
static byte[] ReverseBits(byte[] data) {
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++) {
int b = data[i];
int r = 0;
for (int j = 0; j < 8; j++) {
r <<= 1;
r |= b & 1;
b >>= 1;
}
result[i] = (byte)r;
}
return result;
}
One big problem I see is ToBig changes the contents of the data[] array that is passed to it.
You're calling ToBig on an array named data, then assigning the result to inverted, but since you didn't create a new array inside ToBig, you modified both arrays, then you proceed to treat the arrays data and inverted as different when in reality they are not.

Categories

Resources