formula to pick every pixel in a bitmap without repeating

心不动则不痛 提交于 2019-12-04 07:22:41

if you got enough memory space to store all the pixel positions you can shuffle them:

const int xs=640;            // image resolution
const int ys=480;
color pixel[sz];             // image data
const int sz=xs*ys;          // image size 
int adr[sz],i,j;
for (i=0;i<sz;i++) adr[i]=i; // ordered positions
for (i=0;i<sz;i++)           // shuffle them
 {
 j = random(sz);             // pseudo-randomness with uniform distribution 
 swap(pixel[i],pixel[j]);
 }

this way you got guaranteed that each pixel is used once and most likely all of them are shuffled ...

You need to implement a pseudo-random number generator with a theoretically known period, which is greater than but very close to the number of elements in your list. Suppose R() is a function that implements such a RNG.

Then:

for i = 1...N
    do
        idx = R()
    while idx > N
    output element(idx)
end
  • If the period of the RNG is greater than N, this algorithm is guaranteed to finish, and never output the same element twice
  • If the period of the RNG is close to N, this algorithm will be fast (i.e. the do-while loop will mostly do 1 iteration).
  • If the RNG has good quality, the visual output will look pleasant; here you have to do experiments and decide what is good enough for you

To find a RNG that has an exactly-known period, you should examine theory on RNGs, which is very extensive (maybe too extensive); Wikipedia has useful links. Start with Linear congruential generators: they are very simple, and there is a chance they will be of good enough quality.

Here's a working example based on linear feedback shift registers. Since an n-bit LFSR has a maximal sequence length of 2n−1 steps, this will work best when the number of pixels is one less than a power of 2. For other sizes, the pseudo-random coordinates are discarded until one is obtained that lies within the specified range of coordinates. This is still reasonably efficient; in the worst case (where w×h is a power of 2), there will be an average of two LSFR iterations per coordinate pair.

The following code is in Javascript, but it should be easy enough to port this to Swift or any other language.

Note: For large canvas areas like 1920×1024, it would make more sense to use repeated tiles of a smaller size (e.g., 128×128). The tiling will be imperceptible.

var lsfr_register, lsfr_mask, lsfr_fill_width, lsfr_fill_height, lsfr_state, lsfr_timer;
var lsfr_canvas, lsfr_canvas_context, lsfr_blocks_per_frame, lsfr_frame_rate = 50;

function lsfr_setup(width, height, callback, duration) {
    // Maximal length LSFR feedback terms
    // (sourced from http://users.ece.cmu.edu/~koopman/lfsr/index.html)
    var taps = [ -1, 0x1, 0x3, 0x5, 0x9, 0x12, 0x21, 0x41, 0x8E, 0x108, 0x204, 0x402,
                 0x829, 0x100D, 0x2015, 0x4001, 0x8016, 0x10004, 0x20013, 0x40013,
                 0x80004, 0x100002, 0x200001, 0x400010, 0x80000D, 0x1000004, 0x2000023,
                 0x4000013, 0x8000004, 0x10000002, 0x20000029, 0x40000004, 0x80000057 ];
    nblocks = width * height;
    lsfr_size = nblocks.toString(2).length;
    if (lsfr_size > 32) {
        // Anything longer than about 21 bits would be quite slow anyway
        console.log("Unsupposrted LSFR size ("+lsfr_size+")");
        return;
    }
    lsfr_register = 1;
    lsfr_mask = taps[lsfr_size];
    lsfr_state = nblocks;
    lsfr_fill_width = width;
    lsfr_fill_height = height;
    lsfr_blocks_per_frame = Math.ceil(nblocks / (duration * lsfr_frame_rate));
    lsfr_timer = setInterval(callback, Math.ceil(1000 / lsfr_frame_rate));
}

function lsfr_step() {
    var x, y;
    do {
        // Generate x,y pairs until they are within the bounds of the canvas area
        // Worst-case for an n-bit LSFR is n iterations in one call (2 on average)
        // Best-case (where w*h is one less than a power of 2): 1 call per iteration
        if (lsfr_register & 1) lsfr_register = (lsfr_register >> 1) ^ lsfr_mask;
        else lsfr_register >>= 1;
        y = Math.floor((lsfr_register-1) / lsfr_fill_width);
    } while (y >= lsfr_fill_height);
    x = (lsfr_register-1) % lsfr_fill_width;
    return [x, y];
}

function lsfr_callback() {
    var coords;
    for (var i=0; i<lsfr_blocks_per_frame; i++) {
        // Fetch pseudo-random coordinates and fill the corresponding pixels
        coords = lsfr_step();
        lsfr_canvas_context.fillRect(coords[0],coords[1],1,1);
        if (--lsfr_state <= 0) {
            clearInterval(lsfr_timer);
            break;
        }
    }
}

function start_fade() {
    var w = document.getElementById("w").value * 1;
    var h = document.getElementById("h").value * 1;
    var dur = document.getElementById("dur").value * 1;
    lsfr_canvas = document.getElementById("cv");
    lsfr_canvas.width = w;
    lsfr_canvas.height = h;
    lsfr_canvas_context = lsfr_canvas.getContext("2d");
    lsfr_canvas_context.fillStyle = "#ffff00";
    lsfr_canvas_context.fillRect(0,0,w,h);
    lsfr_canvas_context.fillStyle = "#ff0000";
    lsfr_setup(w, h, lsfr_callback, dur);
}
Size:
<input type="text" size="3" id="w" value="320"/>
×
<input type="text" size="3" id="h" value="240"/>
in
<input type="text" size="3" id="dur" value="3"/>
secs
<button onclick="start_fade(); return 0">Start</button>
<br />
<canvas id="cv" width="320" height="240" style="border:1px solid #ccc"/>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!