两张图片进行分析

news2024/11/28 10:52:40

两张图片进行分析,可以拖动左边图片进行放大、缩小查看图片差异

 

底图

 

<template>
   
   <div class="box_container">
    <section>
      <div class="" v-for="item in imgData.imgDataVal" :key="item.id">
        <img :style="{
          width: boxStyle.width + '%',
          top: boxStyle.top,
          left: boxStyle.left,
        }" :src="item.src" :alt="item.name" />

        <div v-if="clickState" class="selectRegion"
          :style="{ top: selectRegionStyle.top, left: selectRegionStyle.left }"></div>
        <div class="text">
          <p>{{ item.name }}</p>
        </div>

      </div>
      <div ref=" moveDom" id="moveDom"></div>


    </section>

    <p   class="p-diff">差别【不准确】: {{ differencePercentage }}%</p>
  </div>
</template>

<script lang="ts" setup>
  
import imageYT from '../assets/yt.png';
import imageFX from '../assets/fx.png';
import pixelmatch from 'pixelmatch';

import { reactive, ref, onMounted, onBeforeUnmount } from 'vue';
  
 
 
let differencePercentage = ref(0);
onMounted(() => {
  compareImages();
});
function compareImages() {
  const imgA = document.createElement('img');
  const imgB = document.createElement('img');

  imgA.onload = () => {
    imgB.onload = () => {
      const width = imgA.width;
      const height = imgA.height;
      const canvasA = document.createElement('canvas');
      const canvasB = document.createElement('canvas');
      canvasA.width = width;
      canvasA.height = height;
      canvasB.width = width;
      canvasB.height = height;
      const ctxA = canvasA.getContext('2d') as any;
      const ctxB = canvasB.getContext('2d') as any;
      ctxA.drawImage(imgA, 0, 0);
      ctxB.drawImage(imgB, 0, 0);
      const dataA = ctxA.getImageData(0, 0, width, height);
      const dataB = ctxB.getImageData(0, 0, width, height);
      const diff = pixelmatch(dataA.data, dataB.data, null, width, height);
      differencePercentage.value =
        100 - parseInt(((diff / (width * height)) * 100).toFixed(2));
    };
    imgB.src = imageFX as any;
  };
  imgA.src = imageYT as any;
}

let imgData = reactive({
  imgDataVal: [
    {
      id: 1,
      name: '原始图',
      src: imageYT
    },
    {
      id: 2,
      name: '分析图',
      src: imageFX
    }
  ]
})

/**
 * @description: 添加鼠标事件
 * @return {*}
 */

const init = () => {
  moveDom.value = document.getElementById('moveDom')
  moveDom.value.addEventListener('mousemove', moveEvent)
  moveDom.value.addEventListener('wheel', wheelEvent)
  moveDom.value.addEventListener('mousedown', mousedownEvent)
  moveDom.value.addEventListener('mouseup', mouseupEvent)
  moveDom.value.addEventListener('mouseout', mouseoutEvent)
  moveDom.value.addEventListener('mouseover', mouseoverEvent)
}
onMounted(() => {

  init()
})


 const moveDom: any = ref(null);
const images: any = ref(null);
images.value = document.getElementsByClassName('chatImgs');
/**
 * @description: 卸载鼠标事件
 * @return {*}
 */
onBeforeUnmount(() => {
  moveDom.value.removeEventListener('mousemove', moveEvent);
  moveDom.value.removeEventListener('mouseleave', wheelEvent);
  moveDom.value.removeEventListener('mousedown', mousedownEvent);
});
const boxStyle = ref({
  width: 50,
  top: '50%',
  left: '50%',
});
const selectRegionStyle = ref({
  top: '50%',
  left: '50%',
});
const moveX = ref(null);
const moveY = ref(null);

/**
 * @description: 鼠标移动事件
 * @param {*} e
 * @return {*}
 */
const moveEvent = (e: any) => {
  moveX.value = e.offsetX;
  moveY.value = e.offsetY;
  selectRegionStyle.value.left = `${e.offsetX}px`;
  selectRegionStyle.value.top = `${e.offsetY}px`;
  if (clickState.value) {
    boxStyle.value.top = `${e.offsetY}px`;
    boxStyle.value.left = `${e.offsetX}px`;
  }
};
/**
 * @description: 滚轮事件
 * @param {*} e
 * @return {*}
 */
const wheelEvent = (e: any) => {
  if (e.deltaY < 0) {
    if (boxStyle.value.width > 200) {
      return;
    }
    boxStyle.value.width = boxStyle.value.width + 10;
  } else {
    if (boxStyle.value.width < 50) {
      return;
    }
    boxStyle.value.width = boxStyle.value.width - 10;
  }
};

const clickState = ref(false);
const overState = ref(false);
/**
 * @description: 鼠标左键按下事件
 * @param {*} e
 * @return {*}
 */
const mousedownEvent = (e: any) => {
  clickState.value = true;
  overState.value = true;
};
/**
 * @description: 鼠标移入事件
 * @param {*} e
 * @return {*}
 */
const mouseoverEvent = (e: any) => {
  if (overState.value) {
    clickState.value = true;
  }
};
/**
 * @description: 鼠标左键抬起事件
 * @param {*} e
 * @return {*}
 */
const mouseupEvent = (e: any) => {
  clickState.value = false;
  overState.value = false;
};
/**
 * @description: 鼠标移出事件
 * @param {*} e
 * @return {*}
 */
const mouseoutEvent = (e: any) => {
  clickState.value = false;
};
</script>

<style scoped lang="scss">
.box_container {
  width: 100vw;
  height: 100vh;

  padding: 0;

}


section {
  width: 100%;
  height: 85%;
  display: flex;
  justify-content: center;
  justify-items: center;


  >div {
    flex: 1;
    height: 100%;
    position: relative;
    overflow: hidden;
    background-color: #0decb8da;
    box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.07);

    img {
      width: 100%;
      position: absolute;
      transform: translate(-50%, -50%);
      z-index: 0;

    }

    .selectRegion {
      position: absolute;
      width: 100px;
      height: 100px;
      transform: translate(-50%, -50%);
      border: 1px solid rgba(0, 0, 0, 0.3);

    }
  }

  // 左边区域可以拖动
  #moveDom {
    width: 49.8%;
    height: 85.0%;
    background-color: rgba(0, 0, 0, 0);
    position: absolute;
    top: 0;
    left: 0;
    cursor: move;
  }

  >div:nth-child(1) {
    margin-right: 5px;
  }

  >div:nth-child(2) {
    cursor: no-drop;
    margin-left: 5px;
  }

  .text {
    width: 100%;
    height: 50px;
    position: absolute;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.1);

    p {
      width: 100%;
      height: 100%;
      line-height: 100%;
      text-align: center;
      line-height: 50px;
      // color: #333;
      color: #fff;
      font-weight: 600;
      letter-spacing: 10px;
      font-size: 18px;
    }
  }
}
.p-diff{
  display: flex;
  justify-content: center;
  margin-top: 20px;
  font-size: 20px;
  font-weight:600
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1802607.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kafka监控系统efak的安装

下载地址Kafka Eaglehttp://download.kafka-eagle.org/下载地址连接不稳定&#xff0c;可以多次尝试直到成功连接下载 1.解压安装包并重命名 tar -zxvf kafka-eagle-bin-3.0.1.tar.gz 查看到解压后包含一个安装包&#xff0c;再解压 tar -zxvf efak-web-3.0.1-bin.tar.gz 移…

小程序简单版录音机

先来看看效果 结构 先来看看页面结构 <!-- wxml --><view class"wx-container"><view id"title">录音机</view><view id"time">{{hours}}:{{minute}}:{{second}}</view><view class"btngroup"…

【JavaSE】面向对象---多态

前言 本篇以Java初学者视角写下&#xff0c;难免有不足&#xff0c;或者术语不严谨之处。如有错误&#xff0c;欢迎评论区指正。本篇说明多态相关的知识。若本文无法解决您的问题&#xff0c;可以去最下方的参考文献出&#xff0c;找出想要的答案。 多态概念 多态&#xff08…

【Ardiuno】实验使用ESP32连接Wifi(图文)

ESP32最为精华和有特色的地方当然是wifi连接&#xff0c;这里我们就写程序实验一下适使用ESP32主板连接wifi&#xff0c;为了简化实验我们这里只做了连接部分&#xff0c;其他实验在后续再继续。 由于本实验只要在串口监视器中查看结果状态即可&#xff0c;因此电路板上无需连…

最短路径——迪杰斯特拉与弗洛伊德算法

一.迪杰斯特拉算法 首先对于最短路径来说&#xff1a;从vi-vj的最短路径&#xff0c;不用非要经过所有的顶点&#xff0c;只需要找到路径最短的路径即可&#xff1b; 那么迪杰斯特拉的算法&#xff1a;其实也就与最小生成树的思想类似&#xff0c;找到较小的&#xff0c;然后…

在网上赚钱,可以自由掌控时间,灵活的兼职副业选择

朋友们看着周围的人在网上赚钱&#xff0c;自己也会为之心动&#xff0c;随着电子设备的普及&#xff0c;带动了很多的工作、创业以及兼职副业选择的机会&#xff0c;作为普通人的我们&#xff0c;如果厌倦了世俗的朝九晚五&#xff0c;想着改变一下自己的生活&#xff0c;可以…

STM32 printf 重定向到CAN

最近在调试一款电机驱动板 使用的是CAN总线而且板子上只有一个CAN 想移植Easylogger到上面试试easylogger的效果&#xff0c;先实现pritnf的重定向功能来打印输出 只需要添加以下代码即可实现 代码 #include <stdarg.h> uint8_t FDCAN_UserTxBuffer[512]; void FDCAN_p…

btstack协议栈实战篇--Hello World example

btstack协议栈---总目录-CSDN博客 目录 1.定时计时器设置 2.主要应用程序设置 3.运行log如下图 该示例演示了如何提供周期性定时器来切换LED并将调试消息作为最小BTstack测试发送到控制台。 1.定时计时器设置 由于BTstack中的计时器是单触发的&#xff0c;因此通过在心跳中重新…

从0到1:企业办公审批小程序开发笔记

可行性分析 企业办公审批小程序&#xff0c;适合各大公司&#xff0c;企业&#xff0c;机关部门办公审批流程&#xff0c;适用于请假审批&#xff0c;报销审批&#xff0c;外出审批&#xff0c;合同审批&#xff0c;采购审批&#xff0c;入职审批&#xff0c;其他审批等规划化…

Android Qt开发环境部署

我总结了在Qt中搭建Android开发两个要点&#xff1a; 1.JDK一定要是JDK1.8的 2.要下载目标Android版本的SDK&#xff0c;可以在Android studio SDK查看对应Android SDK版本 下面我们开发搭建。首先需要JDK&#xff0c;链接如下&#xff1a;链接&#xff1a;https://pan.baidu.…

★pwn 24.04环境搭建保姆级教程★

★pwn 24.04环境搭建保姆级教程★ &#x1f338;前言&#x1f33a;Ubuntu 24.04虚拟机&#x1f337;VM&#x1f337;Ubuntu 24.04镜像 &#x1f33a;工具&#x1f337;可能出现的git clone错误&#x1f337;复制粘贴问题&#x1f337;攻击&#x1f337;编题 &#x1f33a;美化&…

【CS.DB】从零到精通:这可能是全网最全面最强大的SQL入门教程

文章目录 1. 什么是SQL&#xff1f;1.1 SQL的历史1.1.1 SQL的标准化过程 2. SQL基础语法2.1 数据库操作2.1.1 创建数据库2.1.2 删除数据库 2.2 表操作2.2.1 创建表2.2.2 删除表2.2.3 修改表 2.3 数据操作2.3.1 插入数据2.3.2 更新数据2.3.3 删除数据 2.4 查询数据2.4.1 基本查询…

【Linux】进程4——进程状态

1.进程状态 什么是状态&#xff1f; 每个人都有状态——颓废&#xff0c;阳光&#xff0c;积极向上。。。。 进程也有状态 在操作系统中&#xff0c;由于进程的数量是非常多的&#xff0c;而系统的资源又非常少&#xff0c;所以不可能每一个进程在每时每刻都会处于上处理机运…

【 技术栈】技术方案到底怎么写?

文章目录 一、背景二、技术方案重要性三、常见的技术方案有哪些内容1、系统用例2、功能整体链路2.1、核心业务流程 3、数据库设计4、接口设计5、非功能设计5.1、性能与稳定性5.2、监控 7、系统风险点评估 四、总结 一、背景 工作中&#xff0c;有一些需求或者技术改造&#xf…

计算机网络--应用层

计算机网络–计算机网络概念 计算机网络–物理层 计算机网络–数据链路层 计算机网络–网络层 计算机网络–传输层 计算机网络–应用层 1. 概述 因为不同的网络应用之间需要有一个确定的通信规则。 1.1 两种常用的网络应用模型 1.1.1 客户/服务器模型&#xff08;Client/Se…

Java面试八股之什么是反射,实现原理是什么

Java中什么是反射&#xff0c;实现原理是什么 Java中的反射&#xff08;Reflection&#xff09;是一种强大的特性&#xff0c;它允许程序在运行时检查和操作类、接口、字段和方法的信息。简而言之&#xff0c;反射机制使得程序能够在运行时动态地了解和使用自身或其他程序集中…

python文件:py,ipynb, pyi, pyc, pyd, pyo都是什么文件?

1、Python文件类型介绍 &#x1f4c1; 1.1 .py 文件&#xff1a;源代码基础 .py 文件是 Python 最基本的源代码文件格式&#xff0c;用于存储纯文本形式的 Python 代码。它是开发者编写程序的主要场所&#xff0c;包含函数、类、变量定义以及执行逻辑。Python 解释器直接读取…

【排序算法】总结篇

✨✨这些 排序算法都是指的 需要进行比较的排序算法 ✨✨下面都是略微讲解一下思路&#xff0c;如果需要详细了解哪一个排序&#xff0c;点击&#x1f449;链接即可 ✨✨对于时间、空间复杂度、稳定性&#xff0c;希望你&#x1f9d1;‍&#x1f393;能够理解记忆&#x1f9d1;…

MyBatisPlus插件生成代码

文章目录 概要安装插件使用插件 概要 MyBatis-Plus 是 MyBatis 的增强工具&#xff0c;旨在简化 MyBatis 的开发。MyBatis-Plus 代码生成器插件可以自动生成项目中常见的代码&#xff0c;如实体类、Mapper 接口、Service 接口和实现类、Controller 等&#xff0c;从而减少手动…

python代码中参数的默认值

python中的函数&#xff0c;可以给形参指定默认值。 带有默认值的参数&#xff0c;可以在调用的时候不传参。 如上图所示&#xff0c;在给函数设定形参的时候可以给函数形参设定默认值&#xff0c;当然默认参数的形参应该在非默认形参的后面。 如果在调用函数的时候&#xff…