CS50 Pset 4 edges: 图像是静态的

huangapple go评论81阅读模式
英文:

CS50 Pset 4 edges: Image is static

问题

#include "helpers.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int sq(int x)
{
    return x*x;
}

void edges(int height, int width, RGBTRIPLE image[height][width])
{
    int gx_square[] = {-1,0,1,-2,0,2,-1,0,1};
    int gy_square[] = {-1,-2,-1,0,0,0,1,2,1};
    int gx_red[height][width], gx_blue[height][width], gx_green[height][width];
    int gy_red[height][width], gy_blue[height][width], gy_green[height][width];
    int square = -1;

    for (int i = 0; i < height; i++)
    {
        // Width
        for (int j = 0; j < width; j++)
        {
            gx_red[i][j] = 0;
            gx_blue[i][j] = 0;
            gx_green[i][j] = 0;
            gy_red[i][j] = 0;
            gy_blue[i][j] = 0;
            gy_green[i][j] = 0;
        }
    }
    // Height
    for (int i = 0; i < height; i++)
    {
        // Width
        for (int j = 0; j < width; j++)
        {
            //Fill gx and gy arrays
            for (int column = i - 1; column < i + 2; column++)
            {
                for (int row = j - 1; row < j + 2; row++)
                {
                    //If pixel is outside image
                    square++;
                    if (column < 0 || column >= height || row < 0 || row >= width)
                    {
                            continue;
                    }
                    // Update array
                    gx_red[i][j] += image[column][row].rgbtRed * gx_square[square];
                    gx_blue[i][j] += image[column][row].rgbtBlue * gx_square[square];
                    gx_green[i][j] += image[column][row].rgbtGreen * gx_square[square];
                    gy_red[i][j] += image[column][row].rgbtRed * gy_square[square];
                    gy_blue[i][j] += image[column][row].rgbtBlue * gy_square[square];
                    gy_green[i][j] += image[column][row].rgbtGreen * gy_square[square];
                }
            }
            square = 0;
        }
    }

    // Height
    for (int i = 0; i < height; i++)
    {
        //Width
        for (int j = 0; j < width; j++)
        {
            // Return color
            image[i][j].rgbtRed = sqrt(sq(gx_red[i][j]) + sq(gy_red[i][j]));
            if (image[i][j].rgbtRed > 255)
            {
                image[i][j].rgbtRed = 255;
            }
            image[i][j].rgbtBlue = sqrt(sq(gx_blue[i][j]) + sq(gy_blue[i][j]));
            if (image[i][j].rgbtBlue > 255)
            {
                image[i][j].rgbtBlue = 255;
            }
            image[i][j].rgbtGreen = sqrt(sq(gx_green[i][j]) + sq(gy_green[i][j]));
            if (image[i][j].rgbtGreen > 255)
            {
                image[i][j].rgbtGreen= 255;
            }
        }
    }
    return;
}

这段代码是一个图像处理函数,用于在图像中描绘物体的轮廓。它通过对图像周围的像素进行处理,运行名为gx和gy的两个算法。这些算法通过将像素周围的所有像素乘以一些数字来工作。下面是乘法顺序的可视化表示(中间像素是当前像素):

对于gx,第一个像素乘以-1,下一个乘以0,然后是1,依此类推。然后我们将它们求和。但是乘法的对象不是像素本身,而是颜色值。让我们以红色为例。假设像素周围的所有像素(包括像素本身)都具有255的红色值。

由于像素之间的变化很小,所有值相互抵消,所有数字的和为0。

如果变化很大,我们得到一个大的数字(我很快会解释我们对这个数字做什么)。我们对绿色和蓝色也是如此。gx和gy的目的是检测x坐标和y坐标上的变化。

你可能会想,如果程序查看了图像之外的像素会怎么样。如果是这样,我们将它们视为0(与忽略它们相同)。

最后,我们对(gx.red的平方 + gy.red的平方)取平方根,以获得该像素的新红色值(对绿色和蓝色也是如此)。

我发布这个问题是因为我不太确定我的代码有什么问题。我有gx和gy数组来存储所有颜色值,以及实际乘法算法的数组。如果像素位于边界上,它会跳过它(与0相同)。然后,我将所有像素设置为gx颜色的平方加上gy颜色的平方的平方根。这是我通常使用的图像的样子:

这是使用我的代码的样子。

我想知道为什么会这样。

根据你的建议,它现在看起来像这样:

这是当前的代码:

void edges(int height, int width, RGBTRIPLE image[height][width])
{
    int gx_square[] = {-1,0,1,-2,0,2,-1,0,1};
    int gy_square[] = {-1,-2,-1,0,0,0,1,2,1};
    int gx_red[height][width], gx_blue[height][width], gx_green[height][width];
    int gy_red[height][width], gy_blue[height][width], gy_green[height][width];


    memset( gx_red, 0, sizeof gx_red ); // 使用库函数时尽量使用
    memset( gx_blue, 0, sizeof gx_blue );
    memset( gx_green, 0, sizeof gx_green );
    memset( gy_red, 0, sizeof gy_red );
    memset( gy_blue, 0, sizeof gy_blue );
    memset( gy_green, 0, sizeof gy_green );

    // Height
    for (size_t row = 0; row < height; row++)
    {
        // Width
        for (size_t col = 0; col < width; col++)
        {
            size_t square = 0;
            //Fill gx and gy arrays
            for (int r = row - 1; r < row + 2; r++)
            {
                for (int c = col - 1; c < col + 2; c++, square++)
                {
                    //If pixel is outside image
                    if ( ( 0 <= r && r < width) && ( 0 <= c && c < height ) )
                    {
                        // Update array
                        gx_red[row][col] += image[r][c].rgbtRed * gx_square[square];
                        gx_blue[row][col] += image[r][c].rgbtBlue * gx_square[square];
                        gx_green[row][col] += image[r][c].rgbtGreen * gx_square[square];
                        gy_red[row][col] += image[r][c].rgbtRed * gy_square[square];
                        gy_blue[row][col] += image[r][c].rgbtBlue * gy_square[square];
                        gy_green[row][col] += image[r][c].rgbtGreen * gy_square[square];
                    }
                }
            }
        }
    }

    //Width
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int clr;

            clr = sqrt(sq(gx_red[i][j]) + sq(gy_red[i][j]));
            image[i][j].rgbtRed = ( clr <= 255 ) ? clr : 255;

            clr = sqrt(sq(gx_blue[i][j]) + sq(gy_blue[i][j]));
            image[i][j].rgbtBlue = ( clr <= 255 ) ? clr : 255;

            clr = sqrt(sq(gx_green[i][j]) + sq(gy_green[i][j]));
            image[i][j].rgbtGreen = ( clr <= 255 ) ? clr : 255;
        }
    }
    return;
}

希望这可以帮助你解决问题。

英文:
#include &quot;helpers.h&quot;
#include &lt;math.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;stdio.h&gt;
int sq(int x)
{
return x*x;
}
void edges(int height, int width, RGBTRIPLE image[height][width])
{
int gx_square[] = {-1,0,1,-2,0,2,-1,0,1};
int gy_square[] = {-1,-2,-1,0,0,0,1,2,1};
int gx_red[height][width], gx_blue[height][width], gx_green[height][width];
int gy_red[height][width], gy_blue[height][width], gy_green[height][width];
int square = -1;
for (int i = 0; i &lt; height; i++)
{
// Width
for (int j = 0; j &lt; width; j++)
{
gx_red[i][j] = 0;
gx_blue[i][j] = 0;
gx_green[i][j] = 0;
gy_red[i][j] = 0;
gy_blue[i][j] = 0;
gy_green[i][j] = 0;
}
}
// Height
for (int i = 0; i &lt; height; i++)
{
// Width
for (int j = 0; j &lt; width; j++)
{
//Fill gx and gy arrays
for (int column = i - 1; column &lt; i + 2; column++)
{
for (int row = j - 1; row &lt; j + 2; row++)
{
//If pixel is outside image
square++;
if (column &lt; 0 || column &gt;= height || row &lt; 0 || row &gt;= width)
{
continue;
}
// Update array
gx_red[i][j] += image[column][row].rgbtRed * gx_square[square];
gx_blue[i][j] += image[column][row].rgbtBlue * gx_square[square];
gx_green[i][j] += image[column][row].rgbtGreen * gx_square[square];
gy_red[i][j] += image[column][row].rgbtRed * gy_square[square];
gy_blue[i][j] += image[column][row].rgbtBlue * gy_square[square];
gy_green[i][j] += image[column][row].rgbtGreen * gy_square[square];
}
}
square = 0;
}
}
// Height
for (int i = 0; i &lt; height; i++)
{
//Width
for (int j = 0; j &lt; width; j++)
{
// Return color
image[i][j].rgbtRed = sqrt(sq(gx_red[i][j]) + sq(gy_red[i][j]));
if (image[i][j].rgbtRed &gt; 255)
{
image[i][j].rgbtRed = 255;
}
image[i][j].rgbtBlue = sqrt(sq(gx_blue[i][j]) + sq(gy_blue[i][j]));
if (image[i][j].rgbtBlue &gt; 255)
{
image[i][j].rgbtBlue = 255;
}
image[i][j].rgbtGreen = sqrt(sq(gx_green[i][j]) + sq(gy_green[i][j]));
if (image[i][j].rgbtGreen &gt; 255)
{
image[i][j].rgbtGreen= 255;
}
}
}
return;
}
//./filter -e images/stadium.bmp OUTFILE.bmp
// debug50 filter -e images/stadium.bmp OUTFILE.bmp

My task in edges is to outline any object in an image like this:

CS50 Pset 4 edges: 图像是静态的

You do this by taking all the pixels color around an image, and running 2 algorithms through it called gx and gy. The algorithms work by mulitplying all the pixels around a pixel by a couple of numbers. Here's a visual representation of the multiplication order. (The middle pixel is the current pixel)

CS50 Pset 4 edges: 图像是静态的

So for gx, the first pixel gets multiplied by -1, the next by 0, then 1, and so on. And then we keep the sum. But it's not the pixel that's getting multipled, but the color value. Lets use Red as an example. Lets say all the pixels around a pixel (including the pixel itself) has 255 red.

CS50 Pset 4 edges: 图像是静态的

Since there's little change between the pixels, everything cancels out, and the sum of all the numbers is 0.

CS50 Pset 4 edges: 图像是静态的

If there's a big change, we're left with a big number. (I'll get to what we do with this number soon) And we do this with green and blue. The purpose of gx and gy is to detect changes on the x coordinates and on the y coordinates.
You also might be thinking, what if the program looked at pixels not inside the image. If that is the case, we just treat them as 0. (Which is the same thing as ignoring them)

Finally, we take the square root of (gx.red squared + gy.red squared) to get our new red value for that pixel. (You still do this with green and blue.)

I posted this question because I'm not really sure what's wrong with my code. I have gx and gy arrays that store all the color values, as well as arrays for the actual multiplication algorithm. If the pixel is at the border, it skips it (which is the same as 0). And then I make all the pixels equal to the square root of the square of the gx color + the square of the gy color. This is what the image I use normally looks like:

CS50 Pset 4 edges: 图像是静态的

And this is what it looks like with my code.

CS50 Pset 4 edges: 图像是静态的

I wonder why this is.

Okay with your suggestions, it looks like this:

CS50 Pset 4 edges: 图像是静态的

And this is the current code:

void edges(int height, int width, RGBTRIPLE image[height][width])
{
int gx_square[] = {-1,0,1,-2,0,2,-1,0,1};
int gy_square[] = {-1,-2,-1,0,0,0,1,2,1};
int gx_red[height][width], gx_blue[height][width], gx_green[height][width];
int gy_red[height][width], gy_blue[height][width], gy_green[height][width];
memset( gx_red, 0, sizeof gx_red ); // use library functions when possible.
memset( gx_blue, 0, sizeof gx_blue );
memset( gx_green, 0, sizeof gx_green );
memset( gy_red, 0, sizeof gy_red );
memset( gy_blue, 0, sizeof gy_blue );
memset( gy_green, 0, sizeof gy_green );
// Height
for (size_t row = 0; row &lt; height; row++)
{
// Width
for (size_t col = 0; col &lt; width; col++)
{
size_t square = 0;
//Fill gx and gy arrays
for (int r = row - 1; r &lt; row + 2; r++)
{
for (int c = col - 1; c &lt; col + 2; c++, square++)
{
//If pixel is outside image
if ( ( 0 &lt;= r &amp;&amp; r &lt; width) &amp;&amp; ( 0 &lt;= c &amp;&amp; c &lt; height ) )
{
// Update array
gx_red[row][col] += image[r][c].rgbtRed * gx_square[square];
gx_blue[row][col] += image[r][c].rgbtBlue * gx_square[square];
gx_green[row][col] += image[r][c].rgbtGreen * gx_square[square];
gy_red[row][col] += image[r][c].rgbtRed * gy_square[square];
gy_blue[row][col] += image[r][c].rgbtBlue * gy_square[square];
gy_green[row][col] += image[r][c].rgbtGreen * gy_square[square];
}
}
}
}
}
//Width
for (int i = 0; i &lt; height; i++)
{
for (int j = 0; j &lt; width; j++)
{
int clr;
clr = sqrt(sq(gx_red[i][j]) + sq(gy_red[i][j]));
image[i][j].rgbtRed = ( clr &lt;= 255 ) ? clr : 255;
clr = sqrt(sq(gx_blue[i][j]) + sq(gy_blue[i][j]));
image[i][j].rgbtBlue = ( clr &lt;= 255 ) ? clr : 255;
clr = sqrt(sq(gx_green[i][j]) + sq(gy_green[i][j]));
image[i][j].rgbtGreen = ( clr &lt;= 255 ) ? clr : 255;
}
}
return;
}
//./filter -e images/stadium.bmp OUTFILE.bmp
// debug50 filter -e images/stadium.bmp OUTFILE.bmp

答案1

得分: 2

索引变量square在声明时被初始化为-1,这是一个警告信号。以负值开始索引意味着可能出现问题。正确声明的话,任何索引都应该是size_t类型,即无符号数据类型。

这个bug还被变量的作用域所加剧。它的作用域应该限制在使用它的代码中。

下面是一个未经测试的修订版:

void edges(int height, int width, RGBTRIPLE image[height][width])
{
    int gx_square[] = {-1,0,1,-2,0,2,-1,0,1};
    int gy_square[] = {-1,-2,-1,0,0,0,1,2,1};
    int gx_red[height][width], gx_blue[height][width], gx_green[height][width];
    int gy_red[height][width], gy_blue[height][width], gy_green[height][width];


    memset( gx_red, 0, sizeof gx_red ); // 尽可能使用库函数
    memset( gx_blue, 0, sizeof gx_blue );
    memset( gx_green, 0, sizeof gx_green );
    memset( gy_red, 0, sizeof gy_red );
    memset( gy_blue, 0, sizeof gy_blue );
    memset( gy_green, 0, sizeof gy_green );

    // 高度
    for (size_t row = 0; row < height; row++)
    {
        // 宽度
        for (size_t col = 0; col < width; col++)
        {
            size_t square = 0; // 仅在此像素中使用的局部变量

            // 不要从行/列切换到列/行... 只会让人困惑!!!

            // 填充gx和gy数组
            for (int r = row - 1; r <= row + 1; r++)
            {
                // 每次迭代后,square递增。
                // 没有神秘的-1值...
                for (int c = col - 1; c <= col + 1; c++, square++)
                {
                    // 尽可能避免使用"continue;"。
                    // 使用"正向"逻辑代替。
                    if ( ( 0 <= r && r < height) && ( 0 <= c && c < width ) )
                    {
                        // 更新数组
                        gx_red[row][col] += image[r][c].rgbtRed * gx_square[square];
                        gx_blue[row][col] += image[r][c].rgbtBlue * gx_square[square];
                        gx_green[row][col] += image[r][c].rgbtGreen * gx_square[square];
                        gy_red[row][col] += image[r][c].rgbtRed * gy_square[square];
                        gy_blue[row][col] += image[r][c].rgbtBlue * gy_square[square];
                        gy_green[row][col] += image[r][c].rgbtGreen * gy_square[square];
                    }
                }
            }
        }
    }
    // 未更改的函数代码继续下面。

我不会深入研究,但你的版本从_row/col_切换到_col/row_,可能使用了Sobel数组中错误的乘数。也许你可以检查一下...


编辑(感谢@n.m.可能是一个ai)

很可能RGB三元组由单字节值(0-255)组成。在没有检查的情况下分配sqrt()的结果可能会导致这些值损坏,因为它们试图适应一个字节。

以下是对此进行更正的代码(同样未经测试):

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int clr;

            clr = sqrt(sq(gx_red[i][j]) + sq(gy_red[i][j]));
            image[i][j].rgbtRed = ( clr <= 255 ) ? clr : 255;

            clr = sqrt(sq(gx_blue[i][j]) + sq(gy_blue[i][j]));
            image[i][j].rgbtBlue = ( clr <= 255 ) ? clr : 255;

            clr = sqrt(sq(gx_green[i][j]) + sq(gy_green[i][j]));
            image[i][j].rgbtGreen = ( clr <= 255 ) ? clr : 255;
        }
    }
英文:

The index variable square is initialised to -1 when it is declared. This is a red flag. Starting an index with a negative value suggests something bad is happening. Properly declared, any index should be of type size_t, an unsigned datatype.

The bug is compounded by the scope of that variable. Its scope should be restricted to the only the code where it is used.

Here's an untested revision:

void edges(int height, int width, RGBTRIPLE image[height][width])
{
int gx_square[] = {-1,0,1,-2,0,2,-1,0,1};
int gy_square[] = {-1,-2,-1,0,0,0,1,2,1};
int gx_red[height][width], gx_blue[height][width], gx_green[height][width];
int gy_red[height][width], gy_blue[height][width], gy_green[height][width];
memset( gx_red, 0, sizeof gx_red ); // use library functions when possible.
memset( gx_blue, 0, sizeof gx_blue );
memset( gx_green, 0, sizeof gx_green );
memset( gy_red, 0, sizeof gy_red );
memset( gy_blue, 0, sizeof gy_blue );
memset( gy_green, 0, sizeof gy_green );
// Height
for (size_t row = 0; row &lt; height; row++)
{
// Width
for (size_t col = 0; col &lt; width; col++)
{
size_t square = 0; // local to this single pixel
// DO NOT switch from row/col to col/row... Just confusing!!!
//Fill gx and gy arrays
for (int r = row - 1; r &lt;= row + 1; r++)
{
// square increments AFTER each iteration.
// No mysterious -1 values...
for (int c = col - 1; c &lt;= col + 1; c++, square++)
{
// avoid &quot;continue;&quot; whenever possible.
// use &quot;positive&quot; logic instead.
if ( ( 0 &lt;= r &amp;&amp; r &lt; height) &amp;&amp; ( 0 &lt;= c &amp;&amp; c &lt; width ) )
{
// Update array
gx_red[row][col] += image[r][c].rgbtRed * gx_square[square];
gx_blue[row][col] += image[r][c].rgbtBlue * gx_square[square];
gx_green[row][col] += image[r][c].rgbtGreen * gx_square[square];
gy_red[row][col] += image[r][c].rgbtRed * gy_square[square];
gy_blue[row][col] += image[r][c].rgbtBlue * gy_square[square];
gy_green[row][col] += image[r][c].rgbtGreen * gy_square[square];
}
}
}
}
}
// unaltered function code continues below.

I'm not going to go down the rabbit hole, but it's also possible that your version, switching from row/col to col/row, might be using the wrong multipliers from the Sobel array. Maybe you will check that...

<hr/>

EDIT (with credit to @n.m. could be an ai)

It's likely that the RGB triplet is made up of single byte values (0-255). Assigning the result of sqrt() without checking may lead to corrupting those values trying to fit into a single byte.

The following corrects this (again, untested.)

	for (int i = 0; i &lt; height; i++)
{
for (int j = 0; j &lt; width; j++)
{
int clr;
clr = sqrt(sq(gx_red[i][j]) + sq(gy_red[i][j]));
image[i][j].rgbtRed = ( clr &lt;= 255 ) ? clr : 255;
clr = sqrt(sq(gx_blue[i][j]) + sq(gy_blue[i][j]));
image[i][j].rgbtBlue = ( clr &lt;= 255 ) ? clr : 255;
clr = sqrt(sq(gx_green[i][j]) + sq(gy_green[i][j]));
image[i][j].rgbtGreen = ( clr &lt;= 255 ) ? clr : 255;
}
}

huangapple
  • 本文由 发表于 2023年8月9日 05:13:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76863230.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定