C - 如何将 [600][400] 数组复制到 [4][4] 数组,然后随机化元素位置?
C - How to copy [600][400] array into [4][4] array, then randomize element positions?
我正在尝试制作一个随机播放图像的函数,如下所示:
它的参数采用三个 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 **
}
}
}
}
}
使用更好的数据结构
现在,您正在使用多维数组,它有一些优点和缺点 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];
}
}
现在,图像已被复制到段中,您可以根据需要对它们重新排序,因为 "segments" 只是指针。例如,下面将交换左上角和右下角的部分。
unsigned char seg_temp[] = segments[0];
segments[0] = segments[15];
segments[15] = seg_temp;
最后,要完成流程并将各个段重新合并在一起,您需要反向重做上述流程;做起来应该很简单,所以我把它留给你作为练习。
最后的笔记
如果您还没有,您应该熟悉 malloc()
and free()
functions, as well as memset()
and memcpy()
。它们在未来应该会非常有用,并且还会提高性能,因为您可以在 n
操作中将所有内容复制到目标数组(连同随机播放),而不是在 [=23= 中修改原始数组].
免责声明 1:我没有 运行 任何代码通过编译器。不保证运行开箱即用。
免责声明 2:我也不声称代码经过了很好的优化。得留点事给你做。
用 ID 0, 1, 2, ...15 标记每个块。
-----------------
| 12| 13| 14| 15|
-----------------
| 8 | 9 | 10| 11|
-----------------
| 4 | 5 | 6 | 7 |
-----------------
| 0 | 1 | 2 | 3 |
-----------------
将所有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]);
}
现在的问题是如何交换两个块。假设我们有块 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];
}
}
最后一步应该是将 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;
}
下面的示例代码适用于数组 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);
}
}
我应该提一下,Shuffle 之后有可能什么都没有改变。
我正在尝试制作一个随机播放图像的函数,如下所示:
它的参数采用三个 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 **
}
}
}
}
}
使用更好的数据结构
现在,您正在使用多维数组,它有一些优点和缺点 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];
}
}
现在,图像已被复制到段中,您可以根据需要对它们重新排序,因为 "segments" 只是指针。例如,下面将交换左上角和右下角的部分。
unsigned char seg_temp[] = segments[0];
segments[0] = segments[15];
segments[15] = seg_temp;
最后,要完成流程并将各个段重新合并在一起,您需要反向重做上述流程;做起来应该很简单,所以我把它留给你作为练习。
最后的笔记
如果您还没有,您应该熟悉 malloc()
and free()
functions, as well as memset()
and memcpy()
。它们在未来应该会非常有用,并且还会提高性能,因为您可以在 n
操作中将所有内容复制到目标数组(连同随机播放),而不是在 [=23= 中修改原始数组].
免责声明 1:我没有 运行 任何代码通过编译器。不保证运行开箱即用。
免责声明 2:我也不声称代码经过了很好的优化。得留点事给你做。
用 ID 0, 1, 2, ...15 标记每个块。
----------------- | 12| 13| 14| 15| ----------------- | 8 | 9 | 10| 11| ----------------- | 4 | 5 | 6 | 7 | ----------------- | 0 | 1 | 2 | 3 | -----------------
将所有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]); }
现在的问题是如何交换两个块。假设我们有块 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]; } }
最后一步应该是将 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; }
下面的示例代码适用于数组 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); } }
我应该提一下,Shuffle 之后有可能什么都没有改变。