pnmtofiasco Bug (Solved)

One of the things which some nerds do, is not just to present a photo of themselves, but to present a photo of themselves, which has been distorted in some specific way. In this vein, some of my readers may already have wondered, how I created the following image of myself:

dirk-fract1

This photo was actually obtained by using a Linux program that no longer works. That program started with a pixel-map, and compressed it into a so-called fractal representation, which is also known as ‘Fiasco’ format.

It may be common knowledge by now, that fractals can represent complex geometries, which take up very little data that way, but which exhibit self-similarity. And, fractals can be converted into pixel-maps to varying depths of recursion. But what some people did a long time ago, was explore, how pixel-maps can be compressed as fractals instead.

(Updated 05/26/2018 … )

(As of 05/17/2018 : )

And the approach which was taken, was just to create a tile, which reduces an image by a factor such as 8 or by 16, and then to perform a regression test of sorts, to see how well the pixel-values in different parts of the image, resemble the pixel-values of the tile. On that basis, the degree was computed, to which an image could be reconstructed first as a grid of colors, corresponding to a regular grid of adjacent tile-widths, and by which the tile could be added to the image-colors already there. (Positively or negatively.) These operations would then be applied subtractively, when encoding, but applied additively, when decoding. ( :1 )

(Edit 05/25/2018 :

Even though correlations which exceed ±1.0 or which are negative are sometimes computable, if it was to happen that the pixel-values inside one of the blocks, resemble the negative of the entire image to some degree, the programmers may well have felt that this does not correspond to the spirit of a fractal, since fractals are more about geometry, than they are about statistical correlations. Thus, the original programmers may have clamped the range of allowable correlations to [ 0.0 .. 1.0 ] . And if they did, a practical benefit of doing so would have been, to ease storing the correlations as 8-bit values, while still preserving overall 8-bit accuracy. If greater range was needed, then either to store the correlation as a larger field of bits, or to reduce eventual accuracy, would also become necessary. )

(Edit 05/18/2018 :

It may or may not be that ‘pnmtofiasco’ halves the size of the tile further, the number of times specified by the ‘-z’ command-line parameter, which is also the number of optimization passes and defaults to zero. In any case it’s difficult to estimate how many times the image was initially tiled, based on visual inspection of the decoded image, because if, for the sake of argument, the tiling was initially 8×8, then the color-blocks would also initially have been 8×8, which would next be reduced in size 8x, just to regenerate the tile when decoding the image. And then, if the tile was applied 8×8 times, this would lead to 64×64 apparent blocks. Further, If the tile-size was halved an additional number of times, again it would lead to smaller, apparent color-blocks, just due to self-similarity being simulated to some finite depth.

Apparently, this process is applied, wherever correlations in the encoded image exceed some correlation that follows from the chosen quality-level.

Also, I assume that if several passes are to be made over the image, the algorithm won’t just halve the size of an existing tile when decoding. Instead, the tile would be recomputed from the emerging image, at reduced pixel-sizes, with each pass. This means that when the Fiasco-formatted image is decoded back into a pixel-based image, the latter can be given a much higher resolution than the original image had – “Magnified” – just so that the complexity of the fractals can be appreciated.

The way I just described the encoding procedure so far, would not match exactly to the way the Fiasco-formatted image will be decoded again. The reason is the fact that when decoding, a reduced tile of the entire image is not available, as it was when encoding. To make up for that, ‘pnmtofiasco’ supports the ‘–prediction’ flag, which means that the encoder will use the tile which the decoder would use, to compute correlation and then subtract the product of that from the source image, instead of using the reduced version of the full image.

(As of 05/17/2018 : )

The resulting file-size, to store the 800×800 image above, is ‘only’ 1.3kB !

But, because Web-browsers that visit my site do not know how to display the .WFA File, what I did next was to convert that back into a JPEG File. And another thing I did with this one, was change the parameters, to exaggerate the ways in which fractals are inaccurate, and fractal-like.

But, to create the .WFA File, we’d need a Debian package installed which is named ‘netpbm’, and which contains literally hundreds of conversion tools which run from the command-line. And the command we’re looking for is ‘pnmtofiasco’.

Well, that was then, this is now. When I type in the correct command, given a .PPM File which I know is not corrupted, this is what I get:

 


dirk@Plato:~$ cd ~/Pictures/Fractals
dirk@Plato:~/Pictures/Fractals$ ls
logo-dirk.gif  logo-dirk.ppm
dirk@Plato:~/Pictures/Fractals$ which pnmtofiasco
/usr/bin/pnmtofiasco
dirk@Plato:~/Pictures/Fractals$ pnmtofiasco -z 2 -t 3 -i logo-dirk.ppm -o dirk-fract1.wfa
*** buffer overflow detected ***: pnmtofiasco terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bfb)[0x7fda7b193bfb]
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7fda7b21c1f7]
/lib/x86_64-linux-gnu/libc.so.6(+0xf7330)[0x7fda7b21a330]
pnmtofiasco(+0x27d4c)[0x56173826bd4c]
pnmtofiasco(+0x27f85)[0x56173826bf85]
pnmtofiasco(+0x57b2)[0x5617382497b2]
pnmtofiasco(+0x2e72)[0x561738246e72]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7fda7b1432e1]
pnmtofiasco(+0x324a)[0x56173824724a]
======= Memory map: ========
561738244000-561738275000 r-xp 00000000 08:01 3027143                    /usr/bin/pnmtofiasco
561738474000-561738475000 r--p 00030000 08:01 3027143                    /usr/bin/pnmtofiasco
561738475000-561738476000 rw-p 00031000 08:01 3027143                    /usr/bin/pnmtofiasco
561738476000-5617384ab000 rw-p 00000000 00:00 0 
561738513000-561738580000 rw-p 00000000 00:00 0                          [heap]
7fda7aec5000-7fda7aedb000 r-xp 00000000 08:01 15731552                   /lib/x86_64-linux-gnu/libgcc_s.so.1
7fda7aedb000-7fda7b0da000 ---p 00016000 08:01 15731552                   /lib/x86_64-linux-gnu/libgcc_s.so.1

(...)


 

I get this result, regardless of whether the command-line parameters are given or not. I.e., the same thing happens without the ‘-z’ or the ‘-t’ .

Now one lesson which I learned early with my Linux experiences, is that in general, it’s completely useless to file a bug-report.

And so the ability to compress pixel-maps into fractals is no more…

This bug has been known to exist since 2012, and nobody has ever touched it.

 


 

(Edit 05/19/2018 : )

I’ve decided to have a try, at examining the source code for ‘pnmtofiasco’. And what I found, is that there are issues with it, which are technical in nature, and which were simply careless on the part of the original programmers.

I was able to modify ‘pnmtofiasco’ in such a way, that it compiles and works. The original programmers had written in straight C, but always allocated the smallest amount of memory in bytes, that their code needed, to work. Well in one specific place, they made a slight error in their Math, and wrote a ‘calloc()’ function call, that allocated slightly less memory than was needed. I was able to modify their code, to allocate slightly more memory than it really needs, but always to make sure their code gets enough memory to work, and without changing anything else about their code.

I changed Line 243 in the source-file ‘pnmtofiasco.c’ .


 

The reason I needed to do this, is the fact that on my machine,

sizeof (char *)

Evaluates to 4 (bytes), while

sizeof (void *)

Evaluates to 8 (bytes). But, the original code inserted one or more characters strings, and then appended a ‘NULL’ to the array, which therefore had twice the size of the regular entries. For that reason, I also needed to go back and change Line 252. So now I can give the command

pnmtofiasco -i logo-dirk.ppm

All by itself, and watch the character garbage come out, which would be a .WFA File! :-)


 

The result is that I can create .WFA Files again. ( :2 )


 

But in order for those to be of any value to the user, there needs to be a way to convert .WFA Files back into pixel-maps of some kind. This is usually done with another ‘netpbm’ program named ‘fiascotopnm’. What I found, was that in its original form, that program also segfaults. But, there was another easy fix.

‘fiascotopnm’ analyzes the output-file’s intended name, and tries to separate it into a basename, and a suffix, according to what was typed on the command-line. This was meant to serve two purposes:

  1. If the user had made the mistake of giving the file the suffix ‘.pgm’ and it was a color-file, or, if the user had made the mistake of giving the file the suffix ‘.ppm’ and it was a gray-scale file, ‘fiascotopnm’ would reverse this mistake and save it out to the correct name, with the corrected suffix,
  2. If a video-stream was to be output to sequentially-numbered file-names, ‘fiascotopnm’ needs to know their base-name, so that the program can assemble systematically-concatenated, numbered output-files, corresponding to one frame each.

Well something initially goes wrong, when the program tries to extract the basename and the suffix from the intended output-file. I’m not sure exactly what goes wrong there. But, the memory-allocation function to store the correct file-name, uses the length of the basename, as well as the length of the recognized suffix, to decide how many bytes could be used to store eventual output-file-names. If at that point, either the base-name or the suffix are garbled, according to what the earlier function in the program tried to determine, a buffer-overrun will also take place.

And so, without analyzing exactly why the code does not work 100% as intended, it was easy for me to replace the memory-allocation with one, that just allocates the number of bytes which the originally-typed output-file-name took up, which adds another (1 + 20) characters, and which next uses that, as the buffer to store output-file-names. Further, in line with this hypothesis, I changed the code, for writing 1 output-frame, to code which does not try to assemble a basename, a dot, and a suffix, but which rather just string-copies the originally-typed output-file-name to the buffer.

And the result was, that I can now convert .WFA Files back into pixel-maps as I wish! :-D  There are now two problems left with ‘fiascotopnm’ as it stands:

  1. If I name my PNM File incorrectly, it will stay named that way,
  2. Consistently with my theory of what goes wrong, outputting a video-stream as sequentially-numbered files, should still not work.

But, on one of my computers, at least Fiasco-compression and decompression, for single frames, works again! :-)


 

(Edit 05/19/2018, 16h15 : )

The fact was knawing at me, that I had a version of ‘fiascotopnm’, which would work almost all the time, but only for single frames, when in fact this code is supposed to work for multi-frame streams as well. And as I had written, the original problem seemed to lie, in a C function named ‘get_output_template()’, which extracted the base-name and the suffix, for the file to store output pixel-maps to.

This code used to work! Why was it suddenly not working? Well I found out. Here is a copy of what this function looks like:

 

static void
get_output_template (const char *image_name, const char *wfa_name,
                     bool_t color, char **basename, char **suffix)
/*
 *  Generate image filename template for output of image sequences.
 *  'wfa_name' is the filename of the WFA stream.
 *  Images are either saved with filename 'basename'.'suffix' (still images)
 *  or 'basename'.%03d.'suffix' (videos).
 *
 *  No return value.
 *
 *  Side effects:
 *  '*basename' and '*suffix' is set.
 */
{
    if (!wfa_name || streq (wfa_name, "-"))
        wfa_name = "stdin";       
    /*
     *  Generate filename template
     */
    if (!image_name || streq (image_name, "") || streq (image_name, "-"))
    {
        *basename = strdup (wfa_name);
        *suffix   = NULL;
    }
    else
    {
        *basename = strdup (image_name);
        *suffix   = strrchr (*basename, '.');
    }
    
//    if (*suffix)         /* found name 'basename.suffix' */
//    {
//        **suffix = 0;         /* remove dot */
//        (*suffix)++;
//        if (**suffix == 0)
//            *suffix = strdup (color ? "ppm" : "pgm");
//    }
//    else             /* no suffix found, generate one */
//        *suffix = strdup (color ? "ppm" : "pgm");

    if (*suffix)         /* found name 'basename.suffix' */
    {
        strcpy(*suffix, "");         /* remove dot */
        *suffix = strdup (color ? "ppm" : "pgm");
    }
    else             /* no suffix found, generate one */
        *suffix = strdup (color ? "ppm" : "pgm");
}


 

I commented out the part of the code, which was malfunctioning. The offense took place on line 34, as presented above. The problem there was, that the code assigned the integer zero, to one character of a string. This happened to work, because the code was previously being compiled to run on a 32-bit system, and because the suffix which was already present, was already a 3-byte suffix! Hence, what C will do on a 32-bit system, is copy a 4-byte integer zero, to the point where the dot was, and past that point.

On a 64-bit system, the assignment will copy an 8-byte integer representation of zero, which will go right past the string, which ‘strdup()’ just allocated memory for! Hence, corruption will take place on a 64-bit system.

My corrected code will just use ‘strcpy()’ instead, to remove the dot, will assume that nothing more will follow it, and will just recreate the correct suffix, on that assumption.

The code works, and I should now also be able to output sequentially-numbered .PPM Files, in the case of input streams.

But, this type of problem – a 64-bit system instead of a 32-bit system – may also be a reason why the tight allocators used elsewhere, may have failed. I just glossed over that issue in the other example, by writing a more-generous use of ‘calloc()’ .

(Edit 19h00 : )

I just reverified that the above code-correction was correct. I did so, by making sure that I was using it, recompiling, copying to the root file system, and testing again.

A fact which is worth mentioning, is that on 64-bit CPUs, 8-bit operations are deprecated. In fact, many 16-bit operations are also deprecated, even though “Discipulus” creates Genetic Algorithms, which consist of 16-bit instructions – some of which are no longer available. Addresses are expected to be 32-bit aligned for optimal performance.

The reason why the legacy C functions like ‘strcpy()’ still exist, is because for 64-bit machines, they have been hand-coded in assembler, to preserve the 8-bit logic of their operation. In fact, legacy, 8-bit ASCII is partially deprecated.

Line 34 of the code above, could also be rewritten like so:

 

        **suffix = (unsigned char) ( 0 );         /* remove dot */

 

One negative side-effect of that would be, that ‘suffix’ will be sent back as starting on an odd byte-address 50% of the time, which would at the least cause performance penalties…

The reason why omitting this causes problems, has to do with the additional fact that unlike its successor, C++, C is not “type-safe”. That means that unlike how it is for C++, the data-type and unit-width of what is being computed, is determined entirely by what has been stated explicitly, on the right-hand size of the assignment operator. It then gets assigned to what’s on the left – without taking into consideration, what’s on the left, as long as there is a loose correspondence. In other words, because a ‘char’, a ‘short int’, an ‘int’, and a ‘long int’ are all integers, the compiler will allow one to be assigned to the other, but will not perform any implicit type-conversions in the background.

And then, in order for the constant literal ‘0’ to be equivalent to ‘NULL’, they both need to have 64-bit definitions, while ‘\0′ is defined as an 8-bit entity.


 

(Update 05/21/2018 : )

One concept which I mentioned above somewhat loosely, was that a correlation can be determined, between a reference tile, and the corresponding pixels in a target area in the image. Of course, in order to write working code, we don’t just need such loose descriptions, but Mathematically correct algorithms. And so, I turn to my reference-sheet on Regression Analysis:

http://dirkmittler.homeip.net/Regression-Analysis.pdf



If the equations linked to above are to be applied to the example of this posting, then the main assumption would be, that the values of (x) are pixel-values in the tile, while the values of (y) are pixel-values in the target area. There are exactly two types of Regression Analysis which might be used:

  1. A form that assumes a Y-intercept of zero,
  2. A form that assumes a non-zero Y-intercept.

If Form (1) above is used, this will make the code much, much simpler to write, and as usual, if the product of the correlation and the tile-pixel-values is subtracted from the target area within the image, after that, the correlation will have become zero. Also, the correlation of the tile with itself will be (1).

But then the disadvantage would be, that the non-zero components of the reconstructed image – the Y-intercepts – will be limited to a single array of average colors – blocks – which were subtracted from the target image once, before correlation was computed, with no way to introduce color-values afterward. And this might lead to poor image-accuracies.

Some but not all users might want poor image-accuracy for effect, but high accuracy should eventually be a goal.

And so Form (2) above of Regression Analysis can also be used. If it is, then to compute the coefficients for each area in the target image becomes much more complicated, because the mean of the pixel-values, both in the target area, as well as in the tile, must be computed for each case. But then, the advantage this will bring is, that a new color-value can be added to each transformation of the tile into the target-area, and in the case of an initial 8×8 tiling for example, it means that many more color values can be incorporated, than merely 64. Each of the introduced color-values would then correspond to the non-zero Y-intercepts.

Being able to add a new color-value for each application of the tile would eventually have as benefit, that images could still be reproduced fairly well, which did not exhibit much self-similarity, since any image is eventually an array of color values of some size.

But eventually, either conventional approach out of my reference-sheet will lead to logic issues, about how an initial image is to arise within the decoder, out of averages, for which self-similarities have been computed, with the initial image, but how those averages were supposed to result as Y-intercepts, within the encoder, following from the degrees of image-similarity. I think that the only feasible solution to this issue is, that from the reference tile which is used to determine degrees of image-similarity, the average color-value of the image is subtracted, so that the mean of (x) actually becomes zero, at which point the system will have both an X-intercept and a Y-intercept of zero. Then, the amount of correlation can both be computed and applied, after the average is applied, for the target rectangle belonging to the entire image.


 

One limitation which can arise, just from intending to store a large number of ‘color-values’, is the fact that doing so, would add to the output file-size. If the above image only consumed 1.3kB, then there was not even much space, to store (8×8 + 16×16 + 32×32) color-values. I suppose that a bit-map of those sizes could still be stored, to indicate for which target-areas a non-zero application was suggested by the Math, and that ‘color-values’ could next be stored as a list, corresponding to all the bits in the bit-map, that were ones and not zeroes…

Whether the bits in the bit-map are ones could depend, on whether the values which need to be stored, exceed a threshold, which is derived from the quality-level, that defaults to 20/100, but that can be set to a non-default level from the command-line. A bit for chroma might only be set, if the corresponding bit for luma was set first. And, if a suitable form of Regression Analysis was used, then the luma-bit would be set, if either the correlation, or the Y-luma-intercept exceeded the threshold, both of which would get stored as a result. And so a conclusion which emerges would be, that an operation must be performed often, to store a signed value as 8 bits, yet to determine whether the absolute of that value exceeds some threshold. Further, the first bit-map – corresponding to luma – could be assumed to consist entirely of ones, so that its storage-requirement could also be eliminated.

And the first set of luma-values could also be assumed non-negative.

The way the Fiasco file-format tries to cope with this, is by defining a limited luma-dictionary, and a limited chroma-dictionary.

In 2D computer graphics, images are often stored as (1-channel) luminance values, separately from (2-channel) chroma values. The Fiasco file-format defaults to allowing a luma-dictionary with up to 10,000 entries – which is equivalent to having no restrictions – as well as a chroma-dictionary limited to 40 values.

These parameters can be changed by the user, from the command-line. But such parameters allow for the generated files to be quite small, as they are.

I suppose that an additional question could be asked: ‘How can correlation be computed, both as existing for luma, and as existing for chroma?’ And my guess would be that the original programmers needed to cut corners somewhere. And one way they may have cut corners, would have been to compute correlation purely on the basis of luma.

1: )

A fact which I must admit, is that this format for storing images (named “Fiasco”) is a format which exists in its own right, but which deviates from the Mathematically-correct definition of what a fractal is. According to true Math, a fractal is defined by just one equation. And another format which exists, is called “IFS”, or “Iterated Function System”. Even with IFS, the idea is that each equation which defines a fractal image, applies all the time, to the entire image, but each equation with a different probabalistic weight, that corresponds to that member-equation’s surface-area, which is also the determinant of one equation’s matrix.

What usually does not happen with true fractals, is that a different parameter can be applied to each rectangle that’s a part of an image, to specify to what degree that rectangle resembles the entire image. And it would be even less-correct for a fractal, that each of these blocks can be subdivided into smaller blocks, such that again, the degree of image-similarity of each sub-block, could be rated separately.

But with Fiasco format, this is what happens.


 

The question could be asked, as to how high-quality the same photo as the first one in this posting could be made, even though it’s being compressed as a fractal, if the parameters were set to favor quality, and not just to distort the image, and if we can apply an initial tiling of 16×16 instead of 8×8. And then, the fact would need to be mentioned that the example here has been derived from a mere 200×200 pixel, 18.6kB .GIF Image. And the highest-quality result that I can obtain, takes up 3.1kB as a .WFA File, which has been reproduced below:

dirk-fract-hq4

 

2: )

When I custom-compiled ‘netpbm’, I had to respect the fact that I needed to keep the repository version installed, simply because that package was also a dependency of several other Debian packages. The version in the Debian / Stretch repository, happens to be version 10.x . Well the version which I custom-compiled was version 11.y . What this means, was that to custom-compile also produced a different version of ‘libnetpbm.so’ . But, because the repositories already offer to install either v9 or v10 of ‘libnetpbm’, it was possible for me to put both library-versions into ‘/usr/lib’, and then simply to copy the individual programs into ‘/usr/local/bin’, which I intend to override. The result looks something like this:

 


dirk@Plato:~$ cd /usr/lib
dirk@Plato:/usr/lib$ ls -l libnetpbm*
lrwxrwxrwx 1 root root     17 Jan 30  2016 libnetpbm.so.10 -> libnetpbm.so.10.0
-rw-r--r-- 1 root root 146384 Jan 30  2016 libnetpbm.so.10.0
lrwxrwxrwx 1 root root     18 May 18 13:11 libnetpbm.so.11 -> libnetpbm.so.11.73
-rwxr-xr-x 1 root root 290472 May 18 13:10 libnetpbm.so.11.73
dirk@Plato:/usr/lib$ which pnmtofiasco
/usr/local/bin/pnmtofiasco
dirk@Plato:/usr/lib$ which fiascotopnm
/usr/local/bin/fiascotopnm
dirk@Plato:/usr/lib$


 

And, this survives an ‘ldconfig’ command.


 

3: )

I have a hunch that when encoding, the optimization passes require fewer computations than the first pass did, because two things are no longer done:

  1. The product of the reference tile, with the correlation, is no longer subtracted from the source image,
  2. A new reference tile is no longer reduced, from an anticipated decoded image.

But of course when decoding, the intent is easily, to create greater detail than the source image had, for which reason step (2) above would still be carried out.


 

(Update 05/24/2018 : )

On the side, the question could be asked, how a single array of self-correlations can be applied to an image (n) levels deep, when decoding, without resulting in ‘double-counting’. The thought has occurred to me, that the following procedure could be applied:

  1. The buffer that’s to hold the resulting image could be filled with an array of average-colors – so-called color-blocks.
  2. The image-average color of that buffer could be computed.
  3. The contents of this buffer could be reduced to the size of one tile, and the tile replaced with the result.
  4. The product of the difference, between tile-pixel-colors and the image-average color, with the self-correlation for one position in the grid, could be added to each position in the grid.
  5. Steps (3) and (4) could be repeated (n – 1) times.

The problem with this procedure would be, that the first self-correlation would be added once, to the output-buffer, while the second self-correlation would actually get added to the first, if (n) was equal to (2). This can result in individual pixel-values going out of range ( :4 ) . And so an alternative procedure which will avoid such a result, regardless of how high (n) becomes, could be:

  1. The buffer that’s to hold the resulting image could be filled with an array of average-colors – so-called color-blocks.
  2. The degrees of correlation of each of the blocks would need to be compensated. ( :5 )
  3. The contents of this buffer could be reduced to the size of one tile, and the tile replaced with the result.
  4. The pixel-colors of the image-buffer, belonging to one position within the grid, could be multiplied by (1 – the self-correlation for that position).
  5. The product of the tile-pixel-colors, with the self-correlation for the same position in the grid, could be added to each position in the grid.
  6. Steps (3 – 5) could be repeated (n – 1) times.

 

Only, the second procedure above would have as side-effect, that even though they get used as starting-values, block-averages would not be preserved. Block-averages would get replaced by image-averages, to the same degree as by which block-pixel-values get replaced with tile-values.


 

I’m still making the assumption, that in the ‘Fiasco’ format, color images only give partial support, to the encoding and decoding of the chroma-values (see above). And so I’m given two relevant observations:

  1. The self-correlations were supposedly only computed from the luma-values, not from the original chroma-values, and thus not assumed to be completely accurate for chroma-values,
  2. Chroma-values are already of such a nature, that they are signed values, stored in 8-bit fields with an offset of (128).

Given those observations, I would suggest that an image-average chroma value never needs to be computed, and that these signed values can just be multiplied, by the available self-correlations, as-they-are.

(Update 05/25/2018 : )

4: )

Some people might suggest, in case self-correlates from 2 passes are being added, the only measure that’s needed to prevent an out-of-gamut pixel, is to halve the correlation-coefficients once, before the technique is applied, which I first described above.

But the problem with this assumption would be the fact, that the steps I wrote would add the correlate once, would add the same correlate again, and would then add a fraction of the second-order correlate, that corresponds to the scale-factor squared. In other words, the factor by which the coefficients would need to be reduced, would actually be greater than two, so that the actual coefficients would be less than half, the original coefficients.

And so the exact answer to what reduction-factor is needed, to guarantee barely that pixels don’t go out-of-gamut, is a basic algebra question, which I could not be bothered to solve by hand. Instead, I used ‘wxMaxima’ to solve this question as below. JavaScript from the domain ‘mathjax.org’ must be allowed by the reader’s script-blocker, before he or she can read the formula:



But, to apply such a scale-factor would also mean, that the degree by which a second-order correlate is added to the magnified image, is proportional to x2 , and thus greatly-attenuated!

5: )

  • If the approach was taken, to use alpha-blending to recurse the self-similarity, with which a pixel-map is to be reproduced from the fractal representation (see above), and
  • If the original degree of self-correlation of each block was (p), and
  • If the number of iterations / levels of recursion were (n), then

A derived degree of self-correlation (p’) would need to be computed for each transformation, such that:

 


p' = 1 - (1 - p)(1/n)

 

(Edit 05/26/2018 : )

6: )

It would seem that the conclusion is inevitable, that if we want to decode a Fractal, into spaces that overlap, but not only with geometry information, also with color-information intact, especially the Fiasco-format, then the amount of resource we’ll need will grow, proportionally with the number of levels that we want to recurse.

But, I have come up with a possible way, in which this consumption of resources can be minimized, in the case of Fiasco-format:

 

  1. The buffer that’s to hold the resulting image could be filled with an array of average-colors – so-called color-blocks.
  2. The image-average color of that buffer could be computed, and could also be referred to as (tile 0).
  3. (n) could be initialized as (1).
  4. The contents of this buffer could be reduced to the size of (tile n), and that tile replaced with the result.
  5. The product of the difference, between (tile n) pixel-colors and (tile n-1) pixel-colors, with the self-correlation for one position in the grid, could be added to each position in the grid.
  6. (n) could be incremented.
  7. Steps (4 – 6) could be repeated, until (n) reaches the desired number of levels to go deep, or, until going (n) levels deep, reaches the target resolution, that we’d want to magnify our Fractal representation to.

 

Dirk

 

Print Friendly, PDF & Email

One thought on “pnmtofiasco Bug (Solved)”

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.