Generating moderately interesting images
Abstract: Can you propose a mathematical-ish algorithm over a plane of pixels that will generate a moderately interesting image, preferably one that on the whole resembles something?
The story thus far:
Once upon a time I decided in an effort to reduce cycle waste on my (admittedly too) numerous computers, and set out to generate images in a moderately interesting fashion; using a PRNG and some clever math to create images that would, on the whole, resemble something.
Or at least, that was the plan. As it turns out, clever math requires being a clever mathematician; this I am not.
At some length I arrived at a method that preferred straight lines (as these are generally the components of which our world is made), perhaps too strongly. The result is mildly interesting; resembling, perhaps, city grids as such:
City grids, maybe? http://totlandweb.info/imggen.out.png
Now for the question proper: Given the source code of this little program; can you improve upon it and propose a method that gives somewhat more interesting results? (e.g. not city grids, but perhaps faces, animals, geography, what have you)
This is also meant as a sort of challenge; I suppose and as such I've set down some completely arbitrary and equally optional rules:
The comments in the code says it all really. Suggestions and "solutions" should edit the algorithm itself, not the surrounding framework, except as for to fix errors that prevents the sample from compiling.
The code should compile cleanly with a standard issue C compiler. (If the example provided doesn't, oops! Tell me, and I'll fix. :)
The method should, though again, this is optional, not need to elicit help from your friendly neighborhood math library, and on the whole employ a (P)RNG as its primary data input channel.
Solutions should probably be deliverable by simply yanking out whatever is between the snip lines (the ones that say you should not edit above and below, respectively), with a statement to the effect of what you need to add to the preamble in particular.
Edit: It is sometimes easy to forget that people on the internet cannot read my mind; but there you go. The program should require a minimum of human intervention in the generation of the images, except for to evaluate the results and chose the best ones.
The code requires a C compiler and libpng to build; I'm not entirely confident that the MinGW compiler provides the necessities, but I would be surprised if it didn't. For Debian you'll want the libpng-dev package, and for Mac OS X you'll want the XCode tools..
The source code can be downloaded here.
Warning: Massive code splurge incoming!
// compile with gcc -o imggen -lpng imggen.c
// optionally with -DITERATIONS=x, where x is an appropriate integer
// If you're on a Mac or using MinGW, you may have to fiddle with the linker flags to find the library and includes.
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#ifdef ITERATIONS
#define REPEAT
#endif // ITERATIONS
// YOU MAY CHANGE THE FOLLOWING DEFINES
#define WIDTH 320
#define HEIGHT 240
// YOU MAY REPLACE THE FOLLOWING DEFINES AS APPROPRIATE
#define INK 16384
void writePNG (png_bytepp imageBuffer, png_uint_32 width, png_uint_32 height, int iteration) {
char *fname;
asprintf(&fname, "out.%d.png", iteration);
FILE *fp = fopen(fname, "wb");
if (!fp) return;
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(png_ptr);
png_init_io(png_ptr, fp);
png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, PNG_FILTER_NONE);
png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_rows(png_ptr, info_ptr, imageBuffer);
png_set_invert_mono(png_ptr); /// YOU MAY COMMENT OUT THIS LINE
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
free(fname);
}
int main (int argc, const char * argv[]) {
png_uint_32 height = HEIGHT, width = WIDTH;
int iteration = 1;
#ifdef REPEAT
for (iteration = 1; iteration <= ITERATIONS; iteration++) {
#endif // REPEAT
png_bytepp imageBuffer = malloc(sizeof(png_bytep) * height);
for (png_uint_32 i = 0; i < height; i++) {
imageBuffer[i] = malloc(sizeof(png_byte) * width);
for (png_uint_32 j = 0; j < width; j++) {
imageBuffer[i][j] = 0;
}
}
/// CUT ACROSS THE DASHED LINES
/// -------------------------------------------
开发者_开发技巧 /// NO EDITING ABOVE THIS LINE; EXCEPT AS NOTED
int ink = INK;
int x = rand() % width, y = rand() % height;
int xdir = (rand() % 2)?1:-1;
int ydir = (rand() % 2)?1:-1;
while (ink) {
imageBuffer[y][x] = 255;
--ink;
xdir += (rand() % 2)?(1):(-1);
ydir += (rand() % 2)?(1):(-1);
if (ydir > 0) {
++y;
} else if (ydir < 0) {
--y;
}
if (xdir > 0) {
++x;
} else if (xdir < 0) {
--x;
}
if (x == -1 || y == -1 || x == width || y == height || x == y && x == 0) {
x = rand() % width; y = rand() % height;
xdir = (rand() % 2)?1:-1;
ydir = (rand() % 2)?1:-1;
}
}
/// NO EDITING BELOW THIS LINE
/// -------------------------------------------
writePNG(imageBuffer, width, height, iteration);
for (png_uint_32 i = 0; i < height; i++) {
free(imageBuffer[i]);
}
free(imageBuffer);
#ifdef REPEAT
}
#endif // REPEAT
return 0;
}
Note: While this question doesn't strictly speaking seem "answerable" as such; I still believe that it can give rise to some manner of "right" answer. Maybe.
Happy hunting.
Edit (again): The source code for the simplistic bezier paths used in my answer (read down) can be found here and here.
Fractals? They're not just for stock markets analysis anymore (Mandelbrot joke, sorry).
Some Fractal images tend to be reminiscent of real-world geographies. In particular IFS fractals can be used for fairly realistic plants and trees, and terrain.
fractal.c is a simple monochromatic Mandelbrot set, at its default zoom.
Added:
Context-free grammars can be used to express equations that can draw images that are aesthetically pleasing to humans. Jared Tarbell has a related gallery of some wonderful images generated by programs. Aza's Algorithm Ink
2nd addition:
The other major form of algebraic or computation art is from Cellular automaton (CA), such as (John) Conway's Game of Life, made famous by the 1970 Scientific American article written by Martin Gardner. CA was re-introduced to the public with Stephen Wolfram's self-publication of A New Kind of Science (NKS) in 2002. These tend to be closed dynamic systems, that "live" or "die" based on a simple set of rules.
Related to fractals are chaotic systems, or non-linear dynamic systems if you want to sound smart. They can be modelled on physical systems, like weather forecasting, and can provide not-really-random but hard-to-predict numeric output that can be visualized as a strange attractor (and SA).
Hmmm. I recall making a country generator in logo a long time ago. The basic strategy was to seed it with a painter for each color, and have the rule, "Color painters, move randomly, but may not move on any area that is painted unless it is painted in their own color." The result was several contiguously painted areas. The painters moved randomly in 4 directions and the grid was like 50x50 or so in size.
Once done, I took my 50x50 image, expanded it (with nearest neighbor) to something much larger, and used some standard graphics filters (blur and such) to make it look decent. If you want a monochrome image, just turn any borders black and everything else white.
This strategy does not favor straight lines at all.
The question you are asking is an aesthetic one. The things one person considers "good" or "beautiful" or "moderately interesting" vary so widely from person to person that I don't think a best answer for this can be found.
I know many people who would find the picture you have posted in your question to be disgusting and without merit, riddled with the ideas of the socio-political straitjacket that is city-planning. However, I think it looks interesting.
This may be more of a corollary to your problem than a solution to it, but I have seen interesting results from looking at your same idea from a different angle:
Take a grid (build it from squares, hexagons, or what have you) and generate a random field of points on it. Now, examine all the possible ways to connect that fixed set of points using lines that follow the grid. Take snippets of pictures with a similar theme (for instance, leaves), and convert them to "line art" style to reduce them to an outline. For each set of several points, look through your library of outline pieces and try to find one that can connect the dots (or at least come close, this may require rotating, mirroring, etc). You will find there are a myriad of possible outputs, but if done correctly you can end up with many that look like leaves (even if they are 'new' types of leaves that have never before been seen)!
Instead of straight randomness you could add some trivial state to the behavior. For instance, instead of choosing what x
and y
do based on P
where P
is essentially rand % 2
you could choose them based on a conditional equation. As a simple example:
two-state http://img121.imageshack.us/img121/9018/twostate.png
If you allow state 1
to represent 'maintain current path' and 0
to be 'change direction' then by adjusting the Q
threshold you control the frequency at which you change direction. Adding states adds complexity, but you will likely get moderately interesting results with carefully chosen values for P
and Q
.
In the spirit of totally shattering my own (admittedly arbitrary rules, I've gone right ahead and created an answer myself!
The code uses some very simple bezier curve code I threw together last night, and while the answer is less that compellingly interesting, it's still a bit fun. The code for the beziers can be found here and here.
In addition to editing the snip field, I also added, of course,
#include "bezier.h"
It doesn't work very well without this line. ;)
/// NO EDITING ABOVE THIS LINE; EXCEPT AS NOTED
Bezier *path = newBezier(newPoint(rand() % width,rand() % height), newPoint(rand() % width,rand() % height), newPoint(rand() % width,rand() % height), newPoint(rand() % width,rand() % height));
float t;
Point *point = NULL;
for (t = 0.0; t <= 1.0; t += 0.00000006) {
point = bezierPoint(path, t, point);
int32_t x = point->x, y = point->y;
if (x >= 0 && x < width && y >= 0 && y < height)
imageBuffer[y][x] = 255;
}
destroyPoint(point);
/// NO EDITING BELOW THIS LINE
精彩评论