Added tolerance option and result example with -t 5

This commit is contained in:
Phuntsok Drak-pa 2018-11-27 23:59:04 +01:00
parent 88e04466f3
commit 2633662c4b
No known key found for this signature in database
GPG Key ID: 9CB34B6827C66D22
4 changed files with 55 additions and 19 deletions

BIN
img/asterix5p.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -5,19 +5,39 @@
#include "compress.h" #include "compress.h"
/**
* \var uint32_t tolerance
* \brief Color tolerance
*
* Cette variable est la valeur du pourcentage de tolérance couleur lors de la
* création de nouvelles zones. Cette variable contient une valeur située entre
* 0 et 100 inclus.
*/
int32_t tolerance;
/** /**
* Cette fonction permet dévaluer si le pixel passé en argument est éligible à * Cette fonction permet dévaluer si le pixel passé en argument est éligible à
* la zone passée également en argument. * la zone passée également en argument. Si la \ref tolerance a pour valeur 0,
* alors les couleurs doivent être strictements identiques. Sinon, leur
* différence doit être inférieure à la tolérance de couleur.
* *
* \param[in] t_pixel Pointeur vers le pixel dont léligibilité est testée * \param[in] t_pixel Pointeur vers le pixel dont léligibilité est testée
* \param[in] t_zone Zone à laquelle le pixel est éligible ou non * \param[in] t_zone Zone à laquelle le pixel est éligible ou non
* \return Valeur booléenne, `1` si le pixel est éligible, `0` sinon * \return Valeur booléenne, `1` si le pixel est éligible, `0` sinon
*/ */
int32_t sameColor(Pixel *t_pixel, Zone *t_zone) { int32_t sameColor(Pixel *t_pixel, Zone *t_zone) {
return (t_pixel->red == t_zone->red && t_pixel->green == t_zone->green && int diff_red, diff_green, diff_blue;
t_pixel->blue == t_zone->blue) if (tolerance == 0) {
? 1 return (t_pixel->red == t_zone->red && t_pixel->green == t_zone->green &&
: 0; t_pixel->blue == t_zone->blue)
? 1
: 0;
}
diff_red = (abs((int32_t)t_zone->red - (int32_t)t_pixel->red) * 100) / 255;
diff_green =
(abs((int32_t)t_zone->green - (int32_t)t_pixel->green) * 100) / 255;
diff_blue = (abs((int32_t)t_zone->blue - (int32_t)t_pixel->blue) * 100) / 255;
return ((diff_red + diff_green + diff_blue) / 3) <= tolerance;
} }
/** /**
@ -172,14 +192,17 @@ void write_compressed_file(Image *t_img, FILE *t_output, darray *t_zones) {
* *
* \param[in] t_input_file Nom/chemin du fichier `.ppm` d'entrée * \param[in] t_input_file Nom/chemin du fichier `.ppm` d'entrée
* \param[in] t_output_file Nom/chemin du fichier `.su` de sortie * \param[in] t_output_file Nom/chemin du fichier `.su` de sortie
* \param[in] t_tolerance Pourcentage de tolérance de couleur
*/ */
void compress(const char *t_input_file, const char *t_output_file) { void compress(const char *t_input_file, const char *t_output_file,
int32_t t_tolerance) {
Image *img; Image *img;
darray *zones; darray *zones;
FILE *output_file; FILE *output_file;
if (!t_output_file) { if (!t_output_file) {
t_output_file = DEFAULT_COMPRESSED_NAME; t_output_file = DEFAULT_COMPRESSED_NAME;
} }
tolerance = t_tolerance;
img = newImage(); img = newImage();
imageLoadPPM(t_input_file, img); imageLoadPPM(t_input_file, img);
output_file = get_file(t_output_file, "wb"); output_file = get_file(t_output_file, "wb");

View File

@ -23,6 +23,7 @@ void write_segments(FILE *t_output, darray *t_segments);
/// Écrit les données compressées dans le fichier de sortie /// Écrit les données compressées dans le fichier de sortie
void write_compressed_file(Image *t_img, FILE *t_output, darray *t_zones); void write_compressed_file(Image *t_img, FILE *t_output, darray *t_zones);
/// Compresse l'image d'entrée /// Compresse l'image d'entrée
void compress(const char *t_input_file, const char *t_output_file); void compress(const char *t_input_file, const char *t_output_file,
int32_t tolerance);
#endif /* SRC_COMPRESS_H_ */ #endif /* SRC_COMPRESS_H_ */

View File

@ -24,18 +24,21 @@
*/ */
void help(int t_exit_code) { void help(int t_exit_code) {
puts("Usage:\n" puts("Usage:\n"
"surfaces-unies -i path [-o path] [-options]\n\n" "surfaces-unies -i path [-o path] [--options] [-t 0-100]\n\n"
"The default action is to compress the mandatory input image to a .su\n" "The default action is to compress the mandatory input image to a .su\n"
"file saved in the current directory.\n" "file saved in the current directory.\n"
"The input image MUST be saved in the ppm format.\n" "The input image MUST be saved in the ppm format.\n"
"Options available:\n" "Options available:\n"
"-h --help\n\tdisplay the current message\n" "-h --help\n\tDisplay the current message\n"
"-i --input\n\tpath to the input file (MANDATORY)\n" "-i --input\n\tPath to the input file (MANDATORY)\n"
"-o --output\n" "-o --output\n"
"\tpath to the output file (if the file already exists, it will be\n" "\tPath to the output file (if the file already exists, it will be\n"
"\toverwritten)\n" "\toverwritten)\n"
"-c --compress\n\tcompress the input file\n" "-t --tolerance\n"
"-u --uncompress\n\tuncompresses the input file to the output file."); "\tColor tolerance for lossy compression. By default at 0 for lossless\n"
"\tcompression, at 100 will consider every color to be the same.\n"
"-c --compress\n\tCompress the input file\n"
"-u --uncompress\n\tUncompresses the input file to the output file.");
exit(t_exit_code); exit(t_exit_code);
} }
@ -49,9 +52,10 @@ void help(int t_exit_code) {
* supplémentaires aux fonctions. * supplémentaires aux fonctions.
*/ */
struct Argres { struct Argres {
char *input; /*!< Nom du fichier d'entrée */ char *input; /*!< Nom du fichier d'entrée */
char *output; /*!< Nom du fichier de sortie */ char *output; /*!< Nom du fichier de sortie */
char compress; /*!< Le fichier d'entrée doit-il être compressé ? */ char compress; /*!< Le fichier d'entrée doit-il être compressé ? */
int32_t tolerance; /*!< Tolérance en pourcentage des différences de couleur */
}; };
typedef struct Argres Argres; typedef struct Argres Argres;
@ -72,6 +76,12 @@ void get_args(Argres *t_args, const int *const t_c) {
case 'o': (*t_args).output = optarg; break; case 'o': (*t_args).output = optarg; break;
case 'c': (*t_args).compress = 1; break; case 'c': (*t_args).compress = 1; break;
case 'u': (*t_args).compress = 0; break; case 'u': (*t_args).compress = 0; break;
case 't':
(*t_args).tolerance = atoi(optarg);
if(t_args->tolerance < 0 || t_args->tolerance > 100) {
help(ARGERROR);
}
break;
case '?': case '?':
default: help(ARGERROR); default: help(ARGERROR);
} }
@ -95,16 +105,18 @@ Argres process_args(const int t_argc, char *t_argv[]) {
res.input = NULL; res.input = NULL;
res.compress = 1; res.compress = 1;
res.output = NULL; res.output = NULL;
res.tolerance = 0;
while (1) { while (1) {
int option_index = 0; int option_index = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
{"input", required_argument, NULL, 'i'}, {"input", required_argument, NULL, 'i'},
{"output", required_argument, NULL, 'o'}, {"output", required_argument, NULL, 'o'},
{"tolerance", required_argument, NULL, 't'},
{"compress", no_argument, NULL, 'c'}, {"compress", no_argument, NULL, 'c'},
{"uncompress", no_argument, NULL, 'u'}, {"uncompress", no_argument, NULL, 'u'},
{NULL, 0, NULL, 0}}; {NULL, 0, NULL, 0}};
int c = getopt_long(t_argc, t_argv, "hi:o:cu", long_options, &option_index); int c = getopt_long(t_argc, t_argv, "hi:o:t:cu", long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
get_args(&res, &c); get_args(&res, &c);
@ -129,8 +141,8 @@ int main(int argc, char **argv) {
fprintf(stderr, "ERROR: no input file."); fprintf(stderr, "ERROR: no input file.");
help(ARGERROR); help(ARGERROR);
} }
if(argresults.compress) { if (argresults.compress) {
compress(argresults.input, argresults.output); compress(argresults.input, argresults.output, argresults.tolerance);
} else { } else {
uncompress(argresults.input, argresults.output); uncompress(argresults.input, argresults.output);
} }