C - Как скопировать массив [600] [400] в массив [4] [4], а затем рандомизировать позиции элементов? - PullRequest
0 голосов
/ 26 октября 2018

Я пытаюсь создать функцию, которая перетасовывает изображение, как показано ниже: shuffle

Ее аргумент использует три 600x400 RGB-массива для создания цветов пикселей.Я пытался провести мозговой штурм в течение стольких часов, но я так озадачен методами, как это сделать.Вот идея, которую я пытался выработать, но я был ошеломлен и озадачен:

Скопируйте каждый массив RGB (R [] [], G [] [] и B [] [] отдельно, которые объединены)делает цветное изображение) в соответствующие временные массивы.Разделите временные массивы на 4x4.Каждый элемент будет содержать собственный 2D-массив с блоком исходного изображения.Затем, используя случайную библиотеку, я могу назначить элементы новым местам в 4х4.Я понятия не имею, как сделать это без создания 42 массивов (16 массивов на цвет в 4x4, но 42 массива для R, G и B).Буду признателен за любой совет или вот код, который у меня есть в настоящее время, но я приостановил (или, возможно, забросил) работу над:

Ответы [ 2 ]

0 голосов
/ 26 октября 2018
  1. Маркируйте каждый блок с идентификатором, 0, 1, 2, ... 15.

    -----------------
    | 12| 13| 14| 15|
    -----------------
    | 8 | 9 | 10| 11|
    -----------------                      
    | 4 | 5 | 6 | 7 |
    -----------------
    | 0 | 1 | 2 | 3 |
    -----------------
    
  2. Поместите все идентификаторы в массив, затемперемешать массив. перемешать, как это .Затем обойдите массив и поменяйте содержимое каждого блока.

    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 [ВЫСОТА] [ВЕС], а не R [ВЕС] [ВЫСОТА] (Это определениедолжно работать, но я не могу думать с этим).

    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 ничего не изменится.

0 голосов
/ 26 октября 2018

Используйте более качественную структуру данных

В данный момент вы используете многомерные массивы, у которых есть определенные достоинства и недостатки, слегка затронутые в других местах SO .Поскольку то, что вы делаете, - это обработка изображений, производительность может быть критической для вас, а разыменование многомерных массивов не совсем оптимально по ряду причин (т. Е. Вы, скорее всего, потеряете производительность из-за непоследовательного чтения).

Существует несколько способов как повысить производительность, так и упростив вашу жизнь:

Чередующийся одномерный массив

То есть вы должны использовать один массив unsigned char img[WIDTH * HEIGHT * COLORS],Это дает то преимущество, что ваш код также легче поддерживать, поскольку вы можете обрабатывать изображения RGB, B & W и RGBA с изменением на постоянную COLORS.Чтобы получить доступ к данному цвету одного пикселя, вы можете иметь 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().Они должны оказаться очень полезными в будущем, а также улучшат производительность, так как тогда вы можете скопировать все в целевой массив (вместе с shuffle) в операциях n вместо изменения оригинала в 2n.

Отказ от ответственности 1: Я не запускал код через компилятор.Нет гарантий, что он будет работать "из коробки".

Отказ от ответственности 2: Я также не утверждаю, что код был хорошо оптимизирован.Надо что-то оставить для вас.

...