Retour à mes projets

Projet en C

「Filtre Photo」

C・Filtre Photo

Contenu

Ce programme permet d'éditer une photo en applicant un filtre. Le nom et le chemin vers la photo ainsi que le type de filtre à utiliser est nécessaire en tant que paramètres.
Exemples: changer la couleur de la photo, appliquer un flou, et des effets miroir.

Langage: C
J'ai posté le code ci-dessous pour tester.

  1. Créez les fichiers filter.c, helpers.c, bmp.h et helpers.h dans le même dossier.
  2. Paramètres pour les effets de filtre :
    • -b (Blur - Flou)
    • -g (Grayscale - Niveaux de gris)
    • -r (Reflection - Mirroir)
    • -s (Sépia)
    • -e (Edge - Contour)
  3. Compiler.
  4. Exécutez le programme avec la commande plus bas. Le paramètre est réglé sur -g.

Comment utiliser

Exécutez la commande suivante :

./filter -g images/yard.bmp out.bmp

「Original」

Original Voir taille originale

「Grayscale」

Original Voir taille originale

「Reflection」

Original Voir taille originale

「Sepia」

Original Voir taille originale

「Blur blue_sum=green」

Original Voir taille originale

filter.c


#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>

#include "helpers.h"

int main(int argc, char *argv[])
{

    // Define allowable filters
    char *filters = "bgrs";

    // Get filter flag and check validity
    char filter = getopt(argc, argv, filters);
    if (filter == '?')
    {
        fprintf(stderr, "Invalid filter.\n");
        return 1;
    }

    // Ensure only one filter
    if (getopt(argc, argv, filters) != -1)
    {
        fprintf(stderr, "Only one filter allowed.\n");
        return 2;
    }

    // Ensure proper usage
    if (argc != optind + 2)
    {
        fprintf(stderr, "Usage: filter [flag] infile outfile\n");
        return 3;
    }

    // Remember filenames
    char *infile = argv[optind];
    char *outfile = argv[optind + 1];

    // Open input file
    FILE *inptr = fopen(infile, "r");
    if (inptr == NULL)
    {
        fprintf(stderr, "Could not open %s.\n", infile);
        return 4;
    }

    // Open output file
    FILE *outptr = fopen(outfile, "w");
    if (outptr == NULL)
    {
        fclose(inptr);
        fprintf(stderr, "Could not create %s.\n", outfile);
        return 5;
    }

    // Read infile's BITMAPFILEHEADER
    BITMAPFILEHEADER bf;
    fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);

    // Read infile's BITMAPINFOHEADER
    BITMAPINFOHEADER bi;
    fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);

    // Ensure infile is (likely) a 24-bit uncompressed BMP 4.0
    if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
        bi.biBitCount != 24 || bi.biCompression != 0)
    {
        fclose(outptr);
        fclose(inptr);
        fprintf(stderr, "Unsupported file format.\n");
        return 6;
    }

    int height = abs(bi.biHeight);
    int width = bi.biWidth;

    // Allocate memory for image
    RGBTRIPLE(*image)[width] = calloc(height, width * sizeof(RGBTRIPLE));
    if (image == NULL)
    {
        fprintf(stderr, "Not enough memory to store image.\n");
        fclose(outptr);
        fclose(inptr);
        return 7;
    }

    // Determine padding for scanlines
    int padding = (4 - (width * sizeof(RGBTRIPLE)) % 4) % 4;

    // Iterate over infile's scanlines
    for (int i = 0; i < height; i++)
    {
        // Read row into pixel array
        fread(image[i], sizeof(RGBTRIPLE), width, inptr);

        // Skip over padding
        fseek(inptr, padding, SEEK_CUR);
    }

    // Filter image
    switch (filter)
    {
        // Blur
        case 'b':
            blur(height, width, image);
            break;

        // Grayscale
        case 'g':
            grayscale(height, width, image);
            break;

        // Reflection
        case 'r':
            reflect(height, width, image);
            break;
        // Edges
        case 'e':
            edges(height, width, image);
            break;

        // Sepia
        case 's':
            sepia(height, width, image);
            break;
    }

    // Write outfile's BITMAPFILEHEADER
    fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);

    // Write outfile's BITMAPINFOHEADER
    fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);

    // Write new pixels to outfile
    for (int i = 0; i < height; i++)
    {
        // Write row to outfile
        fwrite(image[i], sizeof(RGBTRIPLE), width, outptr);

        // Write padding at end of row
        for (int k = 0; k < padding; k++)
        {
            fputc(0x00, outptr);
        }
    }

    // Free memory for image
    free(image);

    // Close infile
    fclose(inptr);

    // Close outfile
    fclose(outptr);

    return 0;
}


            

bmp.h


               
// BMP-related data types based on Microsoft's own
#include <stdint.h>

/**
 * Common Data Types
 *
 * The data types in this section are essentially aliases for C/C++
 * primitive data types.
 *
 * Adapted from http://msdn.microsoft.com/en-us/library/cc230309.aspx.
 * See http://en.wikipedia.org/wiki/Stdint.h for more on stdint.h.
 */
typedef uint8_t  BYTE;
typedef uint32_t DWORD;
typedef int32_t  LONG;
typedef uint16_t WORD;

/**
 * BITMAPFILEHEADER
 *
 * The BITMAPFILEHEADER structure contains information about the type, size,
 * and layout of a file that contains a DIB [device-independent bitmap].
 *
 * Adapted from http://msdn.microsoft.com/en-us/library/dd183374(VS.85).aspx.
 */
typedef struct
{
    WORD   bfType;
    DWORD  bfSize;
    WORD   bfReserved1;
    WORD   bfReserved2;
    DWORD  bfOffBits;
} __attribute__((__packed__))
BITMAPFILEHEADER;

/**
 * BITMAPINFOHEADER
 *
 * The BITMAPINFOHEADER structure contains information about the
 * dimensions and color format of a DIB [device-independent bitmap].
 *
 * Adapted from http://msdn.microsoft.com/en-us/library/dd183376(VS.85).aspx.
 */
typedef struct
{
    DWORD  biSize;
    LONG   biWidth;
    LONG   biHeight;
    WORD   biPlanes;
    WORD   biBitCount;
    DWORD  biCompression;
    DWORD  biSizeImage;
    LONG   biXPelsPerMeter;
    LONG   biYPelsPerMeter;
    DWORD  biClrUsed;
    DWORD  biClrImportant;
} __attribute__((__packed__))
BITMAPINFOHEADER;

/**
 * RGBTRIPLE
 *
 * This structure describes a color consisting of relative intensities of
 * red, green, and blue.
 *
 * Adapted from http://msdn.microsoft.com/en-us/library/aa922590.aspx.
 */
typedef struct
{
    BYTE  rgbtBlue;
    BYTE  rgbtGreen;
    BYTE  rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
              

helpers.h


#include "bmp.h"
// Convert image to grayscale
void grayscale(int height, int width, RGBTRIPLE image[height][width]);

// Convert image to sepia
void sepia(int height, int width, RGBTRIPLE image[height][width]);

// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width]);

// Detect edges
void edges(int height, int width, RGBTRIPLE image[height][width]);
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width]);
              

helpers.c


/*
description: Photo filters: Grayscale, blur, reflect and sepia
@file filter.c
@author Jeremy B.
@version 1.0 3 Nov 2020
*/

#include "helpers.h"
#include <math.h>

// Convert image to grayscale
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
    //2D arrays calculating color for each pixel and do an average of it to replace on the original
    //divide sum of colors value by 3.0 for average being rounded

    // row
    for (int i = 0; i < height; i++)
    {
        // column
        for (int j = 0; j < width; j++)
        {
            RGBTRIPLE pixel = image[i][j];
            int avg = round((pixel.rgbtRed + pixel.rgbtGreen + pixel.rgbtBlue) / 3.0);
            image[i][j].rgbtRed = image[i][j].rgbtGreen = image[i][j].rgbtBlue = avg;
        }
    }
    return;
}

// Convert image to sepia
void sepia(int height, int width, RGBTRIPLE image[height][width])
{
    //2D arrays calculating color for each pixel using cs50 - pset4 given formula for sepia pixel
    //pixel value must not go over 255 and should be capped at 255 if over, value must be rounded

    //row
    for (int i = 0; i < height; i++)
    {
        //column
        for (int j = 0; j < width; j++)
        {
            RGBTRIPLE pixel = image[i][j];

            int sepiaRed = round(.393 * pixel.rgbtRed + .769 * pixel.rgbtGreen + .189 * pixel.rgbtBlue);
            int sepiaGreen = round(.349 * pixel.rgbtRed + .686 * pixel.rgbtGreen + .168 * pixel.rgbtBlue);
            int sepiaBlue = round(.272 * pixel.rgbtRed + .534 * pixel.rgbtGreen + .131 * pixel.rgbtBlue);

            //if value over 255, capped at 255 and original value replacement
            if (sepiaRed > 255)
            {
                sepiaRed = 255;
                image[i][j].rgbtRed = sepiaRed;

            }
            else
            {
                image[i][j].rgbtRed = sepiaRed;
            }

            if (sepiaGreen > 255)
            {
                sepiaGreen = 255;
                image[i][j].rgbtGreen = sepiaGreen;
            }
            else
            {
                image[i][j].rgbtGreen = sepiaGreen;
            }

            if (sepiaBlue > 255)
            {
                sepiaBlue = 255;
                image[i][j].rgbtBlue = sepiaBlue;
            }

            else
            {
                image[i][j].rgbtBlue = sepiaBlue;
            }
        }
    }
    return;
}

// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
    //setting swap
    RGBTRIPLE swap[height][width];

    //2D array with an invert reading row and target pixel to swap
    for (int i = 0; i < height; i++)
    {
        //target pixel to swap at first position in a row
        int target = 0;

        //reverse reading, target current pixel
        for (int j = width - 1; j >= 0; j--, target++)
        {
            swap[i][target] = image[i][j];
        }
    }

    //swap loop
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            image[i][j] = swap[i][j];
        }
    }

    return;
}

// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
    //set swap color
    RGBTRIPLE swap[height][width];

    //image row
    for (int i = 0; i < height; i++)
    {
        //image column
        for (int j = 0; j < width; j++)
        {

            //set sum RGB
            float sum_red = 0;
            float sum_blue = 0;
            float sum_green = 0;

            //set counter
            int counter = 0;

            //set 2 area array, row and column for looking around current pixel
            int row_area[] = {i - 1, i, i + 1};
            int col_area[] = {j - 1, j, j + 1};

            //loop row area, up - current - down, around targeted pixel
            for (int row = 0; row < 3; row++)
            {
                //loop column area, left side - current - right side, around targeted pixel
                for (int col = 0; col < 3; col++)
                {
                    //set current
                    int row_current = row_area[row];
                    int col_current = col_area[col];

                    if (row_current >= 0 && row_current < height && col_current >= 0 && col_current < width)
                    {
                        RGBTRIPLE pixel = image[row_current][col_current];

                        //sum total of current pixel iterations (and around area) for each colors
                        sum_red += pixel.rgbtRed;
                        sum_green += pixel.rgbtGreen;
                        sum_blue += pixel.rgbtBlue;

                        //counter++
                        counter++;
                    }
                }
            }

            //determine value of total / count into swap
            swap[i][j].rgbtRed = round(sum_red / counter);
            swap[i][j].rgbtGreen = round(sum_green / counter);
            swap[i][j].rgbtBlue = round(sum_blue / counter);
        }
    }

    //swap loop
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            image[i][j] = swap[i][j];
        }
    }
    return;
}
// Detect edges
void edges(int height, int width, RGBTRIPLE image[height][width])
{
    //set swap
    RGBTRIPLE swap[height][width];
    //sobel operator algorithm
    //Gx kernel 3x3
    int Gx[3][3] =
    {
        {-1, 0, 1},
        {-2, 0, 2},
        {-1, 0, 1}
    };
    //Gy Kernel 3x3
    int Gy[3][3] =
    {
        {-1, -2, -1},
        {0, 0, 0},
        {1, 2, 1}
    };
    //image row
    for (int i = 0; i < height; i++)
    {
        //image column
        for (int j = 0; j < width; j++)
        {
            //set Gx RGB
            float Gx_red = 0;
            float Gx_blue = 0;
            float Gx_green = 0;
            //set Gy RGB
            float Gy_red = 0;
            float Gy_blue = 0;
            float Gy_green = 0;
            //set 2 area array, row and column for looking around current pixel
            int row_area[] = {i - 1, i, i + 1};
            int col_area[] = {j - 1, j, j + 1};
            //loop row area, up - current - down, around targeted pixel
            for (int row = 0; row < 3; row++)
            {
                //loop column area, left side - current - right side, around targeted pixel
                for (int col = 0; col < 3; col++)
                {
                    //set current
                    int row_current = row_area[row];
                    int col_current = col_area[col];
                    if (row_current >= 0 && row_current < height && col_current >= 0 && col_current < width)
                    {
                        RGBTRIPLE pixel = image[row_current][col_current];
                        //sum total of current pixel / position iterations (and around area) for each colors * kernels Gx and Gy positions
                        Gx_red += Gx[row][col] * pixel.rgbtRed;
                        Gx_green += Gx[row][col] * pixel.rgbtGreen;
                        Gx_blue += Gx[row][col] * pixel.rgbtBlue;
                        Gy_red += Gy[row][col] * pixel.rgbtRed;
                        Gy_green += Gy[row][col] * pixel.rgbtGreen;
                        Gy_blue += Gy[row][col] * pixel.rgbtBlue;
                    }
                }
            }
            //Gy and Gx square root caluculation
            int GxGyRed = round(sqrt((Gx_red * Gx_red) + (Gy_red * Gy_red)));
            int GxGyGreen = round(sqrt((Gx_green * Gx_green) + (Gy_green * Gy_green)));
            int GxGyBlue = round(sqrt((Gx_blue * Gx_blue) + (Gy_blue * Gy_blue)));
            //determine value to swap, if value over 255, value is capped at 255
            if (GxGyRed > 255)
            {
                GxGyRed = 255;
                swap[i][j].rgbtRed = GxGyRed;
            }
            else
            {
                swap[i][j].rgbtRed = GxGyRed;
            }
            if (GxGyGreen > 255)
            {
                GxGyGreen = 255;
                swap[i][j].rgbtGreen = GxGyGreen;
            }
            else
            {
                swap[i][j].rgbtGreen = GxGyGreen;
            }
            if (GxGyBlue > 255)
            {
                GxGyBlue = 255;
                swap[i][j].rgbtBlue = GxGyBlue;
            }
            else
            {
                swap[i][j].rgbtBlue = GxGyBlue;
            }
        }
    }
    //swap loop
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            image[i][j] = swap[i][j];
        }
    }
    return;
}