Reputation: 7324
I have a winform desktop C# application.
I have an image of size 720x576.
The image is represented by a 3 dimensional byte array: 720x576x3.
The x3 is representing the 3 colour channels of Red, Green and Blue.
This Image is divided logically (for my purpose) into 4 regions like so:
Say I have a new byte array for Region 4 and I wish to update this 'Base' Array.
I could do it like this:
for (Int16 camIndex = 0; camIndex < 4; camIndex++)
{
if (camsChanged[camIndex])
{
Rectangle roi = Rectangle.Empty;
switch (camIndex)
{
case 0:
roi = new Rectangle(0, 0, 360, 288);
break;
case 1:
roi = new Rectangle(360, 0, 360, 288);
break;
case 2:
roi = new Rectangle(0, 288, 360, 288);
break;
case 3:
roi = new Rectangle(360, 288, 360, 288);
break;
}
for (int x = roi.X; x < roi.X + roi.Width; x++)
{
for (int y = roi.Y; y < roi.Y + roi.Height; y++)
{
BaseImage[x, y, 0] = NewBaseImage[x, y, 0]; //signifies Red Channel
BaseImage[x, y, 1] = NewBaseImage[x, y, 1]; //signifies Green Channel
BaseImage[x, y, 2] = NewBaseImage[x, y, 2]; //signifies Yellow Channel
}
}
}
}
But is there a quicker way? I looked at Buffer.BlockCopy and Array.Copy but I could not see how it could help me in this scenario as it looked to replace the entire 'Base' array?
Thanks
Upvotes: 4
Views: 350
Reputation: 148
Array.Copy eliminates the need for bounds checks on every index call (thus runs much faster). The big wrinkle here is that you're dealing with a multidimensional array. Because you're concerned with speed you should flatten to a single dimensional array. This SO question covers both flattening and indexing (How to "flatten" or "index" 3D-array in 1D array?)
I personally don't have much experience using BlockCopy but Array.Copy yields much better performance than copying each item individually.
EDIT
It occurred to me that you might actually be able to call Array.Copy on a multidimensional array but you have to pass the calculated index values. Unfortunately, you're four regions aren't arranged in contiguous blocks. Take a look at this simpler (2-d) example to illustrate the problem:
int[,] a = new int[4,4];
int quadrent = 1;
for (int x = 0; x < 4; x++)
{
for (int y = 0; y < 4; y++)
{
if (x < 2)
{
if (y < 2)
{
quadrent = 1;
}
else
{
quadrent = 2;
}
}
else
{
if (y < 2)
{
quadrent = 3;
}
else
{
quadrent = 4;
}
}
a[x, y] = quadrent;
}
}
int i, j;
/* output each array element's value */
for (i = 0; i < 4; i++)
{
Console.WriteLine(String.Format("{0}, {1}, {2}, {3}", a[i, 0], a[i, 1], a[i, 2], a[i, 3]));
}
//split the line;
Console.WriteLine();
Console.WriteLine();
Array.Copy(a, 0, a, 11, 4);
for (i = 0; i < 4; i++)
{
Console.WriteLine(String.Format("{0}, {1}, {2}, {3}", a[i, 0], a[i, 1], a[i, 2], a[i, 3]));
}
Console.ReadKey();
Output:
1, 1, 2, 2
1, 1, 2, 2
3, 3, 4, 4
3, 3, 4, 4
1, 1, 2, 2
1, 1, 2, 2
3, 3, 4, 1
1, 2, 2, 4
So you won't be able to call the Array.Copy over the entire region but you'd be able to call it on contiguous segments of the code. The fact that you have a 3rd dimension will add to the complexity but it should be possible. This eliminates the need to flatten directly, but you'll need to still loop over sections. That being said, you should still see a measurable speed improvement.
Upvotes: 1
Reputation: 29244
Thanks for updating the question. Now I can answer using Buffer.BLockCopy()
. I've slaved around for a while, but I got it to work nicely. Here is the proof of concept code:
public enum CameraPos
{
TopLeft = 0,
TopRight = 1,
BottomLeft = 2,
BottomRight = 3
}
class Program
{
static void Main(string[] args)
{
const int W=360, H=288;
var BaseImage=new short[2*W, 2*H, 3];
// Initialize sequential numbers so I can debug the results easier.
short num=0;
for (int x=0; x<2*W; x++)
{
for (int y=0; y<2*H; y++)
{
// num = color + 3*( y+576*x )
BaseImage[x, y, 0]=num++;
BaseImage[x, y, 1]=num++;
BaseImage[x, y, 2]=num++;
}
}
var NewImage=new short[2*W, 2*H, 3];
CopySubImage(BaseImage, NewImage, CameraPos.TopRight);
// this copied x=360..719 and y=0..287
}
static void CopySubImage(short[, ,] base_image, short[, ,] new_image, CameraPos cam_index)
{
int W=base_image.GetLength(0)/2;
int H=base_image.GetLength(1)/2;
int x_index=((int)cam_index%2)*W; // either 0 or 360
// int y_index=((int)cam_index/2)*H; // either 0 or 288 (not needed)
// Copy columns with Buffer.BlockCopy. Shown below are the flat array indeces.
//
// | 3*(0+576*0) 3*(0+576*1) .. 3*(0+576*359) | 3*(0+576*360) .. 3*(0+576*719) |
// | 3*(1+576*0) 3*(1+576*1) .. 3*(1+576*359) | 3*(1+576*360) .. 3*(1+576*719) |
// | .. .. | .. .. |
// | 3*(287+576*0) .. 3*(287+576*359) | 3*(287+576*360) .. 3*(287+576*719) |
// + ---------------------------------------- + ---------------------------------- +
// | 3*(288+576*0) .. 3*(288+576*359) | 3*(288+576*360) .. 3*(288+576*719) |
// | .. .. | .. .. |
// | 3*(575+576*0) .. 3*(575+576*359) | 3*(575+576*360) .. 3*(575+576*719) |
for (int x=x_index; x<x_index+W; x++)
{
int k_start=3*(y_index+2*H*x);
//int k_end=3*(x_index+W+720*y); (not needed)
Buffer.BlockCopy(base_image, k_start, new_image, k_start, 3*H*sizeof(short));
}
}
}
Upvotes: 2