【问题标题】:C - How to copy [600][400] array into [4][4] array, then randomize element positions?C - 如何将 [600][400] 数组复制到 [4][4] 数组中,然后随机化元素位置?
【发布时间】:2018-10-25 22:52:58
【问题描述】:

我正在尝试制作一个随机播放图像的函数,如下所示:

它的参数需要三个 600x400 RGB 数组来创建像素颜色。我已经尝试集思广益了这么多小时,但我对这样做的方法感到非常困惑。这是我尝试过的一个想法,但我不知所措并被难住了:

将每个 RGB 数组(R[][]、G[][] 和 B[][] 分别复制到它们各自的临时数组中。将临时数组拆分为 4x4。每个元素都将包含其自己的二维数组,其中包含原始图像块。然后使用随机库,我可以将元素分配到 4x4 中的新位置。我不知道如何在不制作 42 个数组的情况下做到这一点(4x4 中每种颜色 16 个数组,但 R、G 和 B 有 42 个数组)。我将不胜感激任何建议或这是我目前拥有的代码,但我暂停(或可能放弃)工作:

            void Shuffle(unsigned char R[WIDTH][HEIGHT], unsigned char G[WIDTH][HEIGHT], unsigned char B[WIDTH][HEIGHT]){

            // Initialize 150x100 inner shuffle arrays. These arrays are chunks of the original image
            int shuffArrR[150][100] = {0};
            int shuffArrG[150][100] = {0};
            int shuffArrB[150][100] = {0};

            int row = 0, col = 0;

        /*
                                BOUNDARY INFO FOR 4x4 ARRAY:

                                 C1: C2: C3: C4:    hBound# (row):
                                -------------------->   1
                           R1:  |   |   |   |   |
                                -------------------->   2
                           R2:  |   |   |   |   |
                                -------------------->   3                       
                           R3:  |   |   |   |   |
                                -------------------->   4
                           R4:  |   |   |   |   |
                                -------------------->   5
                                |   |   |   |   |
                                v   v   v   v   v

                vBound# (col):  1   2   3   4   5

                            vBound:         hBound:         
                            #:  col:        #:  row:
                            1    0          1    0
                            2   150         2   100
                            3   300         3   200
                            4   450         4   300
                            5   600         5   400
        */
            // Define boundaries
            int const vBound1 = 0, vBound2 = 150, vBound3 = 300, vBound4 = 450;
            int const hBound1 = 0, hBound2 = 100, hBound3 = 200, hBound4 = 300;

            for(row; row < HEIGHT; row++){
                for(col; col < WIDTH; col++){

                    // Copy RGB arrays to shuffle arrays
                    shuffArrR[col][row] = R[col][row];
                    shuffArrG[col][row] = G[col][row];
                    shuffArrB[col][row] = B[col][row];

                    // Define 16 blocks in 4x4 array ------------------

                    // If in R1
                    if(row >= hBound1 && row <= hBound2){

                        // And in C1
                        if(col >= vBound1 && col <= vBound2){
                            // ** I stopped here after I realized how many arrays I'd have to make to account for every element in the 4x4 **
                        }

                    }
                }
            }

        }

【问题讨论】:

  • 为原始数组中的每个块分配一个索引(0,15)。使用这些索引创建一个数组 array[16] = {0,1,2,...,14,15}; Fisher-Yates shuffle 数组。对于shuffled数组中的entry,index为输出图像中的block,value为block在原图中的index。
  • 如何为 16 个 2D 块中的每一个分配索引?
  • 您确定没有将其转换为更难处理的格式吗?通常像素是压缩的 RGB 或 GRB。
  • 我已将我之前的评论扩展为一个答案。

标签: c arrays multidimensional-array chunks


【解决方案1】:

使用更好的数据结构

现在,您正在使用多维数组,elsewhere on SO 有一些优点和缺点。因为您正在做的是图像处理,所以性能可能对您至关重要,并且由于各种原因(例如,由于非顺序读取,您更有可能失去性能),取消引用多维数组并不是最佳的。

有几种方法既可以提高性能,又可以让您的生活更轻松:

交错一维数组

意思是,您应该使用单个 unsigned char img[WIDTH * HEIGHT * COLORS] 数组。这样做的好处是使您的代码也更易于维护,因为您可以通过更改常量 COLORS 来处理 RGB、B&W 和 RGBA 图像。要访问单个像素的给定颜色,您可以使用img[y * width * COLORS + x * COLORS + color]。您也可以编写一个宏来帮助解决这个问题,例如

#define INDEX_XYZ(x,y,color) ((y) * WIDTH * COLORS + (x) * COLORS + (color))

为了进一步提高函数的可用性,考虑将每个维度的大小以及颜色数量传递给它。例如,您可以将签名更改为...

void Shuffle(unsigned char image[], int height, int width, int colors);

这将允许您在任何尺寸(只要两个尺寸都可以被四整除)和任何颜色的图像上使用相同的功能。您可能还希望传递一个指示细分数量的参数,因此您可以根据需要进行 3×3 分段或 8×8 分段,而无需更改函数或重复代码。

将图像分割成段

一种方法是为段创建数组...

unsigned char segments[SEG_HORI * SEG_VERT][WIDTH / SEG_HORI * HEIGHT / SEG_VERT * COLOR];

注意多维性——这里很好,因为我们希望有多个单独的段来存储数据。

之后我们从原始数据中复制数据:

// Calculate the height/width for the segments; you could do this as a macro too.
int seg_height = HEIGHT / SEG_VERT;
int seg_width = WIDTH / SEG_HORI;
// Iterate through the rows in the picture
for (int y = 0; y < HEIGHT; y++) 
{
    // Obtain the Y-coordinate of the segment.
    int segy = y / seg_height;

    // Iterate through the columns in the picture
    for (int x = 0; x < WIDTH; x++)
    {
        // Calculate the X-coordinate of the segment.
        int segx = x / seg_width,

        // Then calculate its index, using the X and Y coordinates.
            seg  = segy * SEG_HORI + segx,
        // Then, calculate the source index (from the image).
            src_idx = y * WIDTH * COLORS + x * COLORS,

        // Then, map the coordinates to the segment; notice that we take
        // modulos on the coordinates to get them to correctly map.
            dst_idx = y % seg_height * seg_width * COLORS + x % seg_width * COLORS;

        // Then copy the colors. You could also use memcpy(),
        // but the below should be more educational.
        for (int c = 0; c < COLORS; c++)
            segments[seg][dst_idx + c] = img[src_idx + c];
    }
}

现在,图像已被复制到段中,您可以根据需要对它们重新排序,因为“段”只是指针。例如,下面将交换左上角和右下角的部分。

unsigned char seg_temp[] = segments[0];
segments[0] = segments[15];
segments[15] = seg_temp;

最后,要完成流程并将片段重新合并在一起,您需要反向重做上述流程;做起来应该很简单,所以我把它留给你作为练习。

最后说明

如果您还没有,您应该熟悉malloc()free() 函数,以及memset()memcpy()。它们在未来应该会非常有用,并且还会提高这里的性能,因为您可以在 n 操作中将所有内容复制到目标数组(以及随机播放)中,而不是修改 2n 中的原始数组。

免责声明 1:我没有通过编译器运行任何代码。不保证它会开箱即用。

免责声明 2:我也没有声称代码经过良好优化。有事要留给你做。

【讨论】:

    【解决方案2】:
    1. 用 ID 0、1、2、...15 标记每个块。

      -----------------
      | 12| 13| 14| 15|
      -----------------
      | 8 | 9 | 10| 11|
      -----------------                      
      | 4 | 5 | 6 | 7 |
      -----------------
      | 0 | 1 | 2 | 3 |
      -----------------
      
    2. 将所有 ID 放入一个数组中,然后打乱数组。 shuffle like this。然后遍历数组,交换每个块的内容。

      int arr[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
      arr_shuffle(arr, 16);
      int i;
      for (i = 0; i < 16; i++) {
          swap_block(i, arr[i]);
      }
      
    3. 现在的问题是如何交换两个块。假设我们有块 A 和块 B。两者的大小都应该是 100(高度)* 150(宽度)。然后认为A是一个像A[100][150]这样的数组,而B是B[100][150]。交换这个数组将如下所示。

      for (i = 0; i < 100; i++) {
          for (j = 0; j < 150; j++) {
              swap(A[i][j], B[i][j];
          }
      }
      
    4. 最后一步应该是将 A[i][j] 和 B[i][j] 转换为数组 R/G/B 中的实元素。这可以简单地通过数学来完成。

      void get_real_id(int block_id, int x, int y, int *real_x, int *real_y)
      {
          int row, col;
      
          row = block_id / 4;   // convert block id to row number
          col = block_id % 4;   // convert block id to col number
      
          // find BLOCK[y][x] in array R, which should be R[real_y][real_x]
          *real_x = (col * (WIDTH/4)) + x;  
          *real_y = (row * (HEIGHT/4)) + y;
      }
      
    5. 下面的示例代码适用于数组 R。R 的定义是 R[HEIGHT][WEIGHT],而不是 R[WEIGHT][HEIGHT](这个定义应该也可以,但我想不通) .

      int R[HEIGHT][WIDTH];
      
      int arr_shuffle(int *arr, int len)
      {
          size_t i;
          for (i = 0; i < len - 1; i++)
          {
              size_t j = i + rand() / (RAND_MAX / (len - i) + 1);
              int t = arr[j];
              arr[j] = arr[i];
              arr[i] = t;
          }
      }
      
      void get_real_id(int block_id, int x, int y, int *real_x, int *real_y)
      {
          int row, col;
      
          row = block_id / 4;
          col = block_id % 4;
      
          *real_x = (col * (WIDTH/4)) + x;
          *real_y = (row * (HEIGHT/4)) + y;
      }
      
      void swap_block(int src, int dst)
      {
          int w_len = WIDTH / 4;  // should be 150
          int h_len = HEIGHT / 4; // should be 100
      
          int i, j;
      
          for (i = 0; i < h_len; i++) {
              for (j = 0; j < w_len; j++) {
                  int real_src_x;
                  int real_src_y;
      
                  int real_dst_x;
                  int real_dst_y;
      
                  get_real_id(src, j, i, &real_src_x, &real_src_y);
                  get_real_id(dst, j, i, &real_dst_x, &real_dst_y);
      
                  // swap two point.
                  int r = R[real_src_y][real_src_x];
                  R[real_src_y][real_src_x] = R[real_dst_y][real_dst_x];
                  R[real_dst_y][real_dst_x] = r;
              }
          }
      }
      
      int Shuffle()
      {
          int i;
          int arr[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
      
          arr_shuffle(arr, 16);
      
          for (i = 0; i < 16; i++) {
              int src_block_id = i;
              int dst_block_id = arr[i];
      
              swap_block(src_block_id, dst_block_id);
          }
      }
      
    6. 我应该提一下,在 Shuffle 之后可能没有任何变化。

    【讨论】:

      猜你喜欢
      • 2019-02-21
      • 1970-01-01
      • 2016-03-11
      • 1970-01-01
      • 2013-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-18
      相关资源
      最近更新 更多