题目说明

题目出自 Harvard CS50x 课程第四周作业习题

目的是编写一个程序进行BMP图像处理

grayscale 灰度

我们把一个像素的红色、绿色、蓝色的值都设置为0x00则该像素为纯黑,把一个像素的红色、绿色、蓝色的值都设置为0xff则该像素为纯白,因此只要使红、绿、蓝三者数值相等,该像素的颜色就介于纯黑和纯白之间。

因此,我们只用使红、绿、蓝数值相等就可以得到灰度图像了,但是红、绿、蓝的具体数值怎么确定呢?为了确保新图像的每个像素仍然与旧图像的每个像素拥有相同的亮度或暗度,我们可以通过取原始红、绿、蓝数值的平均数来确定。

void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
    float avg;
    // 遍历图形中的每个像素
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            // 取红绿蓝的平均值
            avg = (image[i][j].rgbtBlue + image[i][j].rgbtRed + image[i][j].rgbtGreen) / 3.0;
            // 四舍五入后赋值给源像素
            image[i][j].rgbtBlue = (int) round(avg);
            image[i][j].rgbtRed = (int) round(avg);
            image[i][j].rgbtGreen = (int) round(avg);
        }
    }
    return;
}

reflect 翻转

为了实现反转图像我们仅需将左边的像素放到右边

观察图片可以猜想,像素左右对调后的坐标就等于宽 - 自己的横坐标 - 1

void reflect(int height, int width, RGBTRIPLE image[height][width])
{
    BYTE buffer;

    // 遍历图形中的每个像素
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width / 2; j++)
        {
            buffer = image[i][j].rgbtBlue;
            image[i][j].rgbtBlue = image[i][width - j - 1].rgbtBlue;
            image[i][width - j - 1].rgbtBlue = buffer;

            buffer = image[i][j].rgbtRed;
            image[i][j].rgbtRed = image[i][width - j - 1].rgbtRed;
            image[i][width - j - 1].rgbtRed = buffer;

            buffer = image[i][j].rgbtGreen;
            image[i][j].rgbtGreen = image[i][width - j - 1].rgbtGreen;
            image[i][width - j - 1].rgbtGreen = buffer;
        }
    }
    return;
}

blur 模糊

每个像素的新值是以像素为中心,画一个3x3 框,求框内像素值的平均值。以6为例,6的颜色值是1、2、3、5、6、7、9、10 、11 原始颜色值的平均值

对于边缘像素来说,则是以该像素为中心,画一个3x3 框,求在该框内且同时在图片中的像素值的平均值。以1为例,1的颜色值是2、5、6原始颜色值的平均值

因此我们可以推断出以任意一个像素画框,画框的范围不能超过图片宽度和高度。同时要注意不能使求平均值后的像素立刻赋值到原始图片中,不然这样下一个像素的周围像素值就会被改变。

void blur(int height, int width, RGBTRIPLE image[height][width])
{
    RGBTRIPLE tempImage[height][width];

    // 遍历图形中的每个像素
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            float tempRGB[3] = {0.0,0.0,0.0};
            float times = 0.0;

            //遍历该像素周围的像素
            for (int k = -1; k < 2; k++)
            {
                for (int l = -1; l < 2; l++)
                {
                    //进行框是否在图片内的判断
                    if (i + k >= 0 && j + l >= 0 && i + k < height && j + l < width)
                    {
                        tempRGB[0] += image[i + k][j + l].rgbtRed;
                        tempRGB[1] += image[i + k][j + l].rgbtGreen;
                        tempRGB[2] += image[i + k][j + l].rgbtBlue;
                        times += 1.0;
                    }
                }
            }

            //对该像素周围像素的值求平均值
            for (int m = 0; m < 3; m++)
            {
                tempRGB[m] /= times;
            }

            //将模糊化后的像素值存入temp图片中
            tempImage[i][j].rgbtRed = (int) round(tempRGB[0]);
            tempImage[i][j].rgbtGreen = (int) round(tempRGB[1]);
            tempImage[i][j].rgbtBlue = (int) round(tempRGB[2]);
        }
    }

    //将temp图片值复制给原图片
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            image[i][j].rgbtRed = tempImage[i][j].rgbtRed;
            image[i][j].rgbtGreen = tempImage[i][j].rgbtGreen;
            image[i][j].rgbtBlue = tempImage[i][j].rgbtBlue;
        }

    }

    return;
}

Edges 识别边缘