前端融合图片mask

之前实现了tif文件的融合,现在实现图片的融合,效果如下
前端融合图片mask_第1张图片
第一张是融合右边两张图的结果
我的思路是:

  1. 初始使用canvas加载原图,此时未显示标注
  2. 点击显示标注后,将原图和mask图传给worker
  3. worker接受数据后,转为blob格式再经过createImageBitmap处理拿到宽高
  4. 接着使用OffscreenCanvas渲染原地形图
  5. 再创建一个OffscreenCanvas处理地形mask图
  6. 将该地形mask的imageData值拿到,循环并处理对应的颜色
  7. 把处理后的mask图重新绘制回去
  8. 将处理后的mask的OffscreenCanvas绘制在地形图OffscreenCanvas上
  9. 最后将最新的地形图OffscreenCanvas进行转换返回给主线程

这是index.html代码:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title></title>
  <style type="text/css">
    * {
      margin: 0;
      padding: 0;
    }

    h1 {
      position: absolute;
      top: 0;
      z-index: 99;
      color: red;
    }

  </style>
</head>

<body>
  <input id="checkSVGLabel" type="checkbox" name=""> 显隐标注
  <br>
  <!-- <img id="defaultImg" src="" alt=""> -->
  <canvas id="canvas"></canvas>

  <script type="text/javascript">

    // const terrainImageUrl = 'http://192.168.10.159:5502/images/orz.png'; // 替换为你的地形图URL
    const terrainImageUrl = 'http://192.168.10.159:5502/images/orz5.png'; // 替换为你的地形图URL
    const maskImageUrl = 'http://192.168.10.159:5502/images/mask1.png'; // 替换为你的黑底白框图URL

    // const terrainImageUrl = 'http://192.168.10.159:5502/images/orz3.jpg'; // 替换为你的地形图URL
    // const maskImageUrl = 'http://192.168.10.159:5502/images/mask4.jpg'; // 替换为你的黑底白框图URL
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const checkSVGLabel = document.getElementById('checkSVGLabel');
    // const defaultImg = document.getElementById('defaultImg');
    let changeImgUrl = terrainImageUrl

    let imageWorker = null
    function handleMaskImg (e)
    {
      if (e.target.checked && imageWorker) {
        mergeMaskImg(terrainImageUrl, maskImageUrl, 'mergeMask')
      } else {
        mergeMaskImg(terrainImageUrl, maskImageUrl)
      }
    }

    // 融合色块
    function mergeMaskImg (orz, mask, type)
    {
      if (type === 'mergeMask') {
        imageWorker.postMessage({
          orz,
          mask,
          type: 'creatMaskImg'
        })
      } else {
        const image = new Image();
        image.src = orz;
        image.onload = function ()
        {
          canvas.width = image.width;
          canvas.height = image.height;
          ctx.drawImage(image, 0, 0);
        }

      }
    }

    function clearWorker ()
    {
      if (worker) {
        worker.terminate()
      }
      worker = null
    }

    window.onload = function ()
    {
      mergeMaskImg(terrainImageUrl, maskImageUrl)

      imageWorker = new Worker("../js/imageWorker.js")
      imageWorker.onmessage = (e) =>
      {
        const { type } = e.data;
        if (type === 'putMaskImg') {
          // 读取影像
          // defaultImg.src = e.data.dataUrl

          // 使用画布
          console.log(e)
          const { imageBitmap, canvasWidth, canvasHeight } = e.data;
          canvas.width = canvasWidth;
          canvas.height = canvasHeight;
          ctx.drawImage(imageBitmap, 0, 0, canvasWidth, canvasHeight);
        }
      }
    }

    checkSVGLabel.addEventListener('change', handleMaskImg);

  </script>
</body>

</html>

这是worker代码

self.addEventListener('message', async ({ data }) => {
	if (data.type === 'creatMaskImg') {
		let { orz, mask } = data
		// 加载地形图和蒙版图像
		const [terrainImage, maskImage] = await Promise.all([
			loadImageBitmap(orz),
			loadImageBitmap(mask)
		])
		const canvasWidth = terrainImage.width
		const canvasHeight = terrainImage.height
		const offscreenCanvas = new OffscreenCanvas(canvasWidth, canvasHeight)
		const ctx = offscreenCanvas.getContext('2d')
		// 绘制地形图
		ctx.drawImage(terrainImage, 0, 0, canvasWidth, canvasHeight)

		// 创建一个临时canvas来处理蒙版
		const tempCanvas = new OffscreenCanvas(
			maskImage.width,
			maskImage.height
		)
		const tempCtx = tempCanvas.getContext('2d')
		// 将黑底白框图绘制到临时canvas
		tempCtx.drawImage(maskImage, 0, 0)

		// 获取图像数据
		const maskImageData = tempCtx.getImageData(
			0,
			0,
			tempCanvas.width,
			tempCanvas.height
		)
		const imgData = maskImageData.data

		for (let i = 0; i < imgData.length; i += 4) {
			// 获取当前像素的RGB值
			const red = imgData[i]
			const green = imgData[i + 1]
			const blue = imgData[i + 2]
			if (red === 0 && green === 0 && blue === 0) {
				imgData[i + 3] = 0 // 透明度设置为0
			} else {
				if (red === 255 && green === 255 && blue === 255) {
					// 黑色
					// imgData[i] = 0
					// imgData[i + 1] = 0
					// imgData[i + 2] = 0
					// 紫色
					imgData[i] = 254
					imgData[i + 1] = 0
					imgData[i + 2] = 255
					imgData[i + 3] = imgData[i + 3] * 0.5

					// 透明图
					// imgData[i] = null
					// imgData[i + 1] = null
					// imgData[i + 2] = null
				}
			}
		}
		// 将处理后的蒙版图像数据放回临时canvas
		tempCtx.putImageData(maskImageData, 0, 0)

		// 将临时canvas上的蒙版绘制到地形图上
		ctx.drawImage(tempCanvas, 0, 0, canvasWidth, canvasHeight)

		// ----------------- 传blob图像数据
		// 将处理后的 offscreenCanvas 转换为 Blob
		// const blob = await offscreenCanvas.convertToBlob()
		// const dataUrl = await blobToDataURL(blob)
		// self.postMessage({
		// 	dataUrl,
		// 	canvasWidth,
		// 	canvasHeight,
		// 	type: 'putMaskImg'
		// })
		// ----------------- 传canvas数据
		// 将处理后的 offscreenCanvas 转换为 ImageBitmap 并返回到主线程
		const imageBitmap = offscreenCanvas.transferToImageBitmap()
		self.postMessage(
			{ imageBitmap, canvasWidth, canvasHeight, type: 'putMaskImg' },
			[imageBitmap]
		)
	}
})

async function loadImageBitmap(url) {
	const response = await fetch(url)
	const blob = await response.blob()
	return createImageBitmap(blob)
}

function blobToDataURL(blob) {
	return new Promise((resolve, reject) => {
		const reader = new FileReader()
		reader.onloadend = () => resolve(reader.result)
		reader.onerror = reject
		reader.readAsDataURL(blob)
	})
}

在worker中其实还可以将mask图片的黑色区域进行转换,也可以设置透明值,非常方便,如果不需要复杂的颜色处理,在处理后还可以结合mask-image去实现蒙版效果。

你可能感兴趣的:(前端,mask融合,蒙版,javascript)