简体   繁体   English

尝试将图像数据转换为高度图

[英]Trying to convert imagedata to an heightmap

I'm just trying to convert imagedata to an heightmap, to show in on the canvas. 我只是想将图像数据转换为高度图,以显示在画布上。 But when i do this, a strange thing appears, for all the images I tested. 但是,当我这样做时,对于我测试的所有图像,都会出现一个奇怪的事情。

Here is my code : 这是我的代码:

 window.onload = function() { var canvas = document.getElementById('game'); if(!canvas) { alert("Impossible de récupérer le canvas."); return; } var context = canvas.getContext('2d'); if(!context) { alert("Impossible de récupérer le contexte du canvas."); return; } var img = new Image(); img.src = "noise.png"; var size = 250000; var data = new Float32Array(size); var pxlData = new Array(size); for ( var i = 0; i < size; i ++ ) { data[i] = 0 } for (var i = 0; i < size; i++) { pxlData[i] = new Array(4); pxlData[i][0] = 0; pxlData[i][1] = 0; pxlData[i][2] = 0; } img.onload = function() { context.drawImage(img, 0, 0); var imgd = context.getImageData(0, 0, 500, 500); context.clearRect(0, 0, canvas.width, canvas.height); var pix = imgd.data; var j=0; var x=0; var y=0; var i=0; for (var i = 0, n = pix.length; i < n; i += (4)) { var all = pix[i]+pix[i+1]+pix[i+2]; pxlData[j][0] = pix[i]; pxlData[j][1] = pix[i+1]; pxlData[j][2] = pix[i+2]; pxlData[j][3] = pix[i+3]; data[j++] = all/3; } var alpha; for(y = 0; y < 500; y++) { for(x = 0; x < 500; x++) { if(data[x * y] <= 100){ context.fillStyle = "blue"; }else if(data[x * y] >= 100){ context.fillStyle = "green"; } //context.fillStyle = 'rgba('+ data[x * y] +', '+ data[x * y] +', '+ data[x * y] +', 1)'; context.fillRect(x, y, 1, 1); // context.fillStyle = 'rgba('+ pxlData[x * y][0] +', '+ pxlData[x * y][1] +', '+ pxlData[x * y][2] +', '+ pxlData[x * y][3] +')'; // context.fillRect(x, y, 1, 1); } } }; } 
 <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="style.css" type="text/css"> <script type="text/javascript" src="game.js"></script> <title>Génération de terrain</title> </head> <body> <canvas id="game" width="500" height ="500">Votre navigateur ne supporte pas les canvas.</canvas> </body> </html> 

That's what it's looking like when i run it : 那就是我运行它时的样子:

canvas 帆布

The error is how you index the pixels in the 32 bit float array. 错误是如何索引32位浮点数组中的像素。

You have data[x * y] that means the pixel at 0,0 will be at index 0 * 0 = 0 and pixel at 0,100 will also be at 0 * 100 = 0 and all other indexes will be wrong. 您有data[x * y] ,这意味着0,0处的像素将位于索引0 * 0 = 0而0,100处的像素也将位于0 * 100 = 0 ,所有其他索引都将是错误的。 To get the correct pixel address use x + y * width when indexing from an array where one item is a pixel. 要从一个像素为像素的数组索引时,使用x + y * width来获取正确的像素地址。 If indexing into pixel data 'imageData.data' each pixel is 4 items (r,g,b,a) so you would use data[x * 4 + y * canvas.width * 4] or more simply imageData.data[x + y * canvas.width * 4] 如果索引到像素数据“ imageData.data”,则每个像素为4个项(r,g,b,a),因此您将使用data[x * 4 + y * canvas.width * 4]或更简单地使用imageData.data[x + y * canvas.width * 4]

Looking at your code you have create some common mistakes that will make you code run very slow compared to what it could do. 查看您的代码,您会创建一些常见的错误,这些错误会使您的代码运行速度大大降低。 I have simplified your code. 我简化了您的代码。 It does the same but without all the overhead. 它执行相同的操作,但没有所有开销。 I have added comments, removing your code and suggesting alternative methods of doing the same. 我添加了注释,删除了您的代码并建议了执行此操作的替代方法。

The biggest change is the rendering green and blue loops. 最大的变化是渲染绿色和蓝色循环。 You where setting each pixel with context.fillRect(x,y,1,1); 您在其中使用context.fillRect(x,y,1,1);设置每个像素 this is very very slow. 这非常非常慢。 Rather than draw a rectangle for each pixel use the imageData you got and fill the colour after you read the height then just put that data back onto the canvas. 与其为每个像素绘制一个矩形,不如使用获得的imageData并在读取高度后填充颜色,然后将其放回画布即可。 I used two typeArray views to set and read the data this also improved the performance. 我使用了两个typeArray视图来设置和读取数据,这也提高了性能。

// convert r,g,b,a to 32 bit colour using correct little or big endian
function create32Pixel(r, g, b, a){  // dont call this inside loops as very slow
    var endianConvert = new Uint8ClampedArray(4); // use to convert to correct endian 
    var endianConvert32 = new Uint32Array(endianConvert.buffer); 
    endianConvert[0] = r;
    endianConvert[1] = g;
    endianConvert[2] = b;
    endianConvert[3] = a;
    return endianConvert32[0];
}

window.onload = function()
{
    var canvas = document.getElementById('game');
    if(!canvas)
    {
        alert("Impossible de récupérer le canvas.");
        return;
    }

    var context = canvas.getContext('2d');
    if(!context)
    {
        alert("Impossible de récupérer le contexte du canvas.");
        return;     
    }

    var img = new Image();
    img.src = "noise.png";

    var size = 250000;  
    // Do you really need floats?? 16 bit unsigned int  array can hold 255 * 3 and all javascript 
    // numbers are converted to 64 bit floats so you will not lose precision from original when manipulating the 16bit values.
    // following array is not needed.
    //var dataFloat = new Float32Array(size);
    // following array is not needed.
    //var pxlData = new Array(size);  // bad way to create an array
    //var pxlData = [];  // create empty array and push onto it.


    // can use dataFloat.fill()
    /*for ( var i = 0; i < size; i ++ ) {
        dataFloat[i] = 0
    }*/
    //dataFloat.fill(0); // but not needed as array is zeroed when created (not from an existing buffer)

    // Very inefficient as you are creating a new array for every pixel. Use flat array instead. 
    /*for (var i = 0; i < size; i++) 
    {
        pxlData[i] = new Array(4);
        pxlData[i][0] = 0;
        pxlData[i][1] = 0;
        pxlData[i][2] = 0;
    }*/
    // should do 
    /*var i;
    while(i < size * 4){
        pxlData[i++] = 0;  // array grows as you increase i;
    }*/

    img.onload = function() 
    {
        context.drawImage(img, 0, 0);

        var imgd = context.getImageData(0, 0, canvas.width, canvas.height);

        // don't need to clear
        // context.clearRect(0, 0, canvas.width, canvas.height);

        // make two views one 8bit and the other 32bit. Both point to the same data change one
        // changes the other
        var pixChannels = imgd.data;
        var pixels = new Uint32Array(pixChannels.buffer);

        var j,x,y,j;
        j = x = y = i = 0;


        // Create pixel colours. Need to ensure correct order as some systems
        // use little edian and others big endian
        // see https://en.wikipedia.org/wiki/Endianness for info.
        var green = create32Pixel(0,255,0,255);
        var blue = create32Pixel(0,0,255,255);


        // use j as 32bit pixel index and i as 8bit index
        // read the height and set pixel colour accordingly.
        while(j < pixels.length){
            var height = pixChannels[i++] + pixChannels[i++] + pixChannels[i++];
            if(height <= 300){  // no need to divide by 3 just test for 3 time 100
                pixels[j++] = blue;
            }else{
                pixels[j++] = green;
            }
            i++; // skip alpha channel
        }
        context.putImageData(imgd,0,0); // put pixels back to canvas.

    };



}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM