【问题标题】:Canvas Rotate, toDataUrl, and then Crop is ruining image qualityCanvas Rotate,toDataUrl,然后 Crop 正在破坏图像质量
【发布时间】:2019-07-28 20:16:18
【问题描述】:

我有一张允许用户向任何方向旋转 90 度的图像。每次旋转时,我使用画布执行图像操作,然后保存canvas.toDataURL("image/png", 1) 返回的数据。

问题是每次旋转图像时图像质量都会下降。

我的最终目标是在不损失图像质量的情况下旋转矩形图像并保存新的数据 url。

function rotateAndSave(image: HTMLImageElement, degrees: number): string {
  const imageWidth = image.naturalWidth;
  const imageHeight = image.naturalHeight;
  const startedHorizontalEndedVertical = imageWidth > imageHeight;
  const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;

  const canvas = document.createElement("canvas");
  canvas.width = canvasSize;
  canvas.height = canvasSize;

  const ctx = canvas.getContext("2d");
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // center and rotate canvas
  const translateCanvas = canvasSize / 2;
  ctx.translate(translateCanvas, translateCanvas);
  ctx.rotate(degrees * Math.PI / 180);

  // draw from center
  const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
  const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
  ctx.drawImage(image, translateImageX, translateImageY); 

  // I got 'cropPlusExport' from another stackoverflow question.
  function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight) {
    // create a temporary canvas sized to the cropped size
    const canvas1 = document.createElement('canvas');
    canvas1.width = cropWidth;
    canvas1.height = cropHeight;
    const ctx1 = canvas1.getContext('2d');
    ctx1.setTransform(1, 0, 0, 1, 0, 0);
    ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
    // use the extended from of drawImage to draw the
    // cropped area to the temp canvas
    ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
    return canvas1.toDataURL("image/png", 1);
  }

  // Start Cropping
  let squareImage = new Image();
  squareImage.src = canvas.toDataURL("image/png", 1);
  squareImage.onload = () => {
    const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
    const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
    const sw = imageHeight;
    const sh = imageWidth;
    const data = cropPlusExport(squareImage, sx, sy, sw, sh);

    // Update DOM via angular binding...
    const dataUrl = data.split(",")[1];
    this.imageSource = dataUrl;

    squareImage = null;
  }

示例 html

<div class="view">
  <img [src]="imageSource" />
</div>

请记住,我正在裁剪为图像的自然宽度和高度。所以,奇怪的是,如果我不裁剪,那么图像质量不会改变,但是当我裁剪时,图像质量会改变。

【问题讨论】:

    标签: javascript image image-processing canvas


    【解决方案1】:

    画布绘图是有损的,旋转图像会导致像素的硬修改。所以确实,如果你总是从最后一个状态开始,你最终会为你的图像添加越来越多的工件。

    只需将原始图像存储在某个地方,并始终从那里开始,而不是使用修改后的版本。

    // will fire in a loop
    img.onload = e => elem.rotateAndSave(1);
    
    const elem = {
      // store a copy of the original image
      originalimage: img.cloneNode(),
      angle: 0,
      rotateAndSave(degrees) {
        // always use the stored original image
        const image = this.originalimage;
        // keep track of current transform
        this.angle += degrees;
        const imageWidth = image.naturalWidth;
        const imageHeight = image.naturalHeight;
        const startedHorizontalEndedVertical = imageWidth > imageHeight;
        const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
    
        const canvas = document.createElement("canvas");
        canvas.width = canvasSize;
        canvas.height = canvasSize;
    
        const ctx = canvas.getContext("2d");
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    
        // center and rotate canvas
        const translateCanvas = canvasSize / 2;
        ctx.translate(translateCanvas, translateCanvas);
        ctx.rotate(this.angle * Math.PI / 180);
    
        // draw from center
        const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
        const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
        ctx.drawImage(image, translateImageX, translateImageY);
    
        // I got 'cropPlusExport' from another stackoverflow question.
        function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight) {
          // create a temporary canvas sized to the cropped size
          const canvas1 = document.createElement('canvas');
          canvas1.width = cropWidth;
          canvas1.height = cropHeight;
          const ctx1 = canvas1.getContext('2d');
          ctx1.setTransform(1, 0, 0, 1, 0, 0);
          ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
          // use the extended from of drawImage to draw the
          // cropped area to the temp canvas
          ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
          return canvas1.toDataURL("image/png", 1);
        }
    
        // Start Cropping
        let squareImage = new Image();
        squareImage.src = canvas.toDataURL("image/png", 1);
        squareImage.onload = () => {
          const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
          const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
          const sw = imageHeight;
          const sh = imageWidth;
          const data = cropPlusExport(squareImage, sx, sy, sw, sh);
    
          // Update DOM via angular binding...
          img.src = data;
        }
      }
    };
    &lt;img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img"&gt;

    【讨论】:

    • 在一次轮换中损失如此之多,以至于即使这个解决方案在我的限制下也不合理。感觉好像有一种方法可以减少伪影,但也许没有。例如,如果我只是旋转图像,质量很好。但是旋转后裁剪不是吗?而且,只裁剪未旋转的图像就可以了吗?为什么将两者放在一起会如此糟糕(特别是因为旋转和裁剪是在两个不同的画布上完成的)?
    • 另外,如果图片是正方形,质量很好。
    • ?您的图像上应用了一些 css 吗?在 sn-p 中质量对我来说很好。
    • 好吧,我还是要保存图像,所以如果它们旋转然后刷新,图像会在大约 4 次旋转后变得非常糟糕。不完全是我想在生产代码中放入的内容。
    • 然后保存原始图像和当前变换。在不知道您在做什么的情况下,我们无法帮助您。当您旋转图像时,您无法保持图像的原始质量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-08
    • 2017-10-18
    • 2017-10-27
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 2020-09-27
    相关资源
    最近更新 更多