photomosaics

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 59596010c6a3aec4d11fcf5d1c1495b831aa162d
parent 3bc5e342aace76d3b5acc13e178deae14e5f482c
Author: Wilson Gheen <wilson@wilsonrgheen.com>
Date:   Sun,  1 Jan 2023 06:47:48 -0600

WIP: Add snails-pace photomosaic function that works with a filename taken from a list of images and their average pixel colors. Will soon put everything together

Diffstat:
Mphotomosaics.c | 141++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Ast_john.jpg | 0
Ast_john_mosaic.jpg | 0
3 files changed, 107 insertions(+), 34 deletions(-)

diff --git a/photomosaics.c b/photomosaics.c @@ -30,6 +30,14 @@ static bool parse_long(char *str, long *out) { setlocale(LC_ALL, old_locale); return strncmp(str, endptr, strlen(str)); } +static bool parse_hex_tol(char *str, long *out) { + char *endptr; + const char *old_locale = setlocale(LC_ALL, NULL); + setlocale(LC_ALL|~LC_NUMERIC, ""); + *out = strtol(str, &endptr, 16); + setlocale(LC_ALL, old_locale); + return strncmp(str, endptr, strlen(str)); +} static bool parse_ulong(char *str, unsigned long *out) { char *endptr; const char *old_locale = setlocale(LC_ALL, NULL); @@ -138,35 +146,52 @@ static Image *splotch_img(Image *image, const size_t each_width, const size_t ea return new_image; } -Pixel get_closest_pixel(Pixel p) { - FILE *avgs_list = fopen("al", "rb"); - if(!avgs_list) exit(1); - unsigned char ps[315]; - size_t z = fread(ps, 1, 315, avgs_list); - size_t i; - Pixel closest = {0}; +static bool get_closest_pixel(Pixel p, const size_t width, const size_t height, unsigned char *pixels_out, ExceptionInfo *exception) { + FILE *avgs_list = fopen("my_avgs", "r"); + if(!avgs_list) return false; +#define MY_AVGS_SIZE 5931 + char buf[MY_AVGS_SIZE]; + size_t z = fread(buf, 1, MY_AVGS_SIZE, avgs_list); +/* printf("Read %zu bytes from my_avgs.\n", z);*/ + fclose(avgs_list); + char *filename = strtok(buf, " "); + char *closest_file = malloc(150); float distance_of_closest = sqrtf(powf(0xff, 2) * 3); //max diff value - for(i=0; i < z; i+=3) { -/* if(p.r == ps[i] && p.g == ps[i+1] && p.b == ps[i+2]) {*/ -/* closest.r = ps[i];*/ -/* closest.g = ps[i+1];*/ -/* closest.b = ps[i+2];*/ -/* break;*/ -/* }*/ - //Need to calculate a difference that respects signs - int rdiff = (int)ps[i] - p.r; - int gdiff = (int)ps[i+1] - p.g; - int bdiff = (int)ps[i+2] - p.b; + do { + char *avg_p = strtok(NULL, "\n"); + char rstr[3] = {avg_p[0], avg_p[1],}; + char gstr[3] = {avg_p[2], avg_p[3],}; + char bstr[3] = {avg_p[4], avg_p[5],}; + long rdiff, gdiff, bdiff; + parse_hex_tol(rstr, &rdiff); + parse_hex_tol(gstr, &gdiff); + parse_hex_tol(bstr, &bdiff); + rdiff -= p.r; + gdiff -= p.g; + bdiff -= p.b; float new_distance = sqrtf(powf(rdiff, 2) + powf(gdiff, 2) + powf(bdiff, 2)); - printf("%zu: %.2f\n", i/3, new_distance); if(new_distance < distance_of_closest) { distance_of_closest = new_distance; - closest.r = ps[i]; - closest.g = ps[i+1]; - closest.b = ps[i+2]; + closest_file[0] = 0; + strncat(closest_file, filename, 150); } - } - return closest; + } while((filename=strtok(NULL, " ")) && filename < buf + MY_AVGS_SIZE); + + size_t len = strnlen(closest_file, 150); + if(len < 1 || len >= 150) + return false; + + + closest_file[len-1] = 0; //Get rid of the colon + ImageInfo *image_info = CloneImageInfo((ImageInfo *)NULL); + strcpy(image_info->filename, closest_file); + Image *src_img = ReadImage(image_info, exception); + Image *src_img_r = resize_image_to(src_img, width, height, exception); + ExportImagePixels(src_img_r, 0, 0, width, height, "RGB", CharPixel, pixels_out, exception); + DestroyImage(src_img); + DestroyImage(src_img_r); + DestroyImageInfo(image_info); + return true; } static unsigned char *get_img_with_closest_avg(Pixel p, const size_t width, const size_t height, ExceptionInfo *exception) { @@ -190,16 +215,57 @@ static unsigned char *get_img_with_closest_avg(Pixel p, const size_t width, cons DestroyImage(src_img_r); DestroyImageInfo(image_info); } - FILE *avgs_list = fopen("al", "wb"); - if(!avgs_list) exit(1); - for(int i=0; i < 105; i++) { - char str[4] = {img_avgs[i].r, img_avgs[i].g, img_avgs[i].b,}; - fwrite(str, 1, 3, avgs_list); +/* FILE *avgs_list = fopen("al", "wb");*/ +/* if(!avgs_list) exit(1);*/ +/* for(int i=0; i < 105; i++) {*/ +/* char str[4] = {img_avgs[i].r, img_avgs[i].g, img_avgs[i].b,};*/ +/* fwrite(str, 1, 3, avgs_list);*/ +/* }*/ +/* fclose(avgs_list);*/ + FILE *my_avgs = fopen("my_avgs", "w"); + if(!my_avgs) exit(1); + for(int i=0, c=0; i < 105 && c < IMG_LIST_SIZE; i++) { + fprintf(my_avgs, "%s: %02x%02x%02x\n", &buf[c], img_avgs[i].r, img_avgs[i].g, img_avgs[i].b); + c += strnlen(&buf[c], IMG_LIST_SIZE - c) + 1; } - fclose(avgs_list); + fclose(my_avgs); return NULL; } +static Image *photomosaic(Image *image, const size_t each_width, const size_t each_height, ExceptionInfo *exception) { + const size_t pixel_cnt = image->columns * image->rows; + unsigned char *pixels = malloc(pixel_cnt * 3); + if(!ExportImagePixels(image, 0, 0, image->columns, image->rows, "RGB", CharPixel, pixels, exception)) { + free(pixels); + exit(1); + } + for(size_t i=0, j=0; i < pixel_cnt;) { + /*Specifying 0 for y allows us to automatically use i to "roll over" into next row*/ + Pixel p = get_avg_color(pixels, image->columns, i, 0, each_width, each_height); + unsigned char *new_pixels = malloc(each_width * each_height * 3); + if(!get_closest_pixel(p, each_width, each_height, new_pixels, exception)) exit(1); + for(size_t c=0; c < each_width*each_height;) { + pixels[j] = new_pixels[c*3]; + pixels[j+1] = new_pixels[c*3+1]; + pixels[j+2] = new_pixels[c*3+2]; + j += 3; + if(++c % each_width == 0) + j += (image->columns - each_width) * 3; //next row ... + } + i += each_width; //next splotch + /*If this row is done, skip over all the rows we just splotched*/ + if(i % image->columns == 0) + i += image->columns * (each_height - 1); + j = i * 3; + free(new_pixels); + } + Image *new_image = ConstituteImage(image->columns, image->rows, "RGB", CharPixel, pixels, exception); + free(pixels); + if(!new_image) + MagickError(exception->severity, exception->reason, exception->description); + return new_image; +} + int usage() { return 0; } @@ -218,11 +284,12 @@ int main(int argc, char **argv) { bool prn_pixel_info = false; bool resize = false; bool splotch = false; + bool mosaic = false; ssize_t x = 0, y = 0; size_t length = 1, width = 1; int opt; - while((opt=getopt(argc, argv, "adhi:l:no:Rr:sw:x:y:")) > -1) { + while((opt=getopt(argc, argv, "adhi:l:mno:Rr:sw:x:y:")) > -1) { switch(opt) { case 'a': prn_avg_color = true; @@ -240,6 +307,9 @@ int main(int argc, char **argv) { if(!parse_ulong(optarg, &length)) DIE(2, "FATAL: Argument \"%s\" to option -l could not be parsed to a long long int.\n", optarg); break; + case 'm': + mosaic = true; + break; case 'n': prn_pixel_info = true; break; @@ -275,15 +345,16 @@ int main(int argc, char **argv) { } } - if(!(prn_avg_color ^ dumb_shrink ^ prn_pixel_info ^ resize ^ splotch)) - DIE(2, "FATAL: must specify exactly one of: -a, -d, -n, -r, -s\n"); + + if(!(prn_avg_color ^ dumb_shrink ^ prn_pixel_info ^ resize ^ splotch ^ mosaic)) + DIE(2, "FATAL: must specify exactly one of: -a, -d, -n, (-r|-R), -s, -m\n"); if(strnlen(input_img_filename, 400) < 1) DIE(2, "FATAL: no input image specified.\n"); if((resize || splotch) && strnlen(output_img_filename, 400) < 1) DIE(2, "FATAL: Must specify output image to resize or splotch.\n"); if(prn_pixel_info || prn_avg_color) fprintf(stderr, "point: %zu, %zu\n", x, y); - if(prn_avg_color || dumb_shrink || splotch || (resize && resize_factor < 0.01)) + if(prn_avg_color || dumb_shrink || splotch || mosaic || (resize && resize_factor < 0.01)) fprintf(stderr, "dimensions: %zu x %zu\n", width, length); MagickCoreGenesis(*argv, MagickTrue); @@ -310,6 +381,8 @@ int main(int argc, char **argv) { output_img = make_img_avg_colors(input_img, 0, 0, width, length, exception); else if(splotch) output_img = splotch_img(input_img, width, length, exception); + else if(mosaic) + output_img = photomosaic(input_img, width, length, exception); if(exception->severity != UndefinedException) CatchException(exception); diff --git a/st_john.jpg b/st_john.jpg Binary files differ. diff --git a/st_john_mosaic.jpg b/st_john_mosaic.jpg Binary files differ.