Набор проблем Cs50 4 — Функция отражения с меньшим количеством фильтров

#c #filter #cs50

#c #Фильтр #cs50

Вопрос:

Я написал функцию для отражения изображений, которые были предоставлены в zip-файле как .bmps.

После некоторых исследований я увидел, что многие люди, решившие эту проблему, разделили ширину изображения на 2. Однако я чувствовал, что это неприменимо к моему коду.

Код действительно отражает изображение, видимое на глаз, но он не соответствует ни одному из критериев, предоставленных check50.

Если width / 2 действительно необходим, даже в моем коде, пожалуйста, объясните логику реализации этого в моем экземпляре кода:

 void reflect(int height, int width, RGBTRIPLE image[height][width])
{
    RGBTRIPLE coloursofaddress[width];
    for (int i = 0; i < height; i  )
    {
        for (int j = 0; j < width; j  )
        {
            // Step 3
            coloursofaddress[width - 1 - j].rgbtRed = image[i][j].rgbtRed;
            coloursofaddress[width - 1 - j].rgbtGreen = image[i][j].rgbtGreen;
            coloursofaddress[width - 1 - j].rgbtBlue = image[i][j].rgbtBlue;
            
            // Step 4
            image[i][j].rgbtRed = coloursofaddress[j].rgbtRed;
            image[i][j].rgbtGreen = coloursofaddress[j].rgbtGreen;
            image[i][j].rgbtBlue = coloursofaddress[j].rgbtBlue;
        }
    }
    return;
}
  

Ниже приведены сообщения об ошибках:

 :( reflect correctly filters 1x2 image
    expected "0 0 255n255 0...", not "5 0 0n255 0 0..."
:( reflect correctly filters 1x3 image
    expected "0 0 255n0 255...", not "5 0 0n0 255 0..."
:( reflect correctly filters image that is its own mirror image
    expected "255 0 0n255 0...", not "5 0 0n255 0 0..."
:( reflect correctly filters 3x3 image
    expected "70 80 90n40 5...", not "5 0 0n40 50 6..."
:( reflect correctly filters 4x4 image
    expected "100 110 120n7...", not "5 0 0n0 0 0n..." 
  

Редактировать:
После тщательного дальнейшего изучения отраженного изображения, выводимого приведенным выше кодом, все кажется идеальным, за исключением тонкого дополнительного ряда (ов?) пикселей со случайными цветами — не уверен, почему. Это было видно при увеличении нижней части изображения, но не видно на input .bmp.

ПРАВКА 2: При еще более тщательном рассмотрении выходного отраженного изображения линия «случайных» пикселей внизу, похоже, заканчивается ровно посередине. Кажется, что вся левая половина отраженного изображения на 1 пиксель больше, чем должно быть, а с правой половиной все в порядке. Линия, показывающая это четкое разделение по высоте, слабо видна, идущая вниз по центру изображения. Это начинает становиться забавным.

Комментарии:

1. Предположим, что вы впервые входите во внутренний цикл, j = 0. Вы устанавливаете значение coloursofaddress[width - 1] и затем немедленно используете coloursofaddress[0] , которое не было установлено ни на что конкретное.

2. Примечание: требуется только 1 строка, а не 3: coloursofaddress[width - 1 - j] = image[i][j];

3. Ваша идея выполнима (но не оптимально эффективна), и действительно, вам нужно повторить всю строку, а не только половину: вы заполняете строку буфера пикселями в строке изображения в обратном порядке, строка за строкой, и записываете ее обратно. Ваша единственная ошибка заключается в том, что вы должны сначала заполнить всю строку буфера и только потом скопировать ее обратно. Это будет достигнуто путем переноса шага 4 из внутреннего цикла в его собственный цикл. В нынешнем виде вы «почти переворачиваете» изображение, сдвинутое на половину строки, только случайно, как объяснил Герхардт.

4. «Но цвет пикселя должен в конечном итоге «исправиться» сам» Нет. Вы касаетесь каждого пикселя только один раз, поэтому, как только пикселю присваивается неправильное значение, он застревает там навсегда.

5. Полученное изображение очень похоже на желаемое отраженное изображение, но не идентично, потому что половина его сдвинута на одну линию развертки вниз. Чтобы понять, что происходит, я рекомендую вам попробовать запустить небольшое (скажем, 10×10) изображение через вашу программу и напечатать как исходные, так и целевые пиксели (в виде чисел), чтобы вы могли легко сравнить их.

Ответ №1:

У вас две проблемы.

На шаге 4 половине массива еще не было присвоено значение. Вы используете неопределенные значения для первой строки и старые значения для других строк. Это будет выглядеть как смещение половины изображения на 1 строку.

Для замены значений следует использовать стандартный механизм подкачки. Как упоминалось в комментариях, вам не нужно беспокоиться об отдельных полях, а просто поменять местами всю структуру:

 void reflect(int height, int width, RGBTRIPLE image[height][width])
{
    RGBTRIPLE temp;
    for (int i = 0; i < height; i  )
    {
        for (int j = 0; j < width; j  )
        {
            temp = image[i][j];
            image[i][width - 1 - j] = image[i][j];
            image[i][j] = temp;
        }
    }
    return;
}
  

Кроме того, если вы запустите замену всего диапазона, 0..width-1 вы в конечном итоге замените image[i][0] на image[i][width-1] и позже на image[i][width-1] с image[i][0] , которые дважды меняют местами одни и те же пиксели, что приводит к исходному изображению.

Просто запустите свой цикл с условием j < width/2 :

 // define a SWAP macro just for convenience during following explanation
#define SWAP(x,y)   
  do {              
    RGBTRIPLE temp = (x); 
    (x) = (y);      
    (y) = temp;     
  } while (0)


void reflect(int height, int width, RGBTRIPLE image[height][width])
{
    for (int i = 0; i < height; i  )
    {
        for (int j = 0; j < width/2; j  )
        {
            // Replace image[i] to make code shorter during explanation.
            RGBTRIPLE *row=image[i];
            SWAP(row[j], row[width - 1 - j]);
        }
    }
    return;
}
  

Я сделал 2 замены, которые на самом деле не требуются, но делают следующий текст намного короче.

Почему цикл выполняется только до j < width/2 ? Давайте посмотрим на объем памяти на каждой итерации, если мы хотим преобразовать массив целых чисел:

 width==15
row:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

j=0; SWAP(row[0],row[14]);
row:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     |14| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13| 0|
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

...

j=5; SWAP(row[5],row[9]);
row:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     |14|13|12|11|10| 9| 6| 7| 8| 5| 4| 3| 2| 1| 0|
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

j=6; SWAP(row[6],row[8]);
row:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     |14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

j=7; SWAP(row[7],row[7]);
row:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     |14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
  

Вы можете видеть, что после замены половины элементов мы закончили. Для нечетного числа элементов последний swap просто поменяется местами, не оказывая никакого эффекта.

Если вы теперь продолжите выполнение до width , вы снова переключитесь обратно:

 j=8; SWAP(row[8],row[6]);
row:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     |14|13|12|11|10| 9| 6| 7| 8| 5| 4| 3| 2| 1| 0|
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

...

j=13; SWAP(row[13],row[1]);
row:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     |14| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13| 0|
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

j=14; SWAP(row[14],row[0]);
row:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|
      -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
  

С этого мы и начали.

Очевидно, что мы запускаем SWAP(row[j],row[width-j-1]); и позже то же самое с замененным индексом: SWAP(row[width-j-1],row[j]); который делает то же самое.

При повторной замене восстанавливается исходное состояние, и ваше изображение не будет изменено.

Ответ №2:

Я решил проблему с помощью своего (не очень эффективного) кода! Нет необходимости в ширине / 2. Использовал временный массив и цикл for для замены цветов пикселей слева на правый! Я не совсем понимаю метод width / 2, как показано в других ответах, поэтому этот метод работает для меня, насколько я понимаю.

Комментарии:

1. Использование временной копии всего массива — ужасная идея, если для этого нет веской причины. Я предполагаю, что мое решение работает для вас, но вы не потратили достаточно времени, чтобы понять это. Правильно?

2. Спасибо, что нашли время объяснить мне это! Да, теперь это имеет смысл для меня и намного эффективнее, чем тот странный код, который я создал.