56 | | ''Todo'' |
| 59 | There is a common misconception that values in an information stream should be bit-aligned. This is not the case at all in `img2twit`: the bitstream can store integer values with non-power of two bounds. |
| 60 | |
| 61 | As an illustration, take the example of 16-bit colour coding. There are basically two widespread ways of storing RGB data into a 16-bit word: either using 5 bits per component and wasting the last bit, or using 6 bits for the G value, because the human eye is more sensible to variations of green. The RGB triplet is then computed as ''(((R) * 64 + G) * 32 + B)'', and eg. the green value is retrieved as ''(RGB / 32) % 64''. Though 5-6-5 looks rather balanced, that's 32 values of red, 64 values of green, 32 values of blue: that's actually as many values of green as the other two put together! |
| 62 | |
| 63 | In this case, `img2twit`'s approach would be to use 40 values of each component, where the RGB triplet is computed as ''(((R) * 40 + G) * 40 + B)'', and eg. the green value is retrieved as ''(RGB / 40) % 40''. This makes the computations a lot slower (modulo 64 is just a bit shift, while modulo 40 requires dividing the whole bitstream by 40). However, it offers better packing and a lot more control over the data representation. For instance, I found that 2 bits (4 values) per colour component was not enough, but that 3 bits (8 values) was more than required. I went for 5 values (2.58 bits). |
| 64 | |
| 65 | One drawback is that this makes the bitstream completely unable to recover from bit corruptions. However, for such small streams I don't care at all. |
| 66 | |
| 67 | == Point selection == |
| 68 | |
| 69 | I use 16 values for the X and Y position of a point within a cell, and 6 values for each R, G and B component. This uses roughly 15.755 bits. There is a way to reduce this value down to 15.252 bits by accounting for the fact that two points within a cell are interchangeable, but I have not yet found an elegant way to remove the duplicate information without butchering a lot of code. |
| 70 | |
| 71 | == Algorithm seed == |
| 72 | |
| 73 | For the algorithm to converge quickly, the initial image needs to be as close as possible to an ideal solution. Of course the method works correctly with an initial random seed, but since the fitting is very slow, it can take 2 to 3 times as long to converge to the same solution. |
| 74 | |
| 75 | My current approach is to look at the current cell's brightest and darkest pixels, and to set the two initial points at these locations. There is a lot to be improved in this domain. |
| 76 | |
| 77 | == Image rendering == |
| 78 | |
| 79 | The final image is simply an interpolation of the control points stored in the image. I use natural neighbour interpolation because it is reasonably fast (remember that the main algorithm loop computes the changes caused by changes to points, and thus require at least a partial render). |
| 80 | |
| 81 | The rendering method is responsible for both the artifacts (mainly the embossed dots) and the nice edges found in some parts of the resulting images. |