c# 从子矩阵创建矩阵
c# Creating Matrix from sub matrixes
你好高技能程序员
我有一张 1600x1600 的测试图片。我将其作为灰度 int 值导入到矩阵中。然后我从 matrix.I 创建了 4x4 子矩阵,在这些块中进行了一些数学运算并创建了新块。现在我需要从那个新的 4x4 blocks.But 再次创建一个新矩阵 (1600x1600) 我无法创建循环。我总共有 (1600/4 * 1600/4 = 160 000) 个子矩阵。 (当然我的程序不是静态的,输入图像可以是 anything.This 用于测试图像)。现在这是我的结构。
Bitmap bmp = new Bitmap("c:\test.jpg");
pictureBox1.Image = Image.FromFile("c:\test.jpg");
int width = bmp.Width; int height = bmp.Height;
while (y < height) {
while (x < width) {
pxl = bmp.GetPixel(x, y);
int_grayscale_map[x, y] = GetGrayScale(pxl); //getgrayscale is function that returns int value
x++;}
y++;}
int totalblocknumber = (width/4) * (height / 4); //160 000 in this case
现在我根据这些代码创建并填充了子块。这里有人帮助我。(认为我们将 1600x1600 图像拼成 4x4 块)
Bitmap image = new Bitmap(FILENAME);
List<List<List<Int32>>> grayscale_map_block = newList<List<List<Int32>>>();
for (int row = 0; row < height; row += 4)
{
for (int col = 0; col < width; col += 4)
{
block.Add(new List<List<Color>>() {
new List<Color>() { image.GetPixel(col, row), image.GetPixel(col + 1, row), image.GetPixel(col + 2, row), image.GetPixel(col + 3, row)} ,
new List<Color>() { image.GetPixel(col, row + 1), image.GetPixel(col + 1, row + 1), image.GetPixel(col + 2, row + 1), image.GetPixel(col + 3, row + 1)} ,
new List<Color>() { image.GetPixel(col, row + 2), image.GetPixel(col + 1, row + 2), image.GetPixel(col + 2, row + 2), image.GetPixel(col + 3, row + 2)} ,
new List<Color>() { image.GetPixel(col, row + 3), image.GetPixel(col + 1, row + 3), image.GetPixel(col + 2, row + 3), image.GetPixel(col + 3, row + 3)} ,
});
grayscale_map_block.Add(new List<List<Int32>>() {
new List<Int32>() { GetGrayScale(image.GetPixel(col, row)), GetGrayScale(image.GetPixel(col + 1, row)), GetGrayScale(image.GetPixel(col + 2, row)), GetGrayScale(image.GetPixel(col + 3, row))} ,
new List<Int32>() { GetGrayScale(image.GetPixel(col, row + 1)), GetGrayScale(image.GetPixel(col + 1, row + 1)), GetGrayScale(image.GetPixel(col + 2, row + 1)), GetGrayScale(image.GetPixel(col + 3, row + 1))} ,
new List<Int32>() { GetGrayScale(image.GetPixel(col, row + 2)), GetGrayScale(image.GetPixel(col + 1, row + 2)), GetGrayScale(image.GetPixel(col + 2, row + 2)), GetGrayScale(image.GetPixel(col + 3, row + 2))} ,
new List<Int32>() { GetGrayScale(image.GetPixel(col, row + 3)), GetGrayScale(image.GetPixel(col + 1, row + 3)), GetGrayScale(image.GetPixel(col + 2, row + 3)), GetGrayScale(image.GetPixel(col + 3, row + 3))} ,
});
}
} // Getgrayscale is a function that input color return int value
仅此而已。现在我有 160 000 个 4x4 矩阵 "grayscale_map_block"
我正在使用这段代码来获取块的元素
grayscale_map_block [n] [x] [y] / 第 n 个块,x,y 元素。
其中 n =0-总块数
我必须从这些块中巧妙地创建一个将各个部分组合在一起的循环。一个新的 1600x1600 矩阵。感谢您的帮助..
您可以像这样映射 [x,y] -> [n,x_,y_]:
n = (y / 4) * (width/4) + (x/4);
x_ = x % 4;
y_ = y % 4;
计算n的思路是使用垂直子块的索引(y / 4),然后乘以一行中的子块数(width / 4),然后加上水平子块的索引(x / 4)。
然后使用取模运算符(%)计算子块内的行、列地址。
从 [n,x_,y_] 映射到 [x,y]
x = (n % (width / 4)) * 4 + x_;
y = (n / (width / 4)) * 4 + y_;
这里的想法是从单个索引n中恢复子块的水平和垂直索引。垂直索引是 n 除以一行中的子块数,即 (width / 4)。像素的垂直地址是垂直索引乘以 4 加上子块行。在水平方向上,您再次使用取模运算符来恢复块的水平索引。 n %(宽度/4)。然后同理乘以4再加上x_得到水平像素索引(列索引)。
注意:我提供的数学只有在宽度和高度是4的偶数倍时才有效。如果宽度和高度不能被4整除那么你必须稍微做一下不同的数学,但您还需要处理不是 4 x 4 的子块,所以我假设您暂时不想这样做——但如果需要,我也可以帮助您。
好的。这是一个使用 GetPixel 和 SetPixel 的方法(性能不佳,但易于理解。在完美的世界中,您会改用 LockBits。但现在让我们保持简单。)
我没有使用列表的列表,而是使用了二维数组。这要简单得多。 "blocks" 只是二维数组的二维数组。我想你可以把它想象成一个图像,其中每个像素都是一个完整的图像——或者可以把它想象成一个由小方形图像组成的网格。
看来您已经控制了位图的加载和保存,所以我将省略该部分。
/// <summary>
/// Example Image Processing class
/// Demonstrates treating images as 2D arrays.
/// </summary>
public class ImageProcessor {
/// <summary>
/// Creates a 2D array of Colors from a bitmap.
/// </summary>
/// <param name="bm">The input bitmap</param>
/// <returns>The output Color array</returns>
public static Color[,] BitmapToColorArray(Bitmap bm) {
int width = bm.Width;
int height = bm.Height;
Color[,] colorArray = new Color[width, height];
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
colorArray[x, y] = bm.GetPixel(x, y);
}
}
return colorArray;
}
/// <summary>
/// Creates a Bitmap from a 2D array of Colors.
/// </summary>
/// <param name="colorArray">The input Color 2D array</param>
/// <returns>The output bitmap</returns>
public static Bitmap ColorArrayToBitmap(Color[,] colorArray) {
int width = colorArray.GetLength(0);
int height = colorArray.GetLength(1);
Bitmap bm = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
bm.SetPixel(x, y, colorArray[x, y]);
}
}
return bm;
}
/// <summary>
/// Converts a Color to a gray value 0-255.
/// </summary>
/// <param name="color">The input color</param>
/// <returns>The output gray value.</returns>
public static int ColorToGray(Color color) {
int gray = (color.R * 30 + color.G * 59 + color.B * 11) / 100;
return gray;
}
/// <summary>
/// Converts a gray value to a Color
/// </summary>
/// <param name="gray">The input gray value</param>
/// <returns>The output Color</returns>
public static Color GrayToColor(int gray) {
return Color.FromArgb(gray, gray, gray);
}
/// <summary>
/// Creates a 2D gray array from a 2D Color array
/// </summary>
/// <param name="colorArray">The input 2D Color array</param>
/// <returns>The output 2D gray array</returns>
public static int[,] ColorArrayToGrayArray(Color[,] colorArray) {
int width = colorArray.GetLength(0);
int height = colorArray.GetLength(1);
int[,] grayArray = new int[width, height];
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
grayArray[x,y] = ColorToGray(colorArray[x, y]);
}
}
return grayArray;
}
/// <summary>
/// Creates a 2D Color Array from a 2D gray array
/// </summary>
/// <param name="grayArray">The input 2D gray array</param>
/// <returns>The output 2D Color array</returns>
public static Color[,] GrayArrayToColorArray(int[,] grayArray) {
int width = grayArray.GetLength(0);
int height = grayArray.GetLength(1);
Color[,] colorArray = new Color[width, height];
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
colorArray[x, y] = GrayToColor(grayArray[x, y]);
}
}
return colorArray;
}
/// <summary>
/// Generic function to extract a 2D rectangular sub-area of an array as a new 2D array.
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <param name="src">The input 2D array</param>
/// <param name="srcx">The column of the top-left corner of the sub-area to extract</param>
/// <param name="srcy">The row of the top-left corner of the sub-area to extract</param>
/// <param name="dstWidth">The width of the sub-area to extract</param>
/// <param name="dstHeight">The height o fthe sub-area to extract</param>
/// <returns>The output 2D array</returns>
public static T[,] SubArray<T>(T[,] src, int srcx, int srcy, int dstWidth, int dstHeight) {
int srcWidth = src.GetLength(0);
int srcHeight = src.GetLength(1);
if (srcx < 0) throw new ArgumentOutOfRangeException();
if (srcy < 0) throw new ArgumentOutOfRangeException();
if (srcx + dstWidth > srcWidth) throw new ArgumentOutOfRangeException();
if (srcy + dstHeight > srcHeight) throw new ArgumentOutOfRangeException();
T[,] dst = new T[dstWidth, dstHeight];
for (int dsty = 0; dsty < dstHeight; ++dsty) {
for (int dstx = 0; dstx < dstWidth; ++dstx) {
dst[dstx, dsty] = src[srcx + dstx, srcy + dsty];
}
}
return dst;
}
/// <summary>
/// Generic function to convert a 2D array into blocks (2D array of 2D arrays)
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <param name="src">The input 2D array</param>
/// <param name="blockSize">The width and height of each square block</param>
/// <returns>The output 2D array of 2D arrays</returns>
public T[,][,] ArrayToBlockArray<T>(T[,] src, int blockSize) {
int srcWidth = src.GetLength(0);
int srcHeight = src.GetLength(1);
if (srcWidth % blockSize != 0) throw new Exception(string.Format("Width must be divisible by {0}", blockSize));
if (srcHeight % blockSize != 0) throw new Exception(string.Format("Height must be divisible by {0}", blockSize));
int dstWidth = srcWidth / blockSize;
int dstHeight = srcHeight / blockSize;
T[,][,] dst = new T[dstWidth, dstHeight][,]; // The syntax for creating new array of arrays is weird.
for (int dsty = 0; dsty < dstHeight; ++dsty) {
for (int dstx = 0; dstx < dstWidth; ++dstx) {
dst[dstx, dsty] = SubArray(src, dstx * blockSize, dsty * blockSize, blockSize, blockSize);
}
}
return dst;
}
/// <summary>
/// Generic function to convert a 2D array of blocks (2D array of 2D arrays) back into a single 2D array.
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <param name="src">The input 2D array of 2D arrays</param>
/// <returns>The output 2D array</returns>
public T[,] BlockArrayToArray<T>(T[,][,] src) {
// assume uniform size
int blockWidth = src[0, 0].GetLength(0);
int blockHeight = src[0, 0].GetLength(1);
int srcWidth = src.GetLength(0);
int srcHeight = src.GetLength(1);
int dstWidth = srcWidth * blockWidth;
int dstHeight = srcHeight * blockHeight;
T[,] dst = new T[dstWidth, dstHeight];
for (int srcy = 0; srcy < srcHeight; ++srcy) {
for (int srcx = 0; srcx < srcWidth; ++srcx) {
for (int blocky = 0; blocky < blockHeight; ++blocky ) {
for (int blockx = 0; blockx < blockWidth; ++blockx) {
T[,] block = src[srcx, srcy];
if (block.GetLength(0) != blockWidth) throw new Exception(string.Format("Blocks must all have width {0}", blockWidth));
if (block.GetLength(1) != blockHeight) throw new Exception(string.Format("Blocks must all have height {0}", blockHeight));
int dstx = srcx * blockWidth + blockx;
int dsty = srcy * blockHeight + blocky;
dst[dstx, dsty] = src[srcx, srcy][blockx, blocky];
}
}
}
}
return dst;
}
/// <summary>
/// Example function that does end-to-end processing of a Bitmap.
/// </summary>
/// <param name="srcBitmap">The input bitmap</param>
/// <returns>The output bitmap</returns>
public Bitmap Process(Bitmap srcBitmap) {
const int blockSize = 4;
Color[,] srcColorArray = BitmapToColorArray(srcBitmap);
int[,] srcGrayArray = ColorArrayToGrayArray(srcColorArray);
int[,][,] srcBlockArray = ArrayToBlockArray(srcGrayArray, blockSize);
// TODO: Presumably you're going to modify the source block array.
int[,][,] dstBlockArray = srcBlockArray; // PLACEHOLDER: do nothing for now.
// Reconstitute a new bitmap from the (presumably modified) destination block array.
int[,] dstGrayArray = BlockArrayToArray(dstBlockArray);
Color[,] dstColorArray = GrayArrayToColorArray(dstGrayArray);
Bitmap dstBitmap = ColorArrayToBitmap(dstColorArray);
return dstBitmap;
}
}
下面是一个使用不同索引机制访问二维数组的示例。这里 n
是块号,x
和 y
是 4x4 块内的 0-3 索引。这只是将 (n,x,y) 重新映射到 (xx,yy),这是原始二维数组中数据的索引。
class BlockData
{
public int[,] data;
internal void reindex(int n, int x, int y, out int xx, out int yy)
{
const int blockSize = 4;
int width = data.GetLength(0);
int columns = width / blockSize;
int row = n / columns;
int col = n % columns;
xx = col * blockSize + x;
yy = row * blockSize + y;
}
public int this[int n, int x, int y]
{
get
{
int xx, yy;
reindex(n, x, y, out xx, out yy);
return data[xx, yy];
}
set
{
int xx, yy;
reindex(n, x, y, out xx, out yy);
data[xx, yy] = value;
}
}
public int this[int xx, int yy]
{
get
{
return data[xx, yy];
}
set
{
data[xx, yy] = value;
}
}
}
class Program
{
static void Main(string[] args)
{
BlockData b = new BlockData() { data = new int[1600, 1600] };
b[10, 5] = 999;
// (10,5) is in the 402nd block of 4x4 at (2,1) within that block.
Debug.Assert(b[402, 2, 1] == 999);
b[888, 3, 2] = 777;
// The 888th block is row 2, column 88. Its top-left is at ((88*4),(2*4)).
// (352 + 3, 8 + 2) = (355, 10)
Debug.Assert(b[355, 10] == 777);
}
}
使用相同的策略,您也可以在内部将数据存储为一维数组,并提供从 [n][x][y] 到线性 [i] 的不同索引映射。
为对象提供数组索引运算符实际上只是 "cute"。这不是必需的。这个想法只是通过数学运算来计算您要访问的源数据的索引。但这有助于说明我的观点。
(性能说明:如果您想加快访问速度,可以优化此设置,以便在初始化 data
时预先计算 blockSize
、width
和 columns
时间并避免一直调用 data.GetLength(0)
。)
你好高技能程序员
我有一张 1600x1600 的测试图片。我将其作为灰度 int 值导入到矩阵中。然后我从 matrix.I 创建了 4x4 子矩阵,在这些块中进行了一些数学运算并创建了新块。现在我需要从那个新的 4x4 blocks.But 再次创建一个新矩阵 (1600x1600) 我无法创建循环。我总共有 (1600/4 * 1600/4 = 160 000) 个子矩阵。 (当然我的程序不是静态的,输入图像可以是 anything.This 用于测试图像)。现在这是我的结构。
Bitmap bmp = new Bitmap("c:\test.jpg");
pictureBox1.Image = Image.FromFile("c:\test.jpg");
int width = bmp.Width; int height = bmp.Height;
while (y < height) {
while (x < width) {
pxl = bmp.GetPixel(x, y);
int_grayscale_map[x, y] = GetGrayScale(pxl); //getgrayscale is function that returns int value
x++;}
y++;}
int totalblocknumber = (width/4) * (height / 4); //160 000 in this case
现在我根据这些代码创建并填充了子块。这里有人帮助我。(认为我们将 1600x1600 图像拼成 4x4 块)
Bitmap image = new Bitmap(FILENAME);
List<List<List<Int32>>> grayscale_map_block = newList<List<List<Int32>>>();
for (int row = 0; row < height; row += 4)
{
for (int col = 0; col < width; col += 4)
{
block.Add(new List<List<Color>>() {
new List<Color>() { image.GetPixel(col, row), image.GetPixel(col + 1, row), image.GetPixel(col + 2, row), image.GetPixel(col + 3, row)} ,
new List<Color>() { image.GetPixel(col, row + 1), image.GetPixel(col + 1, row + 1), image.GetPixel(col + 2, row + 1), image.GetPixel(col + 3, row + 1)} ,
new List<Color>() { image.GetPixel(col, row + 2), image.GetPixel(col + 1, row + 2), image.GetPixel(col + 2, row + 2), image.GetPixel(col + 3, row + 2)} ,
new List<Color>() { image.GetPixel(col, row + 3), image.GetPixel(col + 1, row + 3), image.GetPixel(col + 2, row + 3), image.GetPixel(col + 3, row + 3)} ,
});
grayscale_map_block.Add(new List<List<Int32>>() {
new List<Int32>() { GetGrayScale(image.GetPixel(col, row)), GetGrayScale(image.GetPixel(col + 1, row)), GetGrayScale(image.GetPixel(col + 2, row)), GetGrayScale(image.GetPixel(col + 3, row))} ,
new List<Int32>() { GetGrayScale(image.GetPixel(col, row + 1)), GetGrayScale(image.GetPixel(col + 1, row + 1)), GetGrayScale(image.GetPixel(col + 2, row + 1)), GetGrayScale(image.GetPixel(col + 3, row + 1))} ,
new List<Int32>() { GetGrayScale(image.GetPixel(col, row + 2)), GetGrayScale(image.GetPixel(col + 1, row + 2)), GetGrayScale(image.GetPixel(col + 2, row + 2)), GetGrayScale(image.GetPixel(col + 3, row + 2))} ,
new List<Int32>() { GetGrayScale(image.GetPixel(col, row + 3)), GetGrayScale(image.GetPixel(col + 1, row + 3)), GetGrayScale(image.GetPixel(col + 2, row + 3)), GetGrayScale(image.GetPixel(col + 3, row + 3))} ,
});
}
} // Getgrayscale is a function that input color return int value
仅此而已。现在我有 160 000 个 4x4 矩阵 "grayscale_map_block" 我正在使用这段代码来获取块的元素 grayscale_map_block [n] [x] [y] / 第 n 个块,x,y 元素。 其中 n =0-总块数
我必须从这些块中巧妙地创建一个将各个部分组合在一起的循环。一个新的 1600x1600 矩阵。感谢您的帮助..
您可以像这样映射 [x,y] -> [n,x_,y_]:
n = (y / 4) * (width/4) + (x/4);
x_ = x % 4;
y_ = y % 4;
计算n的思路是使用垂直子块的索引(y / 4),然后乘以一行中的子块数(width / 4),然后加上水平子块的索引(x / 4)。
然后使用取模运算符(%)计算子块内的行、列地址。
从 [n,x_,y_] 映射到 [x,y]
x = (n % (width / 4)) * 4 + x_;
y = (n / (width / 4)) * 4 + y_;
这里的想法是从单个索引n中恢复子块的水平和垂直索引。垂直索引是 n 除以一行中的子块数,即 (width / 4)。像素的垂直地址是垂直索引乘以 4 加上子块行。在水平方向上,您再次使用取模运算符来恢复块的水平索引。 n %(宽度/4)。然后同理乘以4再加上x_得到水平像素索引(列索引)。
注意:我提供的数学只有在宽度和高度是4的偶数倍时才有效。如果宽度和高度不能被4整除那么你必须稍微做一下不同的数学,但您还需要处理不是 4 x 4 的子块,所以我假设您暂时不想这样做——但如果需要,我也可以帮助您。
好的。这是一个使用 GetPixel 和 SetPixel 的方法(性能不佳,但易于理解。在完美的世界中,您会改用 LockBits。但现在让我们保持简单。)
我没有使用列表的列表,而是使用了二维数组。这要简单得多。 "blocks" 只是二维数组的二维数组。我想你可以把它想象成一个图像,其中每个像素都是一个完整的图像——或者可以把它想象成一个由小方形图像组成的网格。
看来您已经控制了位图的加载和保存,所以我将省略该部分。
/// <summary>
/// Example Image Processing class
/// Demonstrates treating images as 2D arrays.
/// </summary>
public class ImageProcessor {
/// <summary>
/// Creates a 2D array of Colors from a bitmap.
/// </summary>
/// <param name="bm">The input bitmap</param>
/// <returns>The output Color array</returns>
public static Color[,] BitmapToColorArray(Bitmap bm) {
int width = bm.Width;
int height = bm.Height;
Color[,] colorArray = new Color[width, height];
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
colorArray[x, y] = bm.GetPixel(x, y);
}
}
return colorArray;
}
/// <summary>
/// Creates a Bitmap from a 2D array of Colors.
/// </summary>
/// <param name="colorArray">The input Color 2D array</param>
/// <returns>The output bitmap</returns>
public static Bitmap ColorArrayToBitmap(Color[,] colorArray) {
int width = colorArray.GetLength(0);
int height = colorArray.GetLength(1);
Bitmap bm = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
bm.SetPixel(x, y, colorArray[x, y]);
}
}
return bm;
}
/// <summary>
/// Converts a Color to a gray value 0-255.
/// </summary>
/// <param name="color">The input color</param>
/// <returns>The output gray value.</returns>
public static int ColorToGray(Color color) {
int gray = (color.R * 30 + color.G * 59 + color.B * 11) / 100;
return gray;
}
/// <summary>
/// Converts a gray value to a Color
/// </summary>
/// <param name="gray">The input gray value</param>
/// <returns>The output Color</returns>
public static Color GrayToColor(int gray) {
return Color.FromArgb(gray, gray, gray);
}
/// <summary>
/// Creates a 2D gray array from a 2D Color array
/// </summary>
/// <param name="colorArray">The input 2D Color array</param>
/// <returns>The output 2D gray array</returns>
public static int[,] ColorArrayToGrayArray(Color[,] colorArray) {
int width = colorArray.GetLength(0);
int height = colorArray.GetLength(1);
int[,] grayArray = new int[width, height];
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
grayArray[x,y] = ColorToGray(colorArray[x, y]);
}
}
return grayArray;
}
/// <summary>
/// Creates a 2D Color Array from a 2D gray array
/// </summary>
/// <param name="grayArray">The input 2D gray array</param>
/// <returns>The output 2D Color array</returns>
public static Color[,] GrayArrayToColorArray(int[,] grayArray) {
int width = grayArray.GetLength(0);
int height = grayArray.GetLength(1);
Color[,] colorArray = new Color[width, height];
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
colorArray[x, y] = GrayToColor(grayArray[x, y]);
}
}
return colorArray;
}
/// <summary>
/// Generic function to extract a 2D rectangular sub-area of an array as a new 2D array.
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <param name="src">The input 2D array</param>
/// <param name="srcx">The column of the top-left corner of the sub-area to extract</param>
/// <param name="srcy">The row of the top-left corner of the sub-area to extract</param>
/// <param name="dstWidth">The width of the sub-area to extract</param>
/// <param name="dstHeight">The height o fthe sub-area to extract</param>
/// <returns>The output 2D array</returns>
public static T[,] SubArray<T>(T[,] src, int srcx, int srcy, int dstWidth, int dstHeight) {
int srcWidth = src.GetLength(0);
int srcHeight = src.GetLength(1);
if (srcx < 0) throw new ArgumentOutOfRangeException();
if (srcy < 0) throw new ArgumentOutOfRangeException();
if (srcx + dstWidth > srcWidth) throw new ArgumentOutOfRangeException();
if (srcy + dstHeight > srcHeight) throw new ArgumentOutOfRangeException();
T[,] dst = new T[dstWidth, dstHeight];
for (int dsty = 0; dsty < dstHeight; ++dsty) {
for (int dstx = 0; dstx < dstWidth; ++dstx) {
dst[dstx, dsty] = src[srcx + dstx, srcy + dsty];
}
}
return dst;
}
/// <summary>
/// Generic function to convert a 2D array into blocks (2D array of 2D arrays)
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <param name="src">The input 2D array</param>
/// <param name="blockSize">The width and height of each square block</param>
/// <returns>The output 2D array of 2D arrays</returns>
public T[,][,] ArrayToBlockArray<T>(T[,] src, int blockSize) {
int srcWidth = src.GetLength(0);
int srcHeight = src.GetLength(1);
if (srcWidth % blockSize != 0) throw new Exception(string.Format("Width must be divisible by {0}", blockSize));
if (srcHeight % blockSize != 0) throw new Exception(string.Format("Height must be divisible by {0}", blockSize));
int dstWidth = srcWidth / blockSize;
int dstHeight = srcHeight / blockSize;
T[,][,] dst = new T[dstWidth, dstHeight][,]; // The syntax for creating new array of arrays is weird.
for (int dsty = 0; dsty < dstHeight; ++dsty) {
for (int dstx = 0; dstx < dstWidth; ++dstx) {
dst[dstx, dsty] = SubArray(src, dstx * blockSize, dsty * blockSize, blockSize, blockSize);
}
}
return dst;
}
/// <summary>
/// Generic function to convert a 2D array of blocks (2D array of 2D arrays) back into a single 2D array.
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <param name="src">The input 2D array of 2D arrays</param>
/// <returns>The output 2D array</returns>
public T[,] BlockArrayToArray<T>(T[,][,] src) {
// assume uniform size
int blockWidth = src[0, 0].GetLength(0);
int blockHeight = src[0, 0].GetLength(1);
int srcWidth = src.GetLength(0);
int srcHeight = src.GetLength(1);
int dstWidth = srcWidth * blockWidth;
int dstHeight = srcHeight * blockHeight;
T[,] dst = new T[dstWidth, dstHeight];
for (int srcy = 0; srcy < srcHeight; ++srcy) {
for (int srcx = 0; srcx < srcWidth; ++srcx) {
for (int blocky = 0; blocky < blockHeight; ++blocky ) {
for (int blockx = 0; blockx < blockWidth; ++blockx) {
T[,] block = src[srcx, srcy];
if (block.GetLength(0) != blockWidth) throw new Exception(string.Format("Blocks must all have width {0}", blockWidth));
if (block.GetLength(1) != blockHeight) throw new Exception(string.Format("Blocks must all have height {0}", blockHeight));
int dstx = srcx * blockWidth + blockx;
int dsty = srcy * blockHeight + blocky;
dst[dstx, dsty] = src[srcx, srcy][blockx, blocky];
}
}
}
}
return dst;
}
/// <summary>
/// Example function that does end-to-end processing of a Bitmap.
/// </summary>
/// <param name="srcBitmap">The input bitmap</param>
/// <returns>The output bitmap</returns>
public Bitmap Process(Bitmap srcBitmap) {
const int blockSize = 4;
Color[,] srcColorArray = BitmapToColorArray(srcBitmap);
int[,] srcGrayArray = ColorArrayToGrayArray(srcColorArray);
int[,][,] srcBlockArray = ArrayToBlockArray(srcGrayArray, blockSize);
// TODO: Presumably you're going to modify the source block array.
int[,][,] dstBlockArray = srcBlockArray; // PLACEHOLDER: do nothing for now.
// Reconstitute a new bitmap from the (presumably modified) destination block array.
int[,] dstGrayArray = BlockArrayToArray(dstBlockArray);
Color[,] dstColorArray = GrayArrayToColorArray(dstGrayArray);
Bitmap dstBitmap = ColorArrayToBitmap(dstColorArray);
return dstBitmap;
}
}
下面是一个使用不同索引机制访问二维数组的示例。这里 n
是块号,x
和 y
是 4x4 块内的 0-3 索引。这只是将 (n,x,y) 重新映射到 (xx,yy),这是原始二维数组中数据的索引。
class BlockData
{
public int[,] data;
internal void reindex(int n, int x, int y, out int xx, out int yy)
{
const int blockSize = 4;
int width = data.GetLength(0);
int columns = width / blockSize;
int row = n / columns;
int col = n % columns;
xx = col * blockSize + x;
yy = row * blockSize + y;
}
public int this[int n, int x, int y]
{
get
{
int xx, yy;
reindex(n, x, y, out xx, out yy);
return data[xx, yy];
}
set
{
int xx, yy;
reindex(n, x, y, out xx, out yy);
data[xx, yy] = value;
}
}
public int this[int xx, int yy]
{
get
{
return data[xx, yy];
}
set
{
data[xx, yy] = value;
}
}
}
class Program
{
static void Main(string[] args)
{
BlockData b = new BlockData() { data = new int[1600, 1600] };
b[10, 5] = 999;
// (10,5) is in the 402nd block of 4x4 at (2,1) within that block.
Debug.Assert(b[402, 2, 1] == 999);
b[888, 3, 2] = 777;
// The 888th block is row 2, column 88. Its top-left is at ((88*4),(2*4)).
// (352 + 3, 8 + 2) = (355, 10)
Debug.Assert(b[355, 10] == 777);
}
}
使用相同的策略,您也可以在内部将数据存储为一维数组,并提供从 [n][x][y] 到线性 [i] 的不同索引映射。
为对象提供数组索引运算符实际上只是 "cute"。这不是必需的。这个想法只是通过数学运算来计算您要访问的源数据的索引。但这有助于说明我的观点。
(性能说明:如果您想加快访问速度,可以优化此设置,以便在初始化 data
时预先计算 blockSize
、width
和 columns
时间并避免一直调用 data.GetLength(0)
。)