试图将imagedata转换为高度图

时间:2016-09-24 16:40:35

标签: javascript canvas

我只是想将imagedata转换为高度图,以便在画布上显示。但是,当我这样做时,对于我测试的所有图像,都会出现奇怪的事情。

这是我的代码:

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>

这就是我运行它时的样子:

canvas

1 个答案:

答案 0 :(得分:0)

错误是如何索引32位浮点数组中的像素。

你有data[x * y]这意味着0,0处的像素将位于索引0 * 0 = 0处,0,100处的像素也将位于0 * 100 = 0处,而所有其他索引将是错误的。要从一个项目为像素的数组进行索引时,要获取正确的像素地址,请使用x + y * width。如果索引到像素数据'imageData.data',则每个像素为4个项目(r,g,b,a),因此您可以使用data[x * 4 + y * canvas.width * 4]或更简单的imageData.data[x + y * canvas.width * 4]

查看代码时,您会产生一些常见错误,这些错误会使代码运行速度变得非常慢。我简化了你的代码。它做的相同,但没有所有开销。我添加了注释,删除了您的代码并建议了相同的替代方法。

最大的变化是渲染绿色和蓝色循环。您使用context.fillRect(x,y,1,1);设置每个像素的位置非常慢。不是为每个像素绘制一个矩形,而是使用您获得的imageData并在读取高度后填充颜色,然后将该数据放回到画布上。我使用了两个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.

    };



}