Commit 8bf59fa1 authored by Dominik Widhalm's avatar Dominik Widhalm
Browse files

Added example solutions for task 6.12 (6.11 skipped for now)

parent c668cf51
/******************************************************************************
* C PROGRAMMING *
* BASIC EXERCISES - EXAMPLE SOLUTIONS *
* *
* Task_6.12: Image Comparison *
* Author: Dominik Widhalm *
* Email: dominik.widhalm@technikum-wien.at *
* Date: 2017-08-29 *
* *
******************************************************************************/
/***** INCLUDES ***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/***** MACROS *****************************************************************/
// Maximum width of the image
#define IMG_WIDTH_MAX 400
// Maximum height of the image
#define IMG_HEIGHT_MAX 200
// Select which kind of similarity calculation should be used (0..3D / 1..2D)
#define SIMILARITY 0
// Select if the similarity image (grayscale) should be exported
#define OUTPUT_IMAGE 1
/***** TYPEDEFS ***************************************************************/
/* Enumeration type for image file processing return status */
typedef enum {SUCCESS, NOACCESS, NOIMAGE, TOOLARGE, NOMEM} access_t;
/* Struct type for a pixel */
typedef struct pixel {
// red value
unsigned int R;
// green value
unsigned int G;
// blue value
unsigned int B;
} pixel_t;
/* Struct type for an RGB image */
typedef struct rgb_image {
// width
unsigned int width;
// height
unsigned int height;
// Start address of the pixel data
pixel_t *data;
} rgb_image_t;
/* Struct type for an RGB image */
typedef struct gray_image {
// width
unsigned int width;
// height
unsigned int height;
// Start address of the color data
unsigned int *data;
} gray_image_t;
/***** FUNCTION PROTOTYPES ****************************************************/
access_t image_read (rgb_image_t **img, char *file);
unsigned int pixel_compare (pixel_t *pix1, pixel_t *pix2);
gray_image_t *image_diff (rgb_image_t *img1, rgb_image_t *img2);
unsigned int image_get_similarity (gray_image_t *diff);
access_t image_write (gray_image_t *diff, char *file);
/***** LOCAL FUNCTIONS ********************************************************/
/******************************************************************************
* image_read *
* @brief Function to read in a PPM image. *
* *
* This function reads in a PPM image and stores the information in an *
* respective image structure containing the width and height as well as *
* the actual RGB values. *
* *
* @param img Pointer to the pointer of the image structure *
* @param file String with the filename of the image *
* @retval SUCCESS Image read successful *
* @retval NOACCESS Image read failed -- file cannot be accessed *
* @retval NOIMAGE Image read failed -- file is not an image *
* @retval TOOLARGE Image read failed -- given image is too large *
* @retval NOMEM Image read failed -- allocation error *
******************************************************************************/
access_t image_read (rgb_image_t **img, char *file) {
/* Temporary variables to read in data from file */
char buff[16];
int c;
unsigned int color_depth;
/* Try to open the given image file */
FILE *fp = fopen(file,"r");
/* Check if file can be opened */
if (fp == NULL) {
/* File could not be opened */
return NOACCESS;
}
/* Read in the first line */
if (!fgets(buff,sizeof(buff),fp)) {
/* File could not be read */
return NOACCESS;
}
/* Check if line contains "P3" (RGB color image in ASCII) */
if ((buff[0]!='P') || (buff[1]!='3')) {
/* Not a valid PPM image */
return NOIMAGE;
}
/* Allocate memory for the image data */
*img = (rgb_image_t*)malloc(sizeof(rgb_image_t));
/* Check if allocation was successful */
if (*img == NULL) {
/* Memory could not be allocated */
return NOMEM;
}
/* Read in the next character */
c = getc(fp);
/* Continue to read file content */
while(c=='#') {
/* Line is a comment, search for the end */
while(getc(fp) != '\n');
/* Read in the next character */
c = getc(fp);
/* Ignore all lines containing comments */
}
/* Set file handle back one position */
ungetc(c,fp);
/* Search for image dimension information */
if (fscanf(fp,"%u%*[ \n]%u",&(*img)->width,&(*img)->height) != 2) {
/* Not a valid PPM image */
return NOIMAGE;
}
/* Read in the color depth */
if (fscanf(fp,"%u",&color_depth) != 1) {
/* Not a valid PPM image */
return NOIMAGE;
}
/* Check the color depth */
if (color_depth != 255) {
/* Not a valid PPM image */
return NOIMAGE;
}
/* Read until the end of the line is reached */
while(fgetc(fp) != '\n');
/* Read in the next character */
c = getc(fp);
/* Continue to read file content */
while(c=='#') {
/* Line is a comment, search for the end */
while(getc(fp) != '\n');
/* Read in the next character */
c = getc(fp);
/* Ignore all lines containing comments */
}
/* Set file handle back one position */
ungetc(c,fp);
/* Calculate the number of pixels */
long unsigned int pixels = (*img)->width * (*img)->height;
/* Allocate memory for the pixel color information */
(*img)->data = (pixel_t*)malloc(pixels*sizeof(pixel_t));
/* Check if allocation was successful */
if ((*img)->data == NULL) {
/* Memory could not be allocated */
free(*img);
return NOMEM;
}
/* Get address of current pixel */
pixel_t *data = (*img)->data;
/* Read in the actual pixel color information */
for (long unsigned int i=0; i<pixels; i++) {
/* Read in the current red pixel */
if (fscanf(fp,"%u%*[ \n]%u%*[ \n]%u",&data->R,&data->G,&data->B) != 3) {
/* Not a valid PPM image */
free((*img)->data);
free(*img);
return NOIMAGE;
}
/* Go to the next pixel */
data++;
}
/* Close the opened image file */
fclose(fp);
/* Image reading was successful */
return SUCCESS;
}
/******************************************************************************
* pixel_compare *
* @brief Function to compare two given pixels. *
* *
* This function compares the RGB values of two pixels and returns a *
* similarity values of both in form of an integer value between 0 and *
* 255 (=8bit). The similarity is calculated as follows (color distance: *
* sqrt((pix2->R-pix1->R)^2+(pix2->G-pix1->G)^2+(pix2->B-pix1->B)^2) *
* Alternatively the difference can be calculated as the average *
* difference of all three colors (chosen by SIMILARITY): *
* (|pix2->R-pix1->R| + |pix2->G-pix1->G| + |pix2->B-pix1->B|) / 3 *
* *
* @param pix1 Pointer to the first pixel *
* @param pix2 Pointer to the second pixel *
* @return Similarity of the pixels (0-255) *
******************************************************************************/
unsigned int pixel_compare (pixel_t *pix1, pixel_t *pix2) {
/* Temporary variable to calculate the similarity */
unsigned int similar;
/* Check which calculation should be used */
if (SIMILARITY == 0) {
/* "3D" method -- 3-dimensional color distance */
similar = (pix2->R-pix1->R) * (pix2->R-pix1->R);
similar += (pix2->G-pix1->G) * (pix2->G-pix1->G);
similar += (pix2->B-pix1->B) * (pix2->B-pix1->B);
similar = (unsigned int)sqrt((double)similar);
} else {
/* "2D" method -- average color difference */
similar = (unsigned int)abs((int)pix2->R-(int)pix1->R);
similar += (unsigned int)abs((int)pix2->G-(int)pix1->G);
similar += (unsigned int)abs((int)pix2->B-(int)pix1->B);
similar = (unsigned int)((double)similar / 3.0);
}
/* Return the calculated similarity value */
return similar;
}
/******************************************************************************
* image_diff *
* @brief Function to calculate the difference between two images. *
* *
* This function compares two given images against each other and *
* calculates the "similarity" for each pixel. The resulting image *
* is a grayscale image showing the differences found. *
* *
* @param img1 Pointer to the first image *
* @param img2 Pointer to the second image *
* @return Pointer to the similarity image (grayscale) *
******************************************************************************/
gray_image_t *image_diff (rgb_image_t *img1, rgb_image_t *img2) {
/* Check if both images have the same dimensions */
if ((img1->width != img2->width) || (img1->height != img2->height)) {
/* Images have different dimensions */
return NULL;
}
/* Allocate memory for the similarity image */
gray_image_t* diff = (gray_image_t*)malloc(sizeof(gray_image_t));
/* Check if allocation was successful */
if (diff == NULL) {
/* Memory could not be allocated */
return NULL;
}
/* Set the dimensions of the image */
diff->width = img1->width;
diff->height = img1->height;
/* Calculate the number of pixels */
long unsigned int pixels = diff->width * diff->height;
/* Allocate memory for the pixel color information */
diff->data = (unsigned int*)malloc(pixels*sizeof(unsigned int));
/* Check if allocation was successful */
if (diff->data == NULL) {
/* Memory could not be allocated */
free(diff);
return NULL;
}
/* Helper pointer for pixel data */
pixel_t *pix1 = img1->data;
pixel_t *pix2 = img2->data;
unsigned int *diffpix = diff->data;
/* Compare both images pixel by pixel */
for (long unsigned int i=0; i<pixels; i++) {
/* Calculate similarity of current pixel */
*diffpix = pixel_compare(pix1,pix2);
/* Go to the next pixel */
diffpix++;
pix1++;
pix2++;
}
/* Return the address of the similarity image */
return diff;
}
/******************************************************************************
* image_get_similarity *
* @brief Function to calculate the average similarity. *
* *
* This function calculates the average similarity of a given similarity *
* image by obtaining the average value of all pixels. *
* *
* @param diff Pointer to the similarity image *
* @return Average similarity value (0-255) *
******************************************************************************/
unsigned int image_get_similarity (gray_image_t *diff) {
/* Temporary variable for the average similarity */
long unsigned int sum=0;
/* Temporary variable for the number of pixels */
long unsigned int pixels = diff->width * diff->height;
/* Iterate of the entire image */
for (long unsigned int i=0; i<pixels; i++) {
/* Add current similarity value to the sum */
sum += *((*diff).data);
/* Go to the next pixel */
(*diff).data++;
}
/* Calculate the average */
sum = (long unsigned int)((double)sum / (double)pixels);
/* Return the calculated average */
return (unsigned int)sum;
}
/******************************************************************************
* image_write *
* @brief Function to write the similarity image to a PPM file. *
* *
* This function writes the similarity grayscale image to a PPM file. *
* *
* @param diff Pointer to the similarity image structure *
* @param file String with the filename of the output image *
* @retval SUCCESS Image write successful *
* @retval NOACCESS Image write failed -- file cannot be accessed *
******************************************************************************/
access_t image_write (gray_image_t *diff, char *file) {
/* Try to open the given output file */
FILE *fp = fopen(file,"w");
/* Check if file can be opened */
if (fp == NULL) {
/* File could not be opened */
return NOACCESS;
}
/** Write the PPM file header **/
/* PPM ASCII image format (P3) */
fprintf(fp,"P3\n");
/* Write a comment to the file */
fprintf(fp,"# Similarity image\n");
/* Write the image dimensions */
fprintf(fp,"%d %d\n",diff->width,diff->height);
/* Write the color depth (8-bit = 255) */
fprintf(fp,"255\n");
/** Write the PPM color information **/
unsigned int *diffpix = diff->data;
/* Now output the actual color information */
for (long unsigned int i=0; i<(diff->width * diff->height); i++) {
/* Write the grayscale pixel (P6 is RGB, so write each value three times) */
fprintf(fp,"%d %d %d\n",*diffpix,*diffpix,*diffpix);
/* Go to the next pixel */
diffpix++;
}
/* Close the opened output file */
fclose(fp);
/* Return with success */
return SUCCESS;
}
/***** MAIN ROUTINE ***********************************************************/
int main (int argc, char *argv[]) {
/*** Local Variables ***/
rgb_image_t *image1 = NULL;
rgb_image_t *image2 = NULL;
gray_image_t *similar = NULL;
access_t retval;
unsigned int similarity;
/* Check if the appropriate number of arguments is given */
if (argc != 3) {
/* Number of arguments is invalid */
printf("\nInvalid number of arguments!\n");
printf("Usage: ./a.out image1.ppm image2.ppm\n");
} else {
/* (Try to) read in the first image */
retval = image_read(&image1,argv[1]);
/* Check if read was successful */
if (retval != SUCCESS) {
/* Check the return value */
switch(retval) {
case NOACCESS:
/* File could not be accessed */
printf("\nERROR: file 1 could not be accessed!\n");
break;
case NOIMAGE:
/* File is not an image */
printf("\nERROR: file 1 is not an image!\n");
break;
case TOOLARGE:
/* Image is too large */
printf("\nERROR: image 1 is too large (max %dx%d pixel!\n",IMG_WIDTH_MAX,IMG_HEIGHT_MAX);
break;
case NOMEM:
/* Memory could not be allocated */
printf("\nERROR: memory for image 1 could not be allocated!\n");
break;
default:
/* This statement should never be reached */
break;
}
} else {
/* (Try to) read in the second image */
retval = image_read(&image2,argv[2]);
/* Check if read was successful */
if (retval != SUCCESS) {
/* Check the return value */
switch(retval) {
case NOACCESS:
/* File could not be accessed */
printf("\nERROR: file 2 could not be accessed!\n");
break;
case NOIMAGE:
/* File is not an image */
printf("\nERROR: file 2 is not an image!\n");
break;
case TOOLARGE:
/* Image is too large */
printf("\nERROR: image 2 is too large (max %dx%d pixel!\n",IMG_WIDTH_MAX,IMG_HEIGHT_MAX);
break;
case NOMEM:
/* Memory could not be allocated */
printf("\nERROR: memory for image 2 could not be allocated!\n");
break;
default:
/* This statement should never be reached */
break;
}
} else {
/* Calculate the similarity image */
similar = image_diff(image1,image2);
/* Check if an image was successfully created */
if (similar == NULL) {
/* Similarity could not be obtained */
printf("\nERROR: similarity could be obtained!\n");
} else {
/* Check if the similarity image should be output */
if (OUTPUT_IMAGE == 1) {
/* (Try to) write the similarity image to a file */
if (image_write(similar,(char *)"diff.ppm") != SUCCESS) {
/* Output image could not be created */
printf("\nERROR: could not write output image\n");
}
}
/* Calculate the average similarity */
similarity = 100-(unsigned int)(((double)image_get_similarity(similar)) / 255.0 * 100.0);
/* Output the result */
printf("\nThe images have a similarity of %d%%!\n",similarity);
}
}
}
}
/* Notify the user about the termination of the program */
printf("\nThe program will now be terminated...\n");
return 0; /* Return with Success (0) */
}
/******************************************************************************/
# Get the current path(s) (relevant directory on last position)
TASKPATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
CHPATH := $(dir $(TASKPATH:%/=%))
# Variables for text substitution
empty:=
slash:= /
space:= $(empty) $(empty)
task:= task_
ch:= ch_
# Get TASK number
TASK := $(subst $(slash),$(space),$(TASKPATH))
TASK := $(lastword $(TASK))
TASK := $(subst $(task),$(empty),$(TASK))
# Get CH number
CH := $(subst $(slash),$(space),$(CHPATH))
CH := $(lastword $(CH))
CH := $(subst $(ch),$(empty),$(CH))
# Specify SRD directory
SRCDIR = .
# math.h is required for this example
INCS += -lm
# Call superior makefile
include ../../makefile
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment