C#从子矩阵创建矩阵

时间:2018-09-05 19:12:49

标签: c# image loops matrix submatrix

高技能程序员

我有一个测试图像1600x1600。我将此作为灰度int值导入到矩阵中。然后我从该矩阵创建了4x4子矩阵,并在这些块中进行了一些数学运算并创建了新的块。现在我需要根据新的4x4块再次创建一个新的矩阵(1600x1600)。但是我无法创建循环。我总共有(1600/4 * 1600/4 = 160000)个子矩阵。 (当然我的程序不是静态的,输入图像可以是任何东西。这是用于测试图像的)。现在这是我的结构。

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

所有。现在我有160000张4x4矩阵,尺寸为“ grayscale_map_block” 我正在使用此代码来获取块的元素 grayscale_map_block [n] [x] [y] /第n个块,x,y元素。  其中n = 0-总块数

从那一刻起,我必须巧妙地创建一个将各个部分组合在一起的循环。新的1600x1600矩阵。谢谢您的帮助。

3 个答案:

答案 0 :(得分:0)

您可以这样映射[x,y]-> [n,x_,y_]:

n = (y / 4) * (width/4) + (x/4);
x_ = x % 4;
y_ = y % 4;

这个想法是要计算n是垂直使用子块的索引(y / 4),然后将其乘以一行中子块的数量(宽度/ 4),然后添加索引水平(x / 4)的子块。

然后使用模数运算符(%)计算子块内的行,列地址。

要从[n,x_,y_]映射到[x,y]

x = (n % (width / 4)) * 4 + x_;
y = (n / (width / 4)) * 4 + y_;

这里的想法是从单个索引n中恢复子块的水平和垂直索引。垂直索引是n除以一行中子块的数量,即(宽度/ 4)。像素的垂直地址是垂直索引乘以4加上子块行。在水平方向上,再次使用模数运算符恢复块的水平索引。 n%(宽度/ 4)。然后类似地,乘以4并加x_以获得水平像素索引(列索引)。

注意:我提供的数学仅在宽度和高度是4的倍数时才有效。如果宽度和高度没有被4均分,那么您必须做稍有不同的数学,但是还需要处理不是4 x 4的子块,所以我假设您暂时不想这样做-但如果需要,我也可以为您提供帮助。

答案 1 :(得分:0)

好。这是使用GetPixel和SetPixel的工具(性能差,但易于理解。在理想的情况下,您应该使用LockBits。但是现在就让它保持简单。)

我没有使用列表,而是使用了2D数组。这要简单得多。 “块”只是2D阵列的2D阵列。我想您可以将其视为一个图像,其中每个像素都是完整图像,或者可以将其视为由微小正方形图像组成的网格。

看起来您已经可以控制位图的加载和保存,因此我将省略该部分。

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

答案 2 :(得分:0)

这里是使用不同索引机制访问2D数组的示例。这里n是块号,xy是4x4块内的0-3索引。这只是将(n,x,y)重新映射为(xx,yy),它们是原始2D数组中数据的索引。

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]的不同索引映射。

使用数组索引运算符提供对象实际上只是“可爱”。这不是必需的。这个想法只是做数学计算要访问的源数据的索引。但这有助于说明我的观点。

(性能注意:您可以优化此设置,以便在初始化blockSize时预先计算widthcolumnsdata,如果您想加快访问时间并避免一直打data.GetLength(0)。)