在 p5js 上显示来自本地主机的图像时出错 Canvas

Error displaying an image from localhost on p5js Canvas

问题:

我正在学习 p5.js 并且我正在关注来自 Coding Train YouTube 频道的教程。一切都很好,直到我不得不在 Image 对象上调用库函数。问题是我已经在对象 p 中实例化了库,并且我正在通过 p 对象使用它的变量。我不知道为什么它不能识别 loadPixels() 函数。在教程中,该函数工作正常。

错误信息:

p5.js 说:出现错误,因为“loadPixels”无法作为函数调用

(on line 17 in help.html [file:///G:/Android/help.html:17:11]).
Verify whether "img" has "loadPixels" in it and check the spelling, letter-casing (Javacript is case-sensitive) and its type.
For more: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_a_function#What_went_wrong

代码:

<!DOCTYPE html>
<head>
  <script src='p5/p5.js'></script>
</head>
<body>
  <div id='container'></div>
  <script>
  let sketch = function(p) {
    p.setup = function(){
      p.createCanvas(56, 56);
      img = new Image();
      img.src = "scott.jpg";
    }
    p.draw = function() {
        // p.drawingContext.drawImage(img, 0, 0);
      p.loadPixels();
      img.loadPixels();
      for (var x=0; x<p.width; x++) {
        for (var y=0; y<p.height; y++) {
          // var d = p.dist(x, y, p.width/2, p.height/2);
          var loc = x+y+p.width;
          // p.pixels[loc] = p.color(d);
          p.pixels[loc] = img.pixels[loc];
        }
      }
    }
  p.updatePixels();
  };
  new p5(sketch, 'container');
  </script>
</body>
</html>

编辑: 正如有人指出的那样,问题是我正在使用 Image(),它是 javascript 的默认图像 class。我对我的代码做了一些更改,但现在它给了我这个错误。

错误:-

Uncaught DOMException: The operation is insecure. help.html:18
    openWindow file:///G:/Android/help.html:18
    onclick file:///G:/Android/help.html:1

代码:-

<!DOCTYPE html>
<head>
  <script src='p5/p5.js'></script>
</head>
<body>
  <button onclick="openWindow()">click me to open a new window.</button>
  <div id='container'></div>
  <script>
    function openWindow() {
        var newWindow = window.open("", "Import Image", "height=56,width=56,status=yes,toolbar=no,menubar=no,location=no");  

        newWindow.document.write("<canvas id='imagePlaceholder'>Canvas not supported!</canvas>");
        var canvas = newWindow.document.getElementById("imagePlaceholder");
        var ctx = canvas.getContext("2d");

        ctx.drawImage(img, 0, 0);
        // console.log(ctx.getImageData(0, 0, 56, 56).data);
        dest = ctx.getImageData(0, 0, 56, 56).data;

    }


  let sketch = function(p) {
    p.setup = function(){
      p.createCanvas(56, 56);
      img = new Image();
      img.src = "scott.jpg";
      let dest = p.createImage(56, 56);
      console.log(img);
    }
    p.draw = function() {
        // p.drawingContext.drawImage(img, 0, 0);
      // p.loadPixels();
      img.loadPixels();
      for (var x=0; x<p.width; x++) {
        for (var y=0; y<p.height; y++) {
          // var d = p.dist(x, y, p.width/2, p.height/2);
          var loc = x+y+p.width;
          // p.pixels[loc] = p.color(d);
          p.pixels[loc] = img.pixels[loc];
        }
      }
    }
  p.updatePixels();
  };
  new p5(sketch, 'container');
  </script>
</body>
</html>

我尝试了很多东西,几乎放弃了,但最后我不得不稍微更改一下代码,这对我有用。虽然我得到的是 base64 url 正如 Alice 在评论中所建议的那样,但我将其转换为 Uint8ClampedArray。现在,如果有人想要完整图像或图像的所有像素,那么他们可以遵循此 link :- https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas 。我希望它对任何想要制作基于离线 webcanvas 的应用程序并且不想涉足 CORS 的人有所帮助。

var fileSelector = document.createElement('input');
                fileSelector.setAttribute('type', 'file');
                fileSelector.setAttribute('accept', 'image/gif, image/jpeg, image/png');

                fileSelector.click();
                fileSelector.onchange = function(e) {
                    img = new Image();
                    var file = e.target.files[0];

                    var reader = new FileReader();
                    reader.onloadend = function() {
                         img.src = reader.result;
                    }
                    reader.readAsDataURL(file);


                    var newWindow = window.open("", "_blank", "height=56,width=56,status=yes,toolbar=no,menubar=no,location=no");  
                    
                    newWindow.document.write("<canvas id='imagePlaceholder'>Canvas not supported!</canvas>");
                    var canvas = newWindow.document.getElementById("imagePlaceholder");
                    var ctx = canvas.getContext("2d");

                    ctx.drawImage(img, 0, 0);
                    // console.log(ctx.getImageData(0, 0, 56, 56).data);
                    var dest = ctx.getImageData(0, 0, img.width, img.height).data;
                    console.log(dest);
                    newWindow.close();
                }

这里一个明显的问题是您使用的是内置 Image() 构造函数,它创建了一个 HTMLImageTag(参见 the MDN Article) instead of creating a p5js p5.Image object (see the p5js Reference). However there are several other issues. In p5js you need to load images in the preload function to ensure they are available when you start drawing (this is an asynchronous operation). You'd have a much easier time drawing images in p5js using the built in image function. If you are going to use pixel arrays, you need to understand the structure of these arrays. They don't store Color objects, they store 4 separate numbers for each color channel (red, green, blue, and alpha). So the indices in the array are not (x + y * width), but ((x + y * width) * 4 + channel) where channel is a number from 0 to 3. Also you need to account for the fact that the canvas may have a pixel density > 1, whereas the image will have a pixel density of 1. I strongly suggest you read all of the documentation for the Image related p5js functions.

let sketch = function(p) {
  let img;
  
  p.preload = function() {
    img = p.loadImage("https://s3-ap-southeast-1.amazonaws.com/investingnote-production-webbucket/attachments/41645da792aef1c5054c33de240a52e2c32d205e.png");
  };
  
  p.setup = function() {
    p.createCanvas(200, 200);
  };
  
  p.draw = function() {
    // this would be a lot simpler way to draw the image:
    // p.image(img, 0, 0);
    
    p.loadPixels();
    img.loadPixels();
    // Handle high pixel density displays. This code effectively scale the image up so that each 1 pixel in the source image is density * density pixels in the display, thus preserving the size of the image but leading to visible pixelation.
    let density = p.pixelDensity();
    for (var x = 0; x < p.width && x < img.width; x++) {
      for (var y = 0; y < p.height && y < img.height; y++) {
        // There are 4 values per pixel in the pixels array:
        var srcLoc = (x + y * img.width) * 4;
        for (var xd = 0; xd < density; xd++) {
          for (var yd = 0; yd < density; yd++) {
            var destLoc =
              (x  * density + xd + (y * density + yd) * p.width * density) * 4;
            for (var i = 0; i < 4; i++) {
              p.pixels[destLoc + i] = img.pixels[srcLoc + i];
            }
          }
        }
      }
    }
  
    p.updatePixels();
  };
};

new p5(sketch, 'container');
<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>

<body>
  <div id='container'></div>
</body>

</html>

这是以不同方式处理 pixelDensity 的代码片段的替代版本:

let sketch = function(p) {
  let img;
  
  p.preload = function() {
    img = p.loadImage("https://s3-ap-southeast-1.amazonaws.com/investingnote-production-webbucket/attachments/41645da792aef1c5054c33de240a52e2c32d205e.png");
  };
  
  p.setup = function() {
    p.createCanvas(200, 200);
  };
  
  p.draw = function() {
    // this would be a lot simpler way to draw the image:
    // p.image(img, 0, 0);
    
    p.loadPixels();
    img.loadPixels();
    // Handle high pixel density displays. This code shrinks the image down by mapping one pixel in the source image to 1 / (density ^ 2) actual pixels in the canvas.
    let density = p.pixelDensity();
    for (var x = 0; x < p.width * density && x < img.width; x++) {
      for (var y = 0; y < p.height * density && y < img.height; y++) {
        // There are 4 values per pixel in the pixels array:
        var srcLoc = (x + y * img.width) * 4;
        var destLoc = (x + y * p.width * density) * 4;
        for (var i = 0; i < 4; i++) {
          p.pixels[destLoc + i] = img.pixels[srcLoc + i];
        }
      }
    }
  
    p.updatePixels();
  };
};

new p5(sketch, 'container');
<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>

<body>
  <div id='container'></div>
</body>

</html>

因为您是专门尝试从本地计算机加载图像而不是可公开访问的图像 URL,所以通过用户交互输入文件将是执行此操作的唯一方法。这是 Web 浏览器故意设置的限制,以防止恶意网页从您的本地文件中非法读取数据。然而,有一种更简单的方法可以从文件输入到您的 p5js canvas 中获取图像数据。事实上,这个确切的用例可以在 documentation for the createFileInput function.

中看到

let input;
let img;

function setup() {
  input = createFileInput(handleFile);
  input.position(0, 0);
}

function draw() {
  background(255);
  if (img) {
    image(img, 0, 0, width, height);
  }
}

function handleFile(file) {
  if (file.type === 'image') {
    img = createImg(file.data, '');
    img.hide();
  } else {
    img = null;
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>

<body>
</body>

</html>