0

Hello high Skilled Programmers

I have a test image 1600x1600. I imported this to a matrix as grayscale int values. Then i created 4x4 sub matrixes from that matrix.I made some math operations in these blocks and created new blocks. Now i need to create a new matrix again (1600x1600) from that new 4x4 blocks.But i couldnt create the loop. I Have (1600/4 * 1600/4 = 160 000) sub matrixes totaly. (Of course my program is not static , the input image can be anything.This is for test image). Now this is my structure.

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

Now I created and populated the sub blocks from this codes. Someone helped me here.(think that we puzzled the 1600x1600 image to 4x4 pieces)

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

All that is. Now i have 160 000 piece of 4x4 matrix caled "grayscale_map_block" i am using this code to get the element of the blocks grayscale_map_block [n] [x] [y] / n'th block , x,y element. where n =0-totalblocknumber

From that blocks i must smartly create a loop that get pieces together. A new 1600x1600 matrix. Thanks for your helps..

3 Answers3

0

You can map [x,y] -> [n,x_,y_] like this:

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

The idea is to calculate n is use the index of the sub-block vertically (y / 4), and then multiply that by the number of sub-blocks in a row (width / 4), and then add the index of the sub-block horizontally (x / 4).

Then use the modulus operator (%) to compute the row,column address within the sub-block.

To map from [n,x_,y_] to [x,y]

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

The idea there is to recover the horizontal and vertical index of the sub-block from the single index n. The vertical index is n divided by the number of sub-blocks in a row, which is (width / 4). The vertical address of the pixel is the vertical index times 4 plus the sub-block row. Horizontally you use the modulus operator again to recover the horizontal index of the block. n % (width / 4). Then similarly, multiply by 4 and add x_ to get the horizontal pixel index (column index).

Note: The math I provided only works if width and height are even multiples of 4. If the width and height do not divide evenly by 4 then you have to do slightly different math, but you also would need to handle sub-blocks that aren't 4 x 4, so I'll assume you don't want to do that for now -- but I can help you with that too, if needed.

Wyck
  • 10,311
  • 6
  • 39
  • 60
  • i'll deal with the "dividable by 4 " problem later. Now test objects are always dividable by 4. But i couldnt get this structure completely :( sorry. How can i drive a loop from blocks using this example that populate my final 1600x1600 matrix – SadViolinMan Sep 05 '18 at 19:52
  • `for(y=0; y<1600; y++) for(x=0;x<1600;x++) bmp.SetPixel(x,y, bw_to_color( grayscale_map_block[n][y_][x_]))` – Wyck Sep 05 '18 at 19:55
  • Would it help if I wrote it out entirely? That'll take me more time, I thought this would be enough to give you the gist of it. I'm reluctant to actually write that code because the "list of lists" is an inefficient way to store pixels, it's much better to use an array. And the "getpixel/setpixel" access pattern is also poor if you're going to visit every pixel. You should instead use LockBits and it will be many many times faster. It's not really a good advice to do what you're doing. But I'll do it if you want. – Wyck Sep 05 '18 at 20:01
  • when i started this program , too many suggestions came up, lockbits as well , but since i'm new in c# (this is my first program actually) , all of them looked very diffucult . i couldn't understand them. But with this i did it. Because in math operations i needed that form. Block [blocknumber] [row] [coloumb]. Thats why i choosed it. This is gonna be image encryption program. İf it be slow , i can change my codes again. NOW i am still trying the understand this consept. But still i couldnt. Maybe you should write all . Thanks. And i will search for Lockbits as well – SadViolinMan Sep 05 '18 at 20:09
  • Thank you for the code. İt is a good structure. I think i understand a little. But i must say that , my program actually is not a image processing program. I am only using image functions on start and end of the program.Between Theese i am just making math operations. Thats why i used that form , Block [i] [x] [y]. the math operations i do with 4x4 blocks are complex. Thats why this form suits me. You can see what i am doing from here [link](https://www.youtube.com/watch?v=gP4PqVGudtg) if you say i can get theese form "block [i] [x][y]" from this structure , i can change all my codes. – SadViolinMan Sep 06 '18 at 14:50
  • Otherwise i just only need the codes for the getting final matrix from blocks. The "List – SadViolinMan Sep 06 '18 at 14:54
  • In your previous comment you said "this is gonna be image encryption program", then you said "actually is not a image processing program". Those two statements are in conflict with each other because _image encryption_ *is* _image processing_ IMO. – Wyck Sep 06 '18 at 14:54
  • if you look at this link you'll understand actually it is not a really image processing" [link](https://www.youtube.com/watch?v=gP4PqVGudtg). just math operations. Alot of math operations with 4x4 blocks – SadViolinMan Sep 06 '18 at 14:56
  • It seems like you just want the access pattern of [i][x'][y'] on your 2d [x][y] array where `i` is the _block number_ when turning your 2d array into blocks of 4x4. This is just an index remapping exercise. You don't actually need to store your data in a different format to access it with a different pattern. I can show if you like. – Wyck Sep 06 '18 at 15:18
0

OK. Here's one that uses GetPixel and SetPixel (Bad for performance, but easy to understand. In a perfect world, you would use LockBits instead. But let's just keep it simple for now.)

And instead of using lists of lists, I just went with 2D arrays. This is much simpler. The "blocks" are just 2D arrays of 2D arrays. I suppose you could think of it like an image where each pixel is an entire image -- or maybe think of it as a grid of tiny square images.

Looks like you've got loading and saving of Bitmaps under control, so I'll omit that part.

/// <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;
    }
}
Wyck
  • 10,311
  • 6
  • 39
  • 60
0

Here is an example of accessing a 2D array with a different indexing mechanism. Here n is the block number and x and y are the 0-3 indices within the 4x4 block. This just remaps (n,x,y) to (xx,yy) which are the indices of the data in the original 2D array.

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

Using the same strategy, you could store your data internally as a 1D array too and provide different index mappings from [n][x][y] to just a linear [i].

Providing an object with an array index operator is really just "cute". It's not necessary. The idea is just to do the math to calculate the indices of your source data you want to access. But it helps illustrate my point.

(Performance note: You could optimize this so that blockSize, width, and columns are precomputed when you initialize data if you wanted to speed up access time and avoid calling data.GetLength(0) all the time.)

Wyck
  • 10,311
  • 6
  • 39
  • 60
  • Hmm. I think I'm getting somewhere. Now Lets say i've done math operations in blocks and i'll assign it to "encrypted_array[width,height]". How will i populate it with element in the blocks using this codes. Also this line "Debug.Assert(b[402, 2, 1] == 999);" Visual studio doesn't recognise Debug. İt say "The name Debug Does not exist in current context. What is it for. What should i do after "BlockData enrypted_Array = new BlockData() { data = new int[width, height] }; (Also dont think that i just ask and ask without trying. I do all my effort to understand and make it work ,then I came here) – SadViolinMan Sep 06 '18 at 20:55
  • Two choices to resolve making the "The name Debug does not exist..." error: 1) Add `using System.Diagnostics;` to the top of the file to make the `Debug` thing work. or 2) you can just replace the word `Debug` with `System.Diagnostics.Debug` explicitly. Sorry for omitting it, but resolve "using" issues is a pretty typical kind of thing we do in C# land. So much that Visual Studio provides "Quick actions and refactoring..." to deal with it. You can put your cursor on `Debug` and hit Ctrl+. (that's Control and period) and the IDE will propose the two solutions I mentioned above. – Wyck Sep 07 '18 at 17:03
  • As for how will you use it...it should be an appropriate wrapper for your `int_grayscale_map`. – Wyck Sep 08 '18 at 00:43
  • i've been trying lots of solutions , still couldn't succed it. i dont know why. Yes Debug.Assert is working. But i dont know what it does. From this codes , Where is my latest 2d matrix. İS İT b ? How would i populate it ? adding "BlockData b = new BlockData() { data = new int[1600, 1600] };" code is enough? – SadViolinMan Sep 11 '18 at 19:53
  • `BlockData b = new BlockData() { data = int_grayscale_map };` – Wyck Sep 11 '18 at 20:27
  • i should be able to pass "grayscale_map_block [n] [x] [y]" to class , but i cant do it it says cannot convert type List... to int[","]. i tried convert.toint32 , doesnt work.class needs 2d matrix to pass – SadViolinMan Sep 11 '18 at 20:42
  • The example I wrote uses a 2D array natively, and provides 3D access to it. You can copy it in element by element if you want. `for(int n=0; n – Wyck Sep 12 '18 at 20:56
  • i just need clean 2d array from blocks. i dont think this structure work for me. still it gives errors. i have to start from strach. thanks four your help. i have to delete question to ask again for different ideas. thanks for everythink – SadViolinMan Sep 13 '18 at 20:27
  • Just to confirm....your input is the `int_grayscale_map[n][x][y]` array and your output will be back into the 2d `int_grayscale_map[1600,1600]`(or possibly a brand new `int_brandnew_grayscale_map[1600,1600]` -- which is basically the same code) right? – Wyck Sep 14 '18 at 01:15
  • my first input came from image is **grayscale_map [1600,1600]** , then with List – SadViolinMan Sep 17 '18 at 01:08