JavaScript 图片 灰度算法 双线性插值

灰度算法(效果图

具体代码
 

// 将图像转换为灰度图,并保留Alpha通道
const convertToGrayscale = (imageData) => {
  const data = imageData.data
  for (let i = 0; i < data.length; i += 4) {
    const r = data[i]
    const g = data[i + 1]
    const b = data[i + 2]
    const a = data[i + 3] // 保留Alpha通道

    // 计算灰度值
    const gray = 0.299 * r + 0.587 * g + 0.114 * b

    // 将RGB通道设置为灰度值,Alpha通道保持不变
    data[i] = data[i + 1] = data[i + 2] = gray
    data[i + 3] = a // 保持Alpha通道值
  }
  return imageData
}

双线性插值(效果图)

 

<!-- 插值 -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>图片工具</title>
  </head>
  <body>
    <input type="file" id="imageInput" accept="image/*" />

    <button id="enlargeButton">插值</button>
    <canvas id="canvas"></canvas>
    <!-- <img id="outputImage" /> -->

    <script>
      function bilinearInterpolation(
        imageData,
        originalWidth,
        originalHeight,
        newWidth,
        newHeight
      ) {
        const newImageData = new ImageData(newWidth, newHeight);
        const originalPixels = imageData.data; // 原始图像像素数据
        const newPixels = newImageData.data; // 新图像像素数据

        // 遍历每一个新图像的像素
        for (let i = 0; i < newHeight; i++) {
          for (let j = 0; j < newWidth; j++) {
            // 计算新像素在原图像中的浮动位置 映射关系 坐标是从0开始的
            const x = (j * (originalWidth - 1)) / (newWidth - 1);
            const y = (i * (originalHeight - 1)) / (newHeight - 1);

            // 获取四个邻近像素的坐标
            const x1 = Math.floor(x);
            const y1 = Math.floor(y);
            const x2 = Math.min(x1 + 1, originalWidth - 1);
            const y2 = Math.min(y1 + 1, originalHeight - 1);

            // 获取四个邻近像素的颜色值(RGBA)
            const Q11 = getPixel(originalPixels, x1, y1, originalWidth);
            const Q12 = getPixel(originalPixels, x1, y2, originalWidth);
            const Q21 = getPixel(originalPixels, x2, y1, originalWidth);
            const Q22 = getPixel(originalPixels, x2, y2, originalWidth);

            // 在 x 方向进行线性插值
            const R1 = interpolate(Q11, Q21, x, x1);
            const R2 = interpolate(Q12, Q22, x, x1);

            // 在 y 方向进行线性插值
            const P = interpolate(R1, R2, y, y1);

            // 将插值结果放入新图像 这里是引用类型 会修改自身
            setPixel(newPixels, j, i, newWidth, P);
          }
        }

        return newImageData;
      }

      // 获取像素 (R, G, B, A)
      function getPixel(pixels, x, y, width) {
        const index = (y * width + x) * 4;
        return {
          r: pixels[index],
          g: pixels[index + 1],
          b: pixels[index + 2],
          a: pixels[index + 3],
        };
      }

      // 进行线性插值
      function interpolate(P1, P2, t, t1) {
        const factor = (t - t1) / (t1 + 1 - t1);
        return {
          r: P1.r + (P2.r - P1.r) * factor,
          g: P1.g + (P2.g - P1.g) * factor,
          b: P1.b + (P2.b - P1.b) * factor,
          a: P1.a + (P2.a - P1.a) * factor,
        };
      }

      // 设置新图像像素
      function setPixel(pixels, x, y, width, color) {
        const index = (y * width + x) * 4;
        pixels[index] = color.r;
        pixels[index + 1] = color.g;
        pixels[index + 2] = color.b;
        pixels[index + 3] = color.a;
      }

      function nearestNeighborInterpolation(
        imageData,
        width,
        height,
        scaleX,
        scaleY,
        ctx
      ) {
        const newWidth = Math.round(width * scaleX);
        const newHeight = Math.round(height * scaleY);
        console.log(ctx);
        const newImageData = ctx.createImageData(newWidth, newHeight);
        const data = imageData.data;
        const newData = newImageData.data;

        for (let y = 0; y < newHeight; y++) {
          for (let x = 0; x < newWidth; x++) {
            const srcX = Math.floor(x / scaleX);
            const srcY = Math.floor(y / scaleY);
            const srcIndex = (srcY * width + srcX) * 4;
            const destIndex = (y * newWidth + x) * 4;
            newData[destIndex] = data[srcIndex];
            newData[destIndex + 1] = data[srcIndex + 1];
            newData[destIndex + 2] = data[srcIndex + 2];
            newData[destIndex + 3] = data[srcIndex + 3];
          }
        }

        return newImageData;
      }

      document
        .getElementById("enlargeButton")
        .addEventListener("click", function () {
          const file = document.getElementById("imageInput").files[0];
          const reader = new FileReader();
          reader.onload = function (e) {
            const img = new Image();
            img.onload = function () {
              const canvas = document.getElementById("canvas");
              const ctx = canvas.getContext("2d");
              canvas.width = 600;
              canvas.height = 600;
              ctx.drawImage(img, 0, 0, 600, 600);

              const imageData = ctx.getImageData(0, 0, 600, 600);

              console.log(imageData);
              const newImageData = bilinearInterpolation(
                imageData,
                600,
                600,
                300,
                300
              );
              ctx.putImageData(newImageData, 0, 0);
              console.log(newImageData);
            };
            img.src = e.target.result;
          };
          reader.readAsDataURL(file);
        });
    </script>
  </body>
</html>

结论:对比两个imageData插值是有效的 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值