ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CS50 Week4: Problem Set, Filters
    Programming/CS50 2023. 7. 14. 20:53

    하버드 CS50 강의 4주차 Poblem Set 과제 Filters 의 풀이 과정을 다룹니다.
    C언어로 이차원 배열으로 표현된 이미지를 다루는 문제입니다.

    Intro

    문제 링크에서 자세한 내용을 확인할 수 있습니다.

    다양한 이미지 필터를 만드는 문제

    $ ./filter -r IMAGE.bmp REFLECTED.bmp

    Code

    다음 4가지 함수를 구현해야한다.

    • grayscale: 이미지를 흑백으로 변환
    • reflect: 이미지를 좌우 반전
    • blur: 이미지를 흐리게 만듬
    • edges: 이미지의 가장자리를 강조

    grayscale 함수

    RGB 값의 평균을 구해서 때려박는다.

    // Convert image to grayscale
    void grayscale(int height, int width, RGBTRIPLE image[height][width])
    {
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < height; j++)
            {
                int avg_brightness = round((image[i][j].rgbtBlue +
                                            image[i][j].rgbtGreen +
                                            image[i][j].rgbtRed) / 3.0);
    
                // Set all colors to the average brightness, thus making the image grayscale
                image[i][j].rgbtBlue = avg_brightness;
                image[i][j].rgbtGreen = avg_brightness;
                image[i][j].rgbtRed = avg_brightness;
            }
        }
        return;
    }

    reflect 함수

    절반을 기준으로 대응되는 값을 서로 바꾸어준다.
    홀수여도 괜찮다. 중간은 무시하면 된다.

    // Reflect image horizontally
    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++)
            {
                // Swap corresponding pixels from left and right sides of the image
                RGBTRIPLE temp = image[i][j];
                image[i][j] = image[i][width - j - 1];
                image[i][width - j - 1] = temp;
            }
        }
        return;
    }

    blur 함수

    각 픽셀을 기준으로 di, dj를 두어서 주변 픽셀을 순회하며 평균을 계산한다.
    바깥으로 벗어나는 경우를 주의해한다. continue를 사용해서 처리해주기로 한다.

    blur 함수는 주변 픽셀 값을 참조해서 평균을 계산한다.

    처리 해서 바꾼 픽셀을 input 이미지에 그대로 넣어버리면 다음 계산에 영향이 갈 수 있다.
    따라서 같은 크기의 배열 temp_image를 만들어서 계산이 다 끝난 이후에야 원본 이미지를 바꿔준다.

    // Blur image
    void blur(int height, int width, RGBTRIPLE image[height][width])
    {
        RGBTRIPLE temp_image[height][width];
    
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
                // Calculate color average of the 3x3 grid surrounding image[i][j] pixel
                int sum_red = 0;
                int sum_green = 0;
                int sum_blue = 0;
    
                int pixels_count = 0;
    
                for (int di = -1; di <= 1; di++)
                {
                    for (int dj = -1; dj <= 1; dj++)
                    {
                        if (i + di < 0 || i + di >= height ||  // Height out of bound
                            j + dj < 0 || j + dj >= width)     // Width out of bound
                        {
                            continue;
                        }
    
                        sum_red += image[i + di][j + dj].rgbtRed;
                        sum_green += image[i + di][j + dj].rgbtGreen;
                        sum_blue += image[i + di][j + dj].rgbtBlue;
                        pixels_count++;
                    }
                }
    
                temp_image[i][j].rgbtRed = round((float) sum_red / pixels_count);
                temp_image[i][j].rgbtGreen = round((float) sum_green / pixels_count);
                temp_image[i][j].rgbtBlue = round((float) sum_blue / pixels_count);
            }
        }
    
        // Copy temp_image to image
        for (int j = 0; j < width; j++)
        {
            for (int i = 0; i < height; i++)
            {
                image[i][j] = temp_image[i][j];
            }
        }
        return;
    }

    edges 함수

    복잡해 보이지만 지금까지 한 내용을 토대로 명세에 맞추어 구현하기만 하면 된다.

    // Detect edges
    void edges(int height, int width, RGBTRIPLE image[height][width])
    {
        RGBTRIPLE temp_image[height][width];
    
        // Sobel operator kernels
        int Gx_kernel[3][3] = {{-1, 0, 1},
                               {-2, 0, 2},
                               {-1, 0, 1}};
    
        int Gy_kernel[3][3] = {{-1, -2, -1},
                               { 0,  0,  0},
                               { 1,  2,  1}};
    
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
                int Gx_red = 0;
                int Gx_green = 0;
                int Gx_blue = 0;
                int Gy_red = 0;
                int Gy_green = 0;
                int Gy_blue = 0;
    
                // Calculate gradient for each color, using 3x3 Sobel operator kernels  
                for (int di = -1; di <= 1; di++)
                {
                    for (int dj = -1; dj <= 1; dj++)
                    {
                        if (i + di < 0 || i + di >= height ||  // Height out of bound
                            j + dj < 0 || j + dj >= width)     // Width out of bound
                        {
                            // Out of bound pixels are considered to be black(0x00 for each RGB)
                            continue;                        
                        }
                        else
                        {
    
                            Gx_red += image[i + di][j + dj].rgbtRed * Gx_kernel[di + 1][dj + 1];
                            Gx_green += image[i + di][j + dj].rgbtGreen * Gx_kernel[di + 1][dj + 1];
                            Gx_blue += image[i + di][j + dj].rgbtBlue * Gx_kernel[di + 1][dj + 1];
                            Gy_red += image[i + di][j + dj].rgbtRed * Gy_kernel[di + 1][dj + 1];
                            Gy_green += image[i + di][j + dj].rgbtGreen * Gy_kernel[di + 1][dj + 1];
                            Gy_blue += image[i + di][j + dj].rgbtBlue * Gy_kernel[di + 1][dj + 1];
                        }
                    }
                }
    
                // Calculate gradient magnitude, cap at 255
                temp_image[i][j].rgbtRed = fmin(round(sqrt(Gx_red * Gx_red + Gy_red * Gy_red)), 255);
                temp_image[i][j].rgbtBlue = fmin(round(sqrt(Gx_blue * Gx_blue + Gy_blue * Gy_blue)), 255);
                temp_image[i][j].rgbtGreen = fmin(round(sqrt(Gx_green * Gx_green + Gy_green * Gy_green)), 255);
            }
        }
    
        // Copy temp_image to image
        for (int j = 0; j < width; j++)
        {
            for (int i = 0; i < height; i++)
            {
                image[i][j] = temp_image[i][j];
            }
        }
        return;
    }

    댓글