【问题标题】:Cs50's Problem Set 4 - Filter Less - Reflection FunctionCs50的习题4——少过滤——反射函数
【发布时间】:2020-08-18 02:57:17
【问题描述】:

我已经编写了函数来反映 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 255\n255 0...", not "5 0 0\n255 0 0..."
:( reflect correctly filters 1x3 image
    expected "0 0 255\n0 255...", not "5 0 0\n0 255 0..."
:( reflect correctly filters image that is its own mirror image
    expected "255 0 0\n255 0...", not "5 0 0\n255 0 0..."
:( reflect correctly filters 3x3 image
    expected "70 80 90\n40 5...", not "5 0 0\n40 50 6..."
:( reflect correctly filters 4x4 image
    expected "100 110 120\n7...", not "5 0 0\n0 0 0\n..." 

编辑: 在仔细进一步检查上述代码输出的反射图像后,一切似乎都是完美的,除了一排带有随机颜色的薄薄的附加像素行 - 不知道为什么。放大图片底部可见,但在输入.bmp中不可见。

编辑 2: 在更仔细地检查反射图像输出后,底部的“杂散”像素线似乎在中间结束。反射图像的整个左半部分似乎比它应该的高 1 个像素单位,而右半部分很好。一条显示这种清晰高度划分的线从图片的中心向下隐约可见。这开始变得有趣了。

【问题讨论】:

  • 假设你第一次进入内循环,j=0。您将值设置为coloursofaddress[width - 1],然后立即使用coloursofaddress[0],它没有特别设置。
  • 注意:只需要1行,不需要3行:coloursofaddress[width - 1 - j] = image[i][j];
  • 你的想法是可行的(但不是最有效的),实际上你需要迭代整行,而不仅仅是一半:你用图像的一行中的像素填充像素的缓冲行,以相反的顺序,一行一行地写回去。您唯一的错误是您应该先填充整个缓冲区行,然后再将其复制回来。这将通过将步骤 4 从内部循环提升到它自己的循环来实现。就目前而言,正如 Gerhardt 所解释的那样,您“几乎反转”了图像,移动了半线,只是偶然。
  • "但像素颜色最终应该会自行“纠正”" 不。您只触摸每个像素一次,因此一旦将像素设置为错误值,它就会永远卡在那里。
  • 您得到的图像与所需的反射图像非常相似,但并不完全相同,因为它的一半向下移动了一个扫描线。为了了解发生了什么,我建议您尝试通过您的程序运行一个小(比如 10x10)图像,并 print 源像素和目标像素(作为数字),以便您可以轻松地比较两者.

标签: c filter cs50


【解决方案1】:

你有两个问题。

在第 4 步中,尚未为数组的一半赋值。您对第一行使用不确定的值,对其他行使用旧值。这看起来像是将图像的一半移动了 1 行。

您应该使用标准的交换机制来交换值。 正如在 cmets 中提到的,您无需费心处理单独的字段,只需交换整个结构即可:

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 &lt; 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 &lt; 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|
     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

你可以看到交换了一半的元素后,我们就完成了。对于奇数个元素,最后一次交换只会交换自身,没有任何影响。

如果你现在继续跑到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 方法,所以这种方法对我有用。

    【讨论】:

    • 如果没有充分的理由,使用整个数组的临时副本是一个糟糕的主意。我想,我的解决方案适合你,但你没有花足够的时间来理解它。对吗?
    • 感谢您花时间向我解释这一点!是的,它现在对我来说很有意义,并且比我编写的奇怪代码更有效。
    猜你喜欢
    • 1970-01-01
    • 2022-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    相关资源
    最近更新 更多