1 /*****************************************************************************
2 * This file is part of gfxprim library. *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
19 * Copyright (C) 2009-2012 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
32 #include "histogram.h"
34 static GP_ProgressCallback
*progress_callback
= NULL
;
36 static const char *progress_prefix
= NULL
;
38 static int show_progress(GP_ProgressCallback
*self
)
40 fprintf(stderr
, "\r%s %3.2f%%",
41 progress_prefix
, self
->percentage
);
46 static int param_err(const struct param
*self
, const char *val
, void *priv
)
48 /* invalid parameter name */
50 fprintf(stderr
, "ERROR: %s: Unknown parameter name '%s'\n",
55 /* just regular error */
56 fprintf(stderr
, "ERROR: %s: Invalid %s parameter value %s = '%s'",
57 (char *)priv
, param_type_name(self
->type
), self
->name
, val
);
59 if (self
->type
== PARAM_ENUM
) {
62 fprintf(stderr
, " is not in [");
64 for (i
= 0; self
->enum_table
[i
] != NULL
; i
++)
65 if (self
->enum_table
[i
+1] == NULL
)
66 fprintf(stderr
, "'%s']", self
->enum_table
[i
]);
68 fprintf(stderr
, "'%s' | ", self
->enum_table
[i
]);
71 fprintf(stderr
, "\n");
76 static void print_error(const char *error
)
78 fprintf(stderr
, "ERROR: %s\n", error
);
83 static const char *resize_algs
[] = {
92 static int resize_check_ratio(const struct param
*self
__attribute__((unused
)),
93 void *val
, int count
__attribute__((unused
)))
95 float f
= *((float*)val
);
103 static struct param resize_params
[] = {
104 {"alg", PARAM_ENUM
, "algorithm to be used", resize_algs
, NULL
},
105 {"ratio", PARAM_FLOAT
, "scale ratio", NULL
, resize_check_ratio
},
106 {NULL
, 0, NULL
, NULL
, NULL
}
109 static int resize(GP_Pixmap
**c
, const char *params
)
114 if (param_parse(params
, resize_params
, "resize", param_err
,
119 print_error("resize: ratio parameter is missing");
123 GP_Size w
= ratio
* (*c
)->w
;
124 GP_Size h
= ratio
* (*c
)->h
;
125 GP_Pixmap
*res
= NULL
;
127 res
= GP_FilterResizeAlloc(*c
, w
, h
, alg
, progress_callback
);
140 static const char *scale_algs
[] = {
149 static int scale_check_size(const struct param
*self
__attribute__((unused
)),
150 void *val
, int count
__attribute__((unused
)))
152 int i
= *((int*)val
);
160 static struct param scale_params
[] = {
161 {"alg", PARAM_ENUM
, "algorithm to be used", scale_algs
, NULL
},
162 {"w", PARAM_INT
, "new width (only width may be passed)", NULL
, scale_check_size
},
163 {"h", PARAM_INT
, "new height (only height may be passed)", NULL
, scale_check_size
},
164 {NULL
, 0, NULL
, NULL
, NULL
}
167 static int scale(GP_Pixmap
**c
, const char *params
)
173 if (param_parse(params
, scale_params
, "scale", param_err
,
177 if (w
== -1 && h
== -1) {
178 print_error("scale: w and/or h missing");
183 w
= (*c
)->w
* (1.00 * h
/(*c
)->h
) + 0.5;
186 h
= (*c
)->h
* (1.00 * w
/(*c
)->w
) + 0.5;
188 GP_Pixmap
*res
= NULL
;
190 res
= GP_FilterResizeAlloc(*c
, w
, h
, alg
, progress_callback
);
203 static const char *rotate_rots
[] = {
209 static struct param rotate_params
[] = {
210 {"rot", PARAM_ENUM
, "image rotation", rotate_rots
, NULL
},
211 {NULL
, 0, NULL
, NULL
, NULL
}
214 static int rotate(GP_Pixmap
**c
, const char *params
)
218 if (param_parse(params
, rotate_params
, "rotate", param_err
, &rot
))
222 print_error("rotate: rot parameter is missing");
226 GP_Pixmap
*res
= NULL
;
230 res
= GP_FilterRotate90Alloc(*c
, progress_callback
);
233 res
= GP_FilterRotate180Alloc(*c
, progress_callback
);
236 res
= GP_FilterRotate270Alloc(*c
, progress_callback
);
251 static struct param mirror_params
[] = {
252 {"vert", PARAM_BOOL
, "mirror vertically", NULL
, NULL
},
253 {"horiz", PARAM_BOOL
, "mirror horizontally", NULL
, NULL
},
254 {NULL
, 0, NULL
, NULL
, NULL
}
257 static int mirror(GP_Pixmap
**c
, const char *params
)
259 int vert
= 0, horiz
= 0;
261 if (param_parse(params
, mirror_params
, "mirror", param_err
, &vert
, &horiz
))
265 GP_FilterMirrorV(*c
, *c
, progress_callback
);
268 GP_FilterMirrorH(*c
, *c
, progress_callback
);
273 /* brightness filter */
275 static struct param bright_params
[] = {
276 {"inc", PARAM_FLOAT
, "brightness increment", NULL
, NULL
},
277 {NULL
, 0, NULL
, NULL
, NULL
}
280 static int bright(GP_Pixmap
**c
, const char *params
)
284 if (param_parse(params
, bright_params
, "bright", param_err
, &bright
))
287 GP_FilterBrightness(*c
, *c
, bright
, progress_callback
);
294 static struct param contrast_params
[] = {
295 {"mul", PARAM_FLOAT
, "contrast (1.5 = +50%, 0.5 = -50%)", NULL
, NULL
},
296 {NULL
, 0, NULL
, NULL
, NULL
}
299 static int contrast(GP_Pixmap
**c
, const char *params
)
303 if (param_parse(params
, contrast_params
, "contrast", param_err
, &mul
))
307 print_error("contrast: mul parameter must be >= 0");
311 GP_FilterContrast(*c
, *c
, mul
, progress_callback
);
318 static struct param invert_params
[] = {
319 {NULL
, 0, NULL
, NULL
, NULL
}
322 static int invert(GP_Pixmap
**c
, const char *params
)
324 if (param_parse(params
, invert_params
, "invert", param_err
))
327 GP_FilterInvert(*c
, *c
, progress_callback
);
334 static struct param blur_params
[] = {
335 {"sigma", PARAM_FLOAT
, "sigma parameter, radii of blur (sets both)", NULL
, NULL
},
336 {"sigma_x", PARAM_FLOAT
, "sigma parameter for horizontal direction", NULL
, NULL
},
337 {"sigma_y", PARAM_FLOAT
, "sigma parameter for vertical direction", NULL
, NULL
},
338 {NULL
, 0, NULL
, NULL
, NULL
}
341 static int blur(GP_Pixmap
**c
, const char *params
)
347 if (param_parse(params
, blur_params
, "blur", param_err
, &sigma
, &sigma_x
, &sigma_y
))
355 if (sigma_x
<= 0 && sigma_y
<= 0) {
356 print_error("blur: at least one of sigma_x and sigma_y must be >= 0");
360 GP_FilterGaussianBlur(*c
, *c
, sigma_x
, sigma_y
, progress_callback
);
367 //TODO: this should be generated
368 static const char *dither_formats
[] = {
378 static const GP_PixelType dither_pixel_types
[] = {
387 static struct param dither_params
[] = {
388 {"format", PARAM_ENUM
, "pixel type to be used", dither_formats
, NULL
},
389 {NULL
, 0, NULL
, NULL
, NULL
}
392 static int dither(GP_Pixmap
**c
, const char *params
)
396 if (param_parse(params
, dither_params
, "dither", param_err
, &fmt
))
400 print_error("dither: invalid format or format param missing");
405 bw
= GP_FilterFloydSteinbergAlloc(*c
, dither_pixel_types
[fmt
],
408 //TODO: so far we convert the pixmap back to RGB888
409 //(so we can do further work with it)
410 GP_Blit(bw
, 0, 0, GP_PixmapW(bw
), GP_PixmapH(bw
), *c
, 0, 0);
417 /* jpg save filter */
419 static struct param save_jpg_params
[] = {
420 {"file", PARAM_STR
, "Filename to save the result", NULL
, NULL
},
421 {NULL
, 0, NULL
, NULL
, NULL
}
424 static int save_jpg(GP_Pixmap
**c
, const char *params
)
428 if (param_parse(params
, save_jpg_params
, "jpg", param_err
, &file
))
432 print_error("jpg: filename missing");
436 GP_SaveJPG(*c
, file
, progress_callback
);
441 /* png save filter */
443 static struct param save_png_params
[] = {
444 {"file", PARAM_STR
, "Filename to save the result", NULL
, NULL
},
445 {NULL
, 0, NULL
, NULL
, NULL
}
448 static int save_png(GP_Pixmap
**c
, const char *params
)
452 if (param_parse(params
, save_png_params
, "png", param_err
, &file
))
456 print_error("png: filename missing");
460 GP_SavePNG(*c
, file
, progress_callback
);
467 static struct param median_params
[] = {
468 {"radius", PARAM_INT
, "median radius for both x and y", NULL
, NULL
},
469 {"radius_x", PARAM_INT
, "median radius for x", NULL
, NULL
},
470 {"radius_y", PARAM_INT
, "median radius for y", NULL
, NULL
},
471 {NULL
, 0, NULL
, NULL
, NULL
}
474 static int median(GP_Pixmap
**c
, const char *params
)
476 int rad
= -1, rad_x
, rad_y
;
478 if (param_parse(params
, median_params
, "median", param_err
, &rad
, &rad_x
, &rad_y
))
486 if (rad_x
< 0 || rad_y
< 0)
489 GP_Pixmap
*ret
= GP_FilterMedianAlloc(*c
, rad_x
, rad_y
, progress_callback
);
500 /* sigma mean filter */
502 static struct param sigma_mean_params
[] = {
503 {"radius", PARAM_INT
, "median radius for both x and y", NULL
, NULL
},
504 {"min", PARAM_INT
, "minimal number of pixels to use for the mean", NULL
, NULL
},
505 {"sigma", PARAM_FLOAT
, "sigma scaled to [0,1] interval", NULL
, NULL
},
506 {"radius_x", PARAM_INT
, "median radius for x", NULL
, NULL
},
507 {"radius_y", PARAM_INT
, "median radius for y", NULL
, NULL
},
508 {NULL
, 0, NULL
, NULL
, NULL
}
511 static int sigma_mean(GP_Pixmap
**c
, const char *params
)
513 int rad
= -1, rad_x
, rad_y
, min
= 0;
516 if (param_parse(params
, sigma_mean_params
, "sigma", param_err
,
517 &rad
, &min
, &sigma
, &rad_x
, &rad_y
))
525 if (rad_x
< 0 || rad_y
< 0)
528 (*c
)->gamma
= GP_GammaAcquire((*c
)->pixel_type
, 1.2);
530 GP_Pixmap
*ret
= GP_FilterSigmaAlloc(*c
, rad_x
, rad_y
, min
, sigma
, progress_callback
);
541 /* laplacian edge sharpening filter */
543 static struct param sharpen_params
[] = {
544 {"weight", PARAM_FLOAT
, "sharpening weight from [0,1] interval", NULL
, NULL
},
545 {NULL
, 0, NULL
, NULL
, NULL
}
548 static int sharpen(GP_Pixmap
**c
, const char *params
)
552 if (param_parse(params
, sharpen_params
, "sigma", param_err
, &weight
))
555 GP_Pixmap
*ret
= GP_FilterEdgeSharpeningAlloc(*c
, weight
, progress_callback
);
566 /* gaussian additive noise filter */
568 static struct param gauss_noise_params
[] = {
569 {"sigma", PARAM_FLOAT
, "sigma: amount of noise between [0,1]", NULL
, NULL
},
570 {"mu", PARAM_FLOAT
, "mu: offset of noise between [0,1]", NULL
, NULL
},
571 {NULL
, 0, NULL
, NULL
, NULL
}
574 static int gauss_noise(GP_Pixmap
**c
, const char *params
)
579 if (param_parse(params
, gauss_noise_params
, "gaussian noise", param_err
, &sigma
, &mu
))
582 GP_FilterGaussianNoiseAdd(*c
, *c
, sigma
, mu
, progress_callback
);
589 static const char *arithmetic_ops
[] = {
598 static struct param arithmetic_params
[] = {
599 {"file", PARAM_STR
, "Filename of image to use.", NULL
, NULL
},
600 {"op", PARAM_ENUM
, "Arithmetic peration", arithmetic_ops
, NULL
},
601 {NULL
, 0, NULL
, NULL
, NULL
}
604 static int arithmetic(GP_Pixmap
**c
, const char *params
)
609 if (param_parse(params
, arithmetic_params
, "arithmetic", param_err
, &file
, &op
))
613 print_error("arithmetic: Filename missing");
617 GP_Pixmap
*img
, *res
= NULL
;
619 if ((img
= GP_LoadImage(file
, progress_callback
)) == NULL
) {
620 print_error("arithmetic: Invalid image.");
626 res
= GP_FilterDifferenceAlloc(*c
, img
, progress_callback
);
629 res
= GP_FilterAdditionAlloc(*c
, img
, progress_callback
);
632 res
= GP_FilterMultiplyAlloc(*c
, img
, progress_callback
);
635 res
= GP_FilterMinAlloc(*c
, img
, progress_callback
);
638 res
= GP_FilterMaxAlloc(*c
, img
, progress_callback
);
654 static struct param histogram_params
[] = {
655 {"file", PARAM_STR
, "Filename of image to use.", NULL
, NULL
},
656 {NULL
, 0, NULL
, NULL
, NULL
}
659 static int histogram(GP_Pixmap
**c
, const char *params
)
661 char *file
= "histogram.png";
663 if (param_parse(params
, histogram_params
, "histogram", param_err
, &file
))
667 print_error("histogram: Filename missing");
671 histogram_to_png(*c
, file
);
680 struct param
*param_desc
;
681 int (*apply
)(GP_Pixmap
**c
, const char *params
);
684 static struct filter filter_table
[] = {
685 {"rotate", "rotate image", rotate_params
, rotate
},
686 {"mirror", "mirror vertically/horizontally", mirror_params
, mirror
},
687 {"scale", "scale image to given width and height", scale_params
, scale
},
688 {"resize", "resize image by given ratio", resize_params
, resize
},
689 {"bright", "alter image brightness", bright_params
, bright
},
690 {"contrast", "alter image contrast", contrast_params
, contrast
},
691 {"invert", "inverts image", invert_params
, invert
},
692 {"blur", "gaussian blur", blur_params
, blur
},
693 {"dither", "dithers bitmap", dither_params
, dither
},
694 {"arithmetic", "arithmetic operation", arithmetic_params
, arithmetic
},
695 {"histogram", "save histogram into image file", histogram_params
, histogram
},
696 {"median", "median filter", median_params
, median
},
697 {"sigma", "sigma (mean) filter", sigma_mean_params
, sigma_mean
},
698 {"sharpen", "laplacian edge sharpening", sharpen_params
, sharpen
},
699 {"gauss_noise", "additive gaussian (normaly distributed) noise", gauss_noise_params
, gauss_noise
},
700 {"jpg", "save jpg image", save_jpg_params
, save_jpg
},
701 {"png", "save png image", save_png_params
, save_png
},
702 {NULL
, NULL
, NULL
, NULL
}
705 static struct filter
*get_filter(const char *name
)
709 for (i
= 0; filter_table
[i
].name
!= NULL
; i
++) {
710 if (!strcasecmp(filter_table
[i
].name
, name
))
711 return &filter_table
[i
];
717 static void print_filter_help(void)
721 for (i
= 0; filter_table
[i
].name
!= NULL
; i
++) {
722 printf("%s\n", filter_table
[i
].name
);
724 j
= strlen(filter_table
[i
].name
);
730 printf("* %s\n", filter_table
[i
].desc
);
733 param_describe(filter_table
[i
].param_desc
, " ");
740 #define FILTERS_MAX 255
742 static const char *filter_params
[FILTERS_MAX
];
743 static const struct filter
*filters
[FILTERS_MAX
];
744 static unsigned int filter_cnt
= 0;
746 static void add_filter(char *params
)
748 if (filter_cnt
>= FILTERS_MAX
) {
749 fprintf(stderr
, "Maximal number of filters exceeded (%u), "
750 "increase and recompile.", FILTERS_MAX
);
754 const char *name
= strsep(¶ms
, ":");
756 filters
[filter_cnt
] = get_filter(name
);
758 if (filters
[filter_cnt
] == NULL
) {
759 fprintf(stderr
, "Invalid filter name '%s'\n", name
);
763 filter_params
[filter_cnt
++] = params
;
766 static void apply_filters(GP_Pixmap
**src
)
771 for (i
= 0; i
< filter_cnt
; i
++) {
774 snprintf(buf
, sizeof(buf
), "Filter %s", filters
[i
]->name
);
776 progress_prefix
= buf
;
778 if ((ret
= filters
[i
]->apply(src
, filter_params
[i
]))) {
779 fprintf(stderr
, "Error: %s\n", strerror(ret
));
783 if (progress_callback
!= NULL
)
784 fprintf(stderr
, " done\n");
788 static const char *app_help
= {
790 " <<<<<<<<<< Bitmap Grinder >>>>>>>>>>> \n"
794 " +-{ D| |010101011. \n"
795 " | \\| | +-.0100101. \n"
796 " O=+ +~+-----+ .10110101. \n"
800 " Program options \n"
801 " =============== \n"
803 "-h - prints this help \n"
804 "-p - show filter progress \n"
805 "-v int - sets gfxprim verbosity level \n"
806 "-o fmt - output format, ppm, jpg, png \n"
807 "-f params - apply filter, multiple filters may be used\n"
812 " grider -f resize:ratio=1.5 -f contrast:mul=1.2 in.png\n"
814 " * will resize image 1.5 times and increases contrast \n"
815 " by 20%. The result is, just for now, saved to \n"
816 " out_X.ppm where X is number which is increased for \n"
817 " each image given as parameter. \n"
819 " List of filters \n"
820 " =============== \n"
823 static void print_help(void)
829 static const char *out_fmts
[] = {
836 static void check_fmt(const char *fmt
)
840 for (i
= 0; out_fmts
[i
] != NULL
; i
++)
841 if (!strcmp(out_fmts
[i
], fmt
))
844 fprintf(stderr
, "Invalid output format '%s'\n", fmt
);
848 static void save_by_fmt(struct GP_Pixmap
*bitmap
, const char *name
, const char *fmt
)
852 progress_prefix
= "Saving Image";
854 if (!strcmp(fmt
, "ppm"))
855 ret
= GP_SavePPM(bitmap
, name
, progress_callback
);
856 else if (!strcmp(fmt
, "jpg"))
857 ret
= GP_SaveJPG(bitmap
, name
, progress_callback
);
858 else if (!strcmp(fmt
, "png"))
859 ret
= GP_SavePNG(bitmap
, name
, progress_callback
);
861 printf("Invalid format %s\n", fmt
);
866 fprintf(stderr
, "Failed to save bitmap: %s\n",
871 if (progress_callback
!= NULL
)
872 fprintf(stderr
, " done\n");
875 int main(int argc
, char *argv
[])
879 const char *out_fmt
= "ppm";
881 GP_ProgressCallback callback
= {
882 .callback
= show_progress
,
885 while ((opt
= getopt(argc
, argv
, "f:ho:pv:")) != -1) {
895 fprintf(stderr
, "ERROR: invalid debug level "
896 "'%s', expected number > 0\n",
911 progress_callback
= &callback
;
919 if (optind
>= argc
) {
920 fprintf(stderr
, "ERROR: Expected bitmap filenames\n");
924 for (i
= optind
; i
< argc
; i
++) {
927 snprintf(buf
, sizeof(buf
), "out_%03i.%s", i
- optind
+ 1, out_fmt
);
928 fprintf(stderr
, "Processing '%s' -> '%s'\n", argv
[i
], buf
);
930 progress_prefix
= "Loading image";
932 if ((bitmap
= GP_LoadImage(argv
[i
], progress_callback
)) == NULL
) {
933 fprintf(stderr
, "Failed to load bitmap: %s\n", strerror(errno
));
937 if (progress_callback
!= NULL
)
938 fprintf(stderr
, " done\n");
940 apply_filters(&bitmap
);
942 save_by_fmt(bitmap
, buf
, out_fmt
);