js应用opencv

1.引用import cv from '@techstark/opencv-js';

2.vue代码

java 复制代码
 <div class="historyLeft2">

        <div style="relative: display" v-for="(item, index) in dataReturns3">
          <el-row>
            <el-col :span="16">

              <canvas v-if="index === 0" ref="myCanvas" width="230" height="230"></canvas>

            </el-col>

            <el-col :span="8" style="text-align: left;">
              

              <el-button
               v-if="tiancongflag==0"
          type="success" 
         
          style="margin-top:10px; margin-left:15px; font-size:14px;"
          size="small"
          @click="tiancong"
          round
          >填充缺陷</el-button>
        
          

            <el-button
            v-if="tiancongflag === 1"
          type="warning" 
         
          style="margin-top:10px;margin-left:15px;font-size:14px;"
          size="small"
          @click="tiancong"
          round
          >取消填充</el-button>
        
            </el-col>

          </el-row>
        </div>
      </div>

3.js代码

java 复制代码
 processBase64Image(base64Data) {
      const img = new Image();
      img.onload = () => {
        
        if(this.tiancongflag == 1){
          this.processImage(img);
        }else{
          this.processImage2(img);
        }
        
      };
      console.log(444)
      img.src = base64Data;
    },
    processImage(img) {
      console.log(img)

      this.$nextTick(() => {
        const canvas = this.$refs.myCanvas;
        const ctx = canvas[0].getContext('2d');

        var canvasWidth = canvas[0].width;
        var canvasHeight = canvas[0].height;
        var imgWidth = img.width;
        var imgHeight = img.height;
        var imgYOffset = 20;
        // 计算宽高比和缩放比例
        var scaledWidth = 0;
        var scaledHeight = 0;

        if (imgWidth > imgHeight) {
          scaledWidth = 190;
          scaledHeight = Math.floor(scaledWidth * (imgHeight/imgWidth));
        } else {
          scaledHeight = 210;
          scaledWidth = Math.floor(scaledHeight * (imgWidth/imgHeight));
        }

        // 清除 Canvas
        // ctx.clearRect(0, 0, canvasWidth, canvasHeight);
        ctx.fillStyle = '#081c31';
        ctx.strokeStyle = '#081c31';
        ctx.fillRect(0, 0, canvasWidth, canvasHeight);

        var x = Math.floor((210 - scaledWidth) / 2);
        var y = Math.floor((210 - scaledHeight) / 2);
        ctx.drawImage(img, x, y+imgYOffset, scaledWidth, scaledHeight);

        // 读取图像
        const src = cv.imread(canvas[0]);

        // 创建一个目标矩阵用于灰度图像
        var gray = new cv.Mat();
        cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0);

        //它创建了一个矩形区域(ROI,Region of Interest)并从灰度图像中提取该区域
        let rect = new cv.Rect(x,y+imgYOffset,scaledWidth,scaledHeight);
        gray = gray.roi(rect);

        // 创建 MatVector 并添加灰度图像
        const matVector = new cv.MatVector();
        matVector.push_back(gray);

        // 计算直方图
        const histSize = [256]; // 直方图的大小
        const ranges = [0, 256]; // 像素值范围
        const hist = new cv.Mat(); // 直方图结果
        const channels = [0]; // 通道索引
        const mask = new cv.Mat(); // 掩码
        
        cv.calcHist(matVector, channels, mask, hist, histSize, ranges);

        // 找到直方图的波峰
        const histData = hist.data32F; // 获取直方图数据
        let maxVal = 0;
        let maxIdx = 0;
        for (let i = 0; i < histSize[0]; i++) {
          if (histData[i] > maxVal) {
            maxVal = histData[i];
            maxIdx = i;
          }
        }
      
        // 波峰 ±150 的范围
        const lowerBound = maxIdx - 25;
        const upperBound = maxIdx + 25;
        console.log("222")
        console.log(maxIdx);

        // 创建一个红色的图像
        const redImage = new cv.Mat(src.rows, src.cols, src.type(), [255, 0, 0, 255]);


        for (let i = y+imgYOffset; i < y+imgYOffset+scaledHeight; i++) {
          for (let j = x; j < x+scaledWidth; j++) {
            const pixel = src.ucharPtr(i, j)[1];
            if (pixel < lowerBound || pixel > upperBound) {
              // 将不在波峰 ±150 范围内的像素变为红色
              src.ucharPtr(i, j)[0] = 255; // R
              src.ucharPtr(i, j)[1] = 0;   // G
              src.ucharPtr(i, j)[2] = 0;   // B
            }
          }
        }

        // 显示结果
        cv.imshow(canvas[0], src);
        
        // 释放内存
        src.delete();
        gray.delete();
        matVector.delete();
        hist.delete();
        mask.delete();
        redImage.delete();
       // result.delete();


        // 绘制刻度线
        const scaleLineY = y+20; // 刻度线距离顶部的距离(原为20,增加5个单位)
        const scaleLineLength = scaledWidth; // 刻度线的长度
        const scaleLineX =x; // 刻度线的起始X坐标

        ctx.strokeStyle = '#fbf321'; // 刻度线颜色
        ctx.lineWidth = 2; // 刻度线宽度
        ctx.beginPath();
        ctx.moveTo(scaleLineX, scaleLineY);
        ctx.lineTo(scaleLineX + scaleLineLength, scaleLineY);
        ctx.stroke();

        // 绘制刻度标记(可选)
        const numTicks = 10; // 刻度标记的数量
        const tickLength = 5; // 刻度标记的长度
        const middleTickLength = 10; // 中间刻度标记的长度
        const tickSpacing = scaleLineLength / numTicks; // 刻度标记之间的间距

        for (let i = 0; i <= numTicks; i++) {
          const tickX = scaleLineX + i * tickSpacing;
          const isMiddleTick = i === numTicks / 2; // 判断是否为中间刻度
          const currentTickLength = isMiddleTick ? middleTickLength : tickLength; // 如果是中间刻度,使用更长的长度

          ctx.beginPath();
          ctx.moveTo(tickX, scaleLineY);
          ctx.lineTo(tickX, scaleLineY - currentTickLength); // 刻度线向上绘制
          ctx.stroke();

          // 在最左面标注"0"
          if (i === 0) {
            ctx.fillStyle = '#ffffff'; // 文本颜色
            ctx.font = '12px Arial'; // 字体大小和样式
            ctx.textAlign = 'left'; // 文本左对齐
            ctx.textBaseline = 'bottom'; // 文本底部对齐
            ctx.fillText('0', tickX, scaleLineY - currentTickLength - 1); // 在刻度线上方标注"0"
          }
        }

        // 标注图像长度
        ctx.fillStyle = '#ffffff'; // 文本颜色
        ctx.font = '12px Arial'; // 字体大小和样式
        ctx.textAlign = 'right'; // 文本右对齐
        ctx.textBaseline = 'middle'; // 文本垂直居中对齐

        // 在刻度线最右面标注图像长度
        const labelX = scaleLineX + scaleLineLength + 20; // 文本的X坐标(刻度线最右端 + 10个单位)
        const labelY = scaleLineY - 11; // 文本的Y坐标(在刻度线上方,原为15,增加5个单位)
        if (this.flagf == 1) {
          ctx.fillText(`${(imgWidth * 0.098).toFixed(2)}mm`, labelX, labelY);
        } else {
          ctx.fillText(`${(imgWidth * 0.103).toFixed(2)}mm`, labelX, labelY);
        }

        // 右侧刻度线的位置和长度
        const scaleLineXRight = scaleLineX + scaledWidth; // 右侧刻度线的X坐标(图片右侧偏移20像素)
        const scaleLineYRight = y+20; // 右侧刻度线的Y坐标(从顶部开始)
        var scaleLineLengthRight = 0;
        if (imgHeight - imgWidth > 30) {

          scaleLineLengthRight = scaledHeight; // 右侧刻度线的长度(与图片高度相同)
        } else {
          scaleLineLengthRight = scaledHeight; // 右侧刻度线的长度(与图片高度相同)
        }


        // 绘制右侧刻度线
        ctx.strokeStyle = '#fbf321'; // 刻度线颜色
        ctx.lineWidth = 2; // 刻度线宽度
        ctx.beginPath();
        ctx.moveTo(scaleLineXRight, scaleLineYRight);
        ctx.lineTo(scaleLineXRight, scaleLineYRight + scaleLineLengthRight);
        ctx.stroke();

        // 绘制右侧刻度标记(可选)
        const numTicksRight = 10; // 刻度标记的数量
        const tickLengthRight = 5; // 刻度标记的长度
        const middleTickLengthRight = 10; // 中间刻度标记的长度
        const tickSpacingRight = scaleLineLengthRight / numTicksRight; // 刻度标记之间的间距

        for (let i = 0; i <= numTicksRight; i++) {
          const tickY = scaleLineYRight + i * tickSpacingRight;
          const isMiddleTick = i === numTicksRight / 2; // 判断是否为中间刻度
          const currentTickLength = isMiddleTick ? middleTickLengthRight : tickLengthRight; // 如果是中间刻度,使用更长的长度

          ctx.beginPath();
          ctx.moveTo(scaleLineXRight, tickY);
          ctx.lineTo(scaleLineXRight + currentTickLength, tickY); // 刻度线向右绘制
          ctx.stroke();

          // 在最上面的刻度位置显示 "0"
          if (i === 0) {
            ctx.fillStyle = '#ffffff'; // 文本颜色
            ctx.font = '12px Arial'; // 字体大小和样式
            ctx.textAlign = 'left'; // 文本左对齐
            ctx.textBaseline = 'middle'; // 文本垂直居中对齐
            ctx.fillText('0', scaleLineXRight + currentTickLength + 5, tickY); // 在刻度线右侧绘制 "0"
          }
        }

        // 标注图像高度
        ctx.fillStyle = '#ffffff'; // 文本颜色
        ctx.font = '12px Arial'; // 字体大小和样式
        ctx.textAlign = 'center'; // 文本居中对齐
        ctx.textBaseline = 'middle'; // 文本垂直居中对齐

        // 在右侧刻度线最下面标注图像高度
        const labelXRight = scaleLineXRight + 30; // 文本的X坐标(刻度线右侧偏移15像素)
        const labelYRight = scaleLineYRight + scaleLineLengthRight - 10; // 文本的Y坐标(在刻度线最下面)

        if (this.flagf == 1) {
          ctx.fillText(`${(imgHeight * 0.098).toFixed(2)}mm`, labelXRight, labelYRight); // 绘制文本
        } else {
          ctx.fillText(`${(imgHeight * 0.103).toFixed(2)}mm`, labelXRight, labelYRight); // 绘制文本
        }

      });


    },
  processImage2(img) {
  

      this.$nextTick(() => {

        console.log("ppsasasa")
        const canvas = this.$refs.myCanvas;
        const ctx = canvas[0].getContext('2d');

        var canvasWidth = canvas[0].width;
        var canvasHeight = canvas[0].height;
        var imgWidth = img.width;
        var imgHeight = img.height;
        var imgYOffset = 20;
        // 计算宽高比和缩放比例
        var scaledWidth = 0;
        var scaledHeight = 0;

        if (imgWidth > imgHeight) {
          scaledWidth = 190;
          scaledHeight = Math.floor(scaledWidth * (imgHeight/imgWidth));
        } else {
          scaledHeight = 210;
          scaledWidth = Math.floor(scaledHeight * (imgWidth/imgHeight));
        }

        // 清除 Canvas
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
        ctx.fillStyle = '#081c31';
        ctx.strokeStyle = '#081c31';
        ctx.fillRect(0, 0, canvasWidth, canvasHeight);

        var x = Math.floor((210 - scaledWidth) / 2);
        var y = Math.floor((210 - scaledHeight) / 2);
        ctx.drawImage(img, x, y+imgYOffset, scaledWidth, scaledHeight);

         // 读取图像
         const src = cv.imread(canvas[0]);

      // // 创建一个目标矩阵用于灰度图像
      // const gray = new cv.Mat();
      // cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0);

      // // 创建一个二值化图像,将白色区域分离出来
      // const binary = new cv.Mat();
      // cv.threshold(gray, binary, 170, 255, cv.THRESH_BINARY);


      // // // 将二值化图像作为掩码,将红色图像应用到白色区域
      // const result = new cv.Mat();
  
      // // 将原始图像中非白色区域保留
      // cv.bitwise_not(binary, binary);
      // cv.bitwise_and(src, src, result, binary);

      // // 显示结果
      // cv.imshow(canvas[0], result);

      // // 释放内存
      // src.delete();
      // gray.delete();
      // binary.delete();
    
      // result.delete();


        // 绘制刻度线
        const scaleLineY = y+20; // 刻度线距离顶部的距离(原为20,增加5个单位)
        const scaleLineLength = scaledWidth; // 刻度线的长度
        const scaleLineX =x; // 刻度线的起始X坐标

        ctx.strokeStyle = '#fbf321'; // 刻度线颜色
        ctx.lineWidth = 2; // 刻度线宽度
        ctx.beginPath();
        ctx.moveTo(scaleLineX, scaleLineY);
        ctx.lineTo(scaleLineX + scaleLineLength, scaleLineY);
        ctx.stroke();

        // 绘制刻度标记(可选)
        const numTicks = 10; // 刻度标记的数量
        const tickLength = 5; // 刻度标记的长度
        const middleTickLength = 10; // 中间刻度标记的长度
        const tickSpacing = scaleLineLength / numTicks; // 刻度标记之间的间距

        for (let i = 0; i <= numTicks; i++) {
          const tickX = scaleLineX + i * tickSpacing;
          const isMiddleTick = i === numTicks / 2; // 判断是否为中间刻度
          const currentTickLength = isMiddleTick ? middleTickLength : tickLength; // 如果是中间刻度,使用更长的长度

          ctx.beginPath();
          ctx.moveTo(tickX, scaleLineY);
          ctx.lineTo(tickX, scaleLineY - currentTickLength); // 刻度线向上绘制
          ctx.stroke();

          // 在最左面标注"0"
          if (i === 0) {
            ctx.fillStyle = '#ffffff'; // 文本颜色
            ctx.font = '12px Arial'; // 字体大小和样式
            ctx.textAlign = 'left'; // 文本左对齐
            ctx.textBaseline = 'bottom'; // 文本底部对齐
            ctx.fillText('0', tickX, scaleLineY - currentTickLength - 1); // 在刻度线上方标注"0"
          }
        }

        // 标注图像长度
        ctx.fillStyle = '#ffffff'; // 文本颜色
        ctx.font = '12px Arial'; // 字体大小和样式
        ctx.textAlign = 'right'; // 文本右对齐
        ctx.textBaseline = 'middle'; // 文本垂直居中对齐

        // 在刻度线最右面标注图像长度
        const labelX = scaleLineX + scaleLineLength + 20; // 文本的X坐标(刻度线最右端 + 10个单位)
        const labelY = scaleLineY - 11; // 文本的Y坐标(在刻度线上方,原为15,增加5个单位)
        if (this.flagf == 1) {
          ctx.fillText(`${(imgWidth * 0.098).toFixed(2)}mm`, labelX, labelY);
        } else {
          ctx.fillText(`${(imgWidth * 0.103).toFixed(2)}mm`, labelX, labelY);
        }




        // 右侧刻度线的位置和长度
        const scaleLineXRight = scaleLineX + scaledWidth; // 右侧刻度线的X坐标(图片右侧偏移20像素)
        const scaleLineYRight = y+20; // 右侧刻度线的Y坐标(从顶部开始)
        var scaleLineLengthRight = 0;
        if (imgHeight - imgWidth > 30) {

          scaleLineLengthRight = scaledHeight; // 右侧刻度线的长度(与图片高度相同)
        } else {
          scaleLineLengthRight = scaledHeight; // 右侧刻度线的长度(与图片高度相同)
        }


        // 绘制右侧刻度线
        ctx.strokeStyle = '#fbf321'; // 刻度线颜色
        ctx.lineWidth = 2; // 刻度线宽度
        ctx.beginPath();
        ctx.moveTo(scaleLineXRight, scaleLineYRight);
        ctx.lineTo(scaleLineXRight, scaleLineYRight + scaleLineLengthRight);
        ctx.stroke();

        // 绘制右侧刻度标记(可选)
        const numTicksRight = 10; // 刻度标记的数量
        const tickLengthRight = 5; // 刻度标记的长度
        const middleTickLengthRight = 10; // 中间刻度标记的长度
        const tickSpacingRight = scaleLineLengthRight / numTicksRight; // 刻度标记之间的间距

        for (let i = 0; i <= numTicksRight; i++) {
          const tickY = scaleLineYRight + i * tickSpacingRight;
          const isMiddleTick = i === numTicksRight / 2; // 判断是否为中间刻度
          const currentTickLength = isMiddleTick ? middleTickLengthRight : tickLengthRight; // 如果是中间刻度,使用更长的长度

          ctx.beginPath();
          ctx.moveTo(scaleLineXRight, tickY);
          ctx.lineTo(scaleLineXRight + currentTickLength, tickY); // 刻度线向右绘制
          ctx.stroke();

          // 在最上面的刻度位置显示 "0"
          if (i === 0) {
            ctx.fillStyle = '#ffffff'; // 文本颜色
            ctx.font = '12px Arial'; // 字体大小和样式
            ctx.textAlign = 'left'; // 文本左对齐
            ctx.textBaseline = 'middle'; // 文本垂直居中对齐
            ctx.fillText('0', scaleLineXRight + currentTickLength + 5, tickY); // 在刻度线右侧绘制 "0"
          }
        }

        // 标注图像高度
        ctx.fillStyle = '#ffffff'; // 文本颜色
        ctx.font = '12px Arial'; // 字体大小和样式
        ctx.textAlign = 'center'; // 文本居中对齐
        ctx.textBaseline = 'middle'; // 文本垂直居中对齐

        // 在右侧刻度线最下面标注图像高度
        const labelXRight = scaleLineXRight + 30; // 文本的X坐标(刻度线右侧偏移15像素)
        const labelYRight = scaleLineYRight + scaleLineLengthRight - 10; // 文本的Y坐标(在刻度线最下面)

        if (this.flagf == 1) {
          ctx.fillText(`${(imgHeight * 0.098).toFixed(2)}mm`, labelXRight, labelYRight); // 绘制文本
        } else {
          ctx.fillText(`${(imgHeight * 0.103).toFixed(2)}mm`, labelXRight, labelYRight); // 绘制文本
        }








      });


    },
相关推荐
蓝莓味柯基几秒前
Python3正则表达式:字符串魔法师的指南[特殊字符]‍♂️
开发语言·python·正则表达式
隐世12 分钟前
C++多态讲解
开发语言·c++
大猫会长3 分钟前
lenis滑动插件的笔记
javascript
db_lnn_202120 分钟前
【vue】全局组件及组件模块抽离
前端·javascript·vue.js
码上飞扬29 分钟前
Java大师成长计划之第20天:Spring Framework基础
java·开发语言
Qin_jiangshan29 分钟前
vue实现进度条带指针
前端·javascript·vue.js
刚入门的大一新生29 分钟前
C++初阶-string类的模拟实现1
开发语言·c++
天高任鸟飞dyz31 分钟前
tabs切换#
javascript·vue.js·elementui
Pluchon1 小时前
硅基计划2.0 学习总结 壹 Java初阶
java·开发语言·学习·算法
wowocpp1 小时前
Java MVC
java·开发语言·mvc