/* Antithread example to approximate picture with triangles using * genetic algorithm. Example for a 2 cpu system: * ./arabella arabella.jpg out out.save 2 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Define this to run 100 times without dumping state //#define BENCHMARK /* How many drawings in entire population. */ #define POPULATION_SIZE 1000 /* How many generations without 1% improvement before we terminate. */ #define PLATEAU_GENS 200 /* An image buffer to render into. */ struct image { unsigned height, width; unsigned int stride; /* RGB data */ unsigned char *buffer; }; /* A drawing is a (fixed) number of triangles. */ struct drawing { struct triangle *tri; unsigned int num_tris; unsigned long score; }; /* Here's a triangle. */ struct triangle { struct { unsigned int x, y; } coord[3]; /* RGBA */ unsigned char color[4]; unsigned char mult; uint16_t add[3]; }; /* Get pointer into image at a specific location. */ static unsigned char *image_at(struct image *image, unsigned int x, unsigned int y) { return image->buffer + y * image->stride + x * 3; } /* Blend a dot into this location. */ static void add_dot(unsigned char *buf, unsigned char mult, const uint16_t add[]) { unsigned int c; /* /256 isn't quite right, but it's much faster than /255 */ for (c = 0; c < 3; c++) buf[c] = (buf[c] * mult + add[c]) / 256; } /* Code based on example taken from: * http://www.devmaster.net/forums/showthread.php?t=1094 */ static void add_flat_triangle(struct image *image, int x0, int y0, int x1, int y1, int x2, int y2, unsigned char mult, const uint16_t add[]) { unsigned char *buf; unsigned int i, j; // compute slopes for the two triangle legs float dx0 = (float)(x2 - x0) / (y2 - y0); float dx1 = (float)(x2 - x1) / (y2 - y1); int yRange = 0; float lx = (float) x0, rx = (float) x1; if (y0 < y2) { yRange = y2 - y0; buf = image_at(image, 0, y0); } else { yRange = y0 - y2; buf = image_at(image, 0, y2); lx = rx = (float)x2; } for (i=0; i < yRange; ++i) { for (j=(int)(lx); j<(int)((rx) + 1.0f); ++j) add_dot(buf + 3 * j, mult, add); lx += dx0; rx += dx1; buf += image->stride; } } static void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } static void paint_triangle(struct image *image, const struct triangle *tri) { int i0 = 0, i1 = 1, i2 = 2; int x0, y0, x1, y1, x2, y2; /* Could do this on triangle creation. */ // sort verts by height if (tri->coord[i0].y > tri->coord[i1].y) swap(&i0, &i1); if (tri->coord[i0].y > tri->coord[i2].y) swap(&i0, &i2); if (tri->coord[i1].y > tri->coord[i2].y) swap(&i1, &i2); x0 = tri->coord[i0].x, y0 = tri->coord[i0].y; x1 = tri->coord[i1].x, y1 = tri->coord[i1].y; x2 = tri->coord[i2].x, y2 = tri->coord[i2].y; // test for easy cases, else split trinagle in two and render both halfs if (y1 == y2) { if (x1 > x2) swap(&x1, &x2); add_flat_triangle(image, x1, y1, x2, y2, x0, y0, tri->mult, tri->add); } else if (y0 == y1) { if (x0 > x1) swap(&x0, &x1); add_flat_triangle(image, x0, y0, x1, y1, x2, y2, tri->mult, tri->add); } else { // compute x pos of the vert that builds the splitting line with x1 int tmp_x = x0 + (int)(0.5f + (float)(y1-y0) * (float)(x2-x0) / (float)(y2-y0)); if (x1 > tmp_x) swap(&x1, &tmp_x); add_flat_triangle(image, x1, y1, tmp_x, y1, x0, y0, tri->mult, tri->add); add_flat_triangle(image, x1, y1, tmp_x, y1, x2, y2, tri->mult, tri->add); } } /* Create a new image, allocated off context. */ static struct image *new_image(const void *ctx, unsigned width, unsigned height, unsigned stride) { struct image *image = talloc(ctx, struct image); image->width = width; image->height = height; image->stride = stride; image->buffer = talloc_zero_array(image, unsigned char, stride * height); return image; } /* Taken from JPEG example code. Quality is 1 to 100. */ static void write_jpeg_file(const struct image *image, const char *filename, int quality) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; FILE *outfile; JSAMPROW row_pointer[1]; int row_stride; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); if ((outfile = fopen(filename, "wb")) == NULL) err(1, "can't open %s", filename); jpeg_stdio_dest(&cinfo, outfile); cinfo.image_width = image->width; cinfo.image_height = image->height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); row_stride = image->width * 3; while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = image->buffer + cinfo.next_scanline*row_stride; (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); fclose(outfile); jpeg_destroy_compress(&cinfo); } static struct image *read_jpeg_file(const void *ctx, const char *filename) { struct jpeg_decompress_struct cinfo; struct image *image; struct jpeg_error_mgr jerr; FILE *infile; int row_stride; if ((infile = fopen(filename, "rb")) == NULL) err(1, "can't open %s", filename); cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); row_stride = cinfo.output_width * cinfo.output_components; image = new_image(ctx, cinfo.output_width, cinfo.output_height, row_stride); while (cinfo.output_scanline < cinfo.output_height) { JSAMPROW row = &image->buffer[cinfo.output_scanline*row_stride]; jpeg_read_scanlines(&cinfo, &row, 1); } (void) jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); return image; } /* Higher means closer to perfect match. We assume images same size. */ static unsigned long compare_images(const struct image *a, const struct image *b) { unsigned long result = 0; unsigned i; /* Huge images won't work here. We'd need to get cleverer. */ assert(a->height * a->stride < ULONG_MAX / 8); for (i = 0; i < a->height * a->stride; i++) { if (a->buffer[i] > b->buffer[i]) result += a->buffer[i] - b->buffer[i]; else result += b->buffer[i] - a->buffer[i]; } return result; } /* Precalculate the alpha adds and multiplies for this color/alpha combo. */ static void calc_multipliers(struct triangle *tri) { /* Multiply by 255 - alpha. */ tri->mult = (255 - tri->color[3]); /* Add alpha * color */ tri->add[0] = (tri->color[0] * tri->color[3]); tri->add[1] = (tri->color[1] * tri->color[3]); tri->add[2] = (tri->color[2] * tri->color[3]); } /* Render the image of this drawing, and return it. */ static struct image *image_of_drawing(const void *ctx, const struct drawing *drawing, const struct image *master) { struct image *image; unsigned int i; image = new_image(ctx, master->width, master->height, master->stride); for (i = 0; i < drawing->num_tris; i++) paint_triangle(image, &drawing->tri[i]); return image; } /* Render the image and compare with the master. */ static void score_drawing(struct drawing *drawing, const struct image *master) { struct image *image; /* We don't allocate it off the drawing, since we don't need * it inside the shared area. */ image = image_of_drawing(NULL, drawing, master); drawing->score = compare_images(image, master); talloc_free(image); } /* Create a new drawing allocated off context (which is the antithread * pool context, so this is all allocated inside the pool so the * antithreads can access it). */ static struct drawing *new_drawing(const void *ctx, unsigned int num_tris) { struct drawing *drawing = talloc_zero(ctx, struct drawing); drawing->num_tris = num_tris; drawing->tri = talloc_array(drawing, struct triangle, num_tris); return drawing; } /* Make a random change to the drawing: frob one triangle. */ static void mutate_drawing(struct drawing *drawing, const struct image *master) { unsigned int i, r = random(); struct triangle *tri = &drawing->tri[r % drawing->num_tris]; r /= drawing->num_tris; r %= 12; if (r < 6) { /* Move one corner in x or y dir. */ if (r % 2) tri->coord[r/2].x = random() % master->width; else tri->coord[r/2].y = random() % master->height; } else if (r < 10) { /* Change one aspect of color. */ tri->color[r - 6] = random() % 256; } else if (r == 10) { /* Completely move a triangle. */ for (i = 0; i < 3; i++) { tri->coord[i].x = random() % master->width; tri->coord[i].y = random() % master->height; } } else { /* Completely change a triangle's colour. */ for (i = 0; i < 4; i++) tri->color[i] = random() % 256; } calc_multipliers(tri); } /* Breed two drawings together, and throw in a mutation. */ static struct drawing *breed_drawing(const void *ctx, const struct drawing *a, const struct drawing *b, const struct image *master) { unsigned int i; struct drawing *drawing; unsigned int r = random(), randmax = RAND_MAX; assert(a->num_tris == b->num_tris); drawing = new_drawing(ctx, a->num_tris); for (i = 0; i < a->num_tris; i++) { switch (r & 1) { case 0: /* Take from A. */ drawing->tri[i] = a->tri[i]; break; case 1: /* Take from B. */ drawing->tri[i] = b->tri[i]; break; } r >>= 1; randmax >>= 1; if (randmax == 0) { r = random(); randmax = RAND_MAX; } } mutate_drawing(drawing, master); score_drawing(drawing, master); return drawing; } /* This is our anti-thread. It does the time-consuming operation of * breeding two drawings together and scoring the result. */ static void *breeder(struct at_pool *atp, struct image *master) { const struct drawing *a, *b; /* For simplicity, controller just hands us two pointers in * separate writes. It could put them in the pool for us to * read. */ while ((a = at_read_parent(atp)) != NULL) { struct drawing *child; b = at_read_parent(atp); child = breed_drawing(at_pool_ctx(atp), a, b, master); at_tell_parent(atp, child); } /* Unused: we never exit. */ return NULL; } /* We keep a very rough count of how much work each athread has. This * works fine since fairly all rendering takes about the same time. * * Better alternative would be to put all the pending work somewhere * in the shared area and notify any idle thread. The threads would * keep looking in that shared area until they can't see any more * work, then they'd at_tell_parent() back. */ struct athread_work { struct athread *at; unsigned int pending; }; /* It's assumed that there isn't more than num results pending. */ static unsigned gather_results(struct athread_work *athreads, unsigned int num_threads, struct drawing **drawing, unsigned int num, bool block) { unsigned int i, maxfd = 0, done = 0; struct timeval zero = { .tv_sec = 0, .tv_usec = 0 }; /* If it mattered, we could cache this fd mask and maxfd. */ for (i = 0; i < num_threads; i++) { if (at_fd(athreads[i].at) > maxfd) maxfd = at_fd(athreads[i].at); } do { fd_set set; FD_ZERO(&set); for (i = 0; i < num_threads; i++) FD_SET(at_fd(athreads[i].at), &set); if (select(maxfd+1, &set, NULL, NULL, block ? NULL : &zero) < 0) err(1, "Selecting on antithread results"); for (i = 0; i < num_threads; i++) { if (!FD_ISSET(at_fd(athreads[i].at), &set)) continue; *drawing = at_read(athreads[i].at); if (!*drawing) err(1, "Error with thread %u", i); drawing++; num--; athreads[i].pending--; done++; } } while (block && num); return done; } /* Hand work to an antithread to breed these two together. */ static void tell_some_breeder(struct athread_work *athreads, unsigned int num_threads, const struct drawing *a, const struct drawing *b) { unsigned int i, best = 0; /* Find least loaded thread. */ for (i = 1; i < num_threads; i++) { if (athreads[i].pending < athreads[best].pending) best = i; } at_tell(athreads[best].at, a); at_tell(athreads[best].at, b); athreads[best].pending++; } /* We seed initial triangles colours from the master image. */ static const unsigned char *initial_random_color(const struct image *master) { return master->buffer + (random() % (master->height * master->width))*3; } /* Create an initial random drawing. */ static struct drawing *random_drawing(const void *ctx, const struct image *master, unsigned int num_tris) { struct drawing *drawing = new_drawing(ctx, num_tris); unsigned int i; for (i = 0; i < drawing->num_tris; i++) { unsigned int c; struct triangle *tri = &drawing->tri[i]; for (c = 0; c < 3; c++) { tri->coord[c].x = random() % master->width; tri->coord[c].y = random() % master->height; } memcpy(tri->color, initial_random_color(master), 3); tri->color[3] = (random() % 255) + 1; calc_multipliers(tri); } score_drawing(drawing, master); return drawing; } /* Read in a drawing from the saved state file. */ static struct drawing *read_drawing(const void *ctx, FILE *in, const struct image *master, unsigned int *generation) { struct drawing *drawing; unsigned int i; if (fscanf(in, "%u triangles, generation %u:\n", &i, generation) != 2) errx(1, "Reading saved state"); drawing = new_drawing(ctx, i); for (i = 0; i < drawing->num_tris; i++) { unsigned int color[4]; if (fscanf(in, "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n", &drawing->tri[i].coord[0].x, &drawing->tri[i].coord[0].y, &drawing->tri[i].coord[1].x, &drawing->tri[i].coord[1].y, &drawing->tri[i].coord[2].x, &drawing->tri[i].coord[2].y, &color[0], &color[1], &color[2], &color[3]) != 10) errx(1, "Reading saved state"); drawing->tri[i].color[0] = color[0]; drawing->tri[i].color[1] = color[1]; drawing->tri[i].color[2] = color[2]; drawing->tri[i].color[3] = color[3]; calc_multipliers(&drawing->tri[i]); } score_drawing(drawing, master); return drawing; } /* Comparison function for sorting drawings best to worst. */ static int compare_drawing_scores(const void *_a, const void *_b) { struct drawing **a = (void *)_a, **b = (void *)_b; return (*a)->score - (*b)->score; } /* Save one drawing to state file */ static void dump_drawings(struct drawing **drawing, const char *outname, unsigned int generation) { FILE *out; unsigned int i, j; char *tmpout = talloc_asprintf(NULL, "%s.tmp", outname); out = fopen(tmpout, "w"); if (!out) err(1, "Opening %s", tmpout); fprintf(out, "POPULATION_SIZE=%u\n", POPULATION_SIZE); for (i = 0; i < POPULATION_SIZE; i++) { fprintf(out, "%u triangles, generation %u:\n", drawing[i]->num_tris, generation); for (j = 0; j < drawing[i]->num_tris; j++) { fprintf(out, "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n", drawing[i]->tri[j].coord[0].x, drawing[i]->tri[j].coord[0].y, drawing[i]->tri[j].coord[1].x, drawing[i]->tri[j].coord[1].y, drawing[i]->tri[j].coord[2].x, drawing[i]->tri[j].coord[2].y, drawing[i]->tri[j].color[0], drawing[i]->tri[j].color[1], drawing[i]->tri[j].color[2], drawing[i]->tri[j].color[3]); } } if (fclose(out) != 0) err(1, "Failure closing %s", tmpout); if (rename(tmpout, outname) != 0) err(1, "Renaming %s over %s", tmpout, outname); talloc_free(tmpout); } /* Save state file. */ static void dump_state(struct drawing *drawing[POPULATION_SIZE], const struct image *master, const char *outpic, const char *outstate, unsigned int gen) { char *out = talloc_asprintf(NULL, "%s.%08u.jpg", outpic, gen); struct image *image; printf("Dumping gen %u to %s & %s\n", gen, out, outstate); dump_drawings(drawing, outstate, gen); image = image_of_drawing(out, drawing[0], master); write_jpeg_file(image, out, 80); talloc_free(out); } /* Biassed coinflip moves us towards top performers. I didn't spend * too much time on it, but 1/32 seems to give decent results (see * breeding-algos.gnumeric). */ static struct drawing *select_good_drawing(struct drawing *drawing[], unsigned int population) { if (population == 1) return drawing[0]; if (random() % 32) return select_good_drawing(drawing, population/2); return select_good_drawing(drawing + population/2, population/2); } static void usage(void) { errx(1, "usage: []"); } int main(int argc, char *argv[]) { struct image *master; unsigned int gen, since_prev_best, num_threads, i; struct drawing *drawing[POPULATION_SIZE]; unsigned long prev_best, poolsize; struct at_pool *atp; struct athread_work *athreads; if (argc != 6 && argc != 7) usage(); /* Room for triangles and master image, with some spare. * We should really read master image header first, so we can be * more precise than "about 3MB". ccan/alloc also needs some * more work to be more efficient. */ poolsize = (POPULATION_SIZE + POPULATION_SIZE/4) * (sizeof(struct drawing) + atoi(argv[4]) * sizeof(struct triangle)) * 2 + 1000 * 1000 * 3; atp = at_pool(poolsize); if (!atp) err(1, "Creating pool of %lu bytes", poolsize); /* Auto-free the pool and anything hanging off it (eg. threads). */ talloc_steal(talloc_autofree_context(), atp); /* Read in file */ master = read_jpeg_file(at_pool_ctx(atp), argv[1]); if (argc == 6) { printf("Creating initial population"); fflush(stdout); for (i = 0; i < POPULATION_SIZE; i++) { drawing[i] = random_drawing(at_pool_ctx(atp), master, atoi(argv[4])); printf("."); fflush(stdout); } printf("\n"); gen = 0; } else { FILE *state; char header[100]; state = fopen(argv[6], "r"); if (!state) err(1, "Opening %s", argv[6]); fflush(stdout); fgets(header, 100, state); printf("Loading initial population from %s: %s", argv[6], header); for (i = 0; i < POPULATION_SIZE; i++) { drawing[i] = read_drawing(at_pool_ctx(atp), state, master, &gen); gen++; /* We start working on the _next_ gen */ printf("."); fflush(stdout); } } num_threads = atoi(argv[5]); if (!num_threads) usage(); /* Hang the threads off the pool (not *in* the pool). */ athreads = talloc_array(atp, struct athread_work, num_threads); for (i = 0; i < num_threads; i++) { athreads[i].pending = 0; athreads[i].at = at_run(atp, breeder, master); if (!athreads[i].at) err(1, "Creating antithread %u", i); } since_prev_best = 0; /* Worse than theoretically worst case. */ prev_best = master->height * master->stride * 256; while (since_prev_best < PLATEAU_GENS) { unsigned int j, done = 0; struct drawing *new[POPULATION_SIZE/4]; qsort(drawing, POPULATION_SIZE, sizeof(drawing[0]), compare_drawing_scores); printf("Best %lu, worst %lu\n", drawing[0]->score, drawing[POPULATION_SIZE-1]->score); /* Probability of being chosen to breed depends on * rank. We breed over lowest 1/4 population. */ for (j = 0; j < POPULATION_SIZE / 4; j++) { struct drawing *best1, *best2; best1 = select_good_drawing(drawing, POPULATION_SIZE); best2 = select_good_drawing(drawing, POPULATION_SIZE); tell_some_breeder(athreads, num_threads, best1, best2); /* We reap during loop, so return pipes don't fill. * See "Better alternative" above. */ done += gather_results(athreads, num_threads, new + done, j - done, false); } /* Collate final results. */ gather_results(athreads, num_threads, new+done, j-done, true); /* Overwrite bottom 1/4 */ for (j = POPULATION_SIZE * 3 / 4; j < POPULATION_SIZE; j++) { talloc_free(drawing[j]); drawing[j] = new[j - POPULATION_SIZE * 3 / 4]; } /* We dump on every 1% improvement in score. */ if (drawing[0]->score < prev_best * 0.99) { #ifndef BENCHMARK dump_state(drawing, master, argv[2], argv[3], gen); #endif prev_best = drawing[0]->score; since_prev_best = 0; } else since_prev_best++; #ifdef BENCHMARK if (gen == 100) exit(0); #endif gen++; } /* Dump final state */ printf("No improvement over %lu for %u gens\n", prev_best, since_prev_best); dump_state(drawing, master, argv[2], argv[3], gen); return 0; }