Cs50滤镜问题——反映。像素不改变值,不知道为什么 (C)

Cs50 filter problem - reflect. Pixels dont change values, not sure why (C)

我正在尝试通过保存列的左侧像素并将其替换为相对右侧的像素来实现反射过滤器。事情是我没有得到我预期的结果,图像保持不变。也许它没有进入 while 循环,但在我看来逻辑上它应该进入。

// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
    //int n = width;
    for(int i = 0; i < height; i++)
    {
        printf("%i\n",width);
        int j = 0;
        int left = 0 + j;
        int right = width - 1 - j;
        while (j != width )
        {
            int pixRed = image[i][left].rgbtRed;
            int pixGreen = image[i][left].rgbtGreen;
            int pixBlue = image[i][left].rgbtBlue;

            image[i][left].rgbtRed = image[i][right].rgbtRed;
            image[i][left].rgbtGreen = image[i][right].rgbtGreen;
            image[i][left].rgbtBlue = image[i][right].rgbtBlue;

            image[i][right].rgbtRed = pixRed;
            image[i][right].rgbtGreen = pixGreen;
            image[i][right].rgbtBlue = pixBlue;
            j++;
            printf("%i\n",j);
        }
        return;
    }

您发布的代码中有 不平衡 大括号。但是,我推测你的 return 是 [会] 在外循环的中间,所以你只会做一行。

您需要递增 left 并递减 right。实际上,您在内循环底部更改 j,但不要调整 left/right

不需要使用.whatever--我们可以使用整个RGBTRIPLE

您的索引逻辑有点复杂。

您正在做很多二维索引。由于数组有两个动态维度,这意味着在内循环中有 lot 的乘法运算。

指向行的单独指针可以简化代码并使其 运行 更快一些。


这是重构后的版本:

// Reflect image horizontally
void
reflect(int height, int width, RGBTRIPLE image[height][width])
{
    // int n = width;
    for (int i = 0; i < height; i++) {
        RGBTRIPLE *row = &image[i][0];

        int left = 0;
        int right = width - 1;
        for (;  left < right;  ++left, --right) {
            RGBTRIPLE tmp = row[left];
            row[left] = row[right];
            row[right] = tmp;
        }
    }
}

我们也可以让leftright成为指针:

// Reflect image horizontally
void
reflect(int height, int width, RGBTRIPLE image[height][width])
{
    // int n = width;
    for (int i = 0; i < height; i++) {
        RGBTRIPLE *left = &image[i][0];
        RGBTRIPLE *right = &left[width - 1];

        for (;  left < right;  ++left, --right) {
            RGBTRIPLE tmp = *left;
            *left = *right;
            *right = tmp;
        }
    }
}

更新:

I don't get what RGBTRIPLE *right = &left[width - 1]; means. the expression in the squared brackets is assigned as an index to &image? how does it take one index if its had 2 dimensions? – yonatan goldin 8 hours ago

这是二维数组中一维指针的基本访问。这是一个需要掌握的非常的重要概念。

考虑一个 WIDTH=8,HEIGHT=6 的数组。如果我们将其放在纸上,单元格索引将如下所示:

+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 2D  | 0,0 | 0,1 | 0,2 | 0,3 | 0,4 | 0,5 | 0,6 | 0,7 |
| 1D  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 2D  | 1,0 | 1,1 | 1,2 | 1,3 | 1,4 | 1,5 | 1,6 | 1,7 |
| 1D  |  8  |  9  | 10  | 11  | 12  | 13  | 14  | 15  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 2D  | 2,0 | 2,1 | 2,2 | 2,3 | 2,4 | 2,5 | 2,6 | 2,7 |
| 1D  | 16  | 17  | 18  | 19  | 20  | 21  | 22  | 23  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 2D  | 3,0 | 3,1 | 3,2 | 3,3 | 3,4 | 3,5 | 3,6 | 3,7 |
| 1D  | 24  | 25  | 26  | 27  | 28  | 29  | 30  | 31  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 2D  | 4,0 | 4,1 | 4,2 | 4,3 | 4,4 | 4,5 | 4,6 | 4,7 |
| 1D  | 32  | 33  | 34  | 35  | 36  | 37  | 38  | 39  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 2D  | 5,0 | 5,1 | 5,2 | 5,3 | 5,4 | 5,5 | 5,6 | 5,7 |
| 1D  | 40  | 41  | 42  | 43  | 44  | 45  | 46  | 47  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+

一些 definitions/code:

#define WIDTH   8
#define HEIGHT  6

// 2D array:
RGBTRIPLE d2[HEIGHT][WIDTH];

// pointer to access d2 as 1D array/vector:
RGBTRIPLE *d1 = &d2[0][0];

// pointer to cell from "2D" array:
RGBTRIPLE *p2;

// pointer to cell from "1D" array:
RGBTRIPLE *p1;

有时候,与其提及rowcolumnnumbers/indexes,不如谈论yx[=136=更容易]分别。

要从二维数组中获取单元格的地址,对于给定的 x,y 点,我们需要:

p2 = &d2[y][x];

要使用一维指针获得相同地址,我们需要:

p1 = &d1[(y * WIDTH) + x];

此时,p2p1 将具有 相同的 地址。

请注意,虽然计算机语言(如 C)了解[并支持] 二维数组,但[大多数] 计算机体系结构。因此,编译器必须采用类似于上述 p2 的语句,将其 [内部] 转换为类似于 p1 的语句,并从中生成代码。

对于点[1][0],要使用的线性索引是:(1 * WIDTH) + 0,即:(1 * 8) + 08.

对于点[1][WIDTH - 1],要使用的线性索引是:(1 * WIDTH) + (WIDTH - 1),即(1 * 8) + (8 - 1)15

所以,如果我们有:

left = &d2[1][0];
right = &d2[1][WIDTH - 1];

那么,我们还有:

left = &d1[8];
right = &d1[15];

但是,这也是事实:

right = &left[15 - 8];
right = &left[7];

在这里,7也是WIDTH - 1

如果您查看上面的内容 table,对于每个 2D 坐标,我们都有一个 1D 偏移量。同样,对于 [3][5],偏移量是:29

这也是:(3 * 8) + 5

我会把上面的table打印在纸上,然后用铅笔手工做一些计算,直到你理解如何使用二维和1D.

例如,在下面的代码中,all for 循环对 执行完全相同的 操作53=] 数组并以 相同的 顺序访问每个单独的单元格:

for (int y = 0;  y < HEIGHT;  ++y) {
    for (int x = 0;  x < WIDTH;  ++x)
        d2[y][x] += 23;
}

int end = HEIGHT * WIDTH;
for (int off = 0;  off < end;  ++off)
    d1[off] += 23;

RGBTRIPLE *p1 = &d2[0][0];

// NOTE: e1 and e2 are the same
RGBTRIPLE *e2 = &d2[HEIGHT][WIDTH];
RGBTRIPLE *e1 = &p1[HEIGHT * WIDTH];

for (;  p1 < e1;  ++p1)
    *p1 += 23;