利用canvas 实现图片的标注,把标注像素点传入到后端

背景:我们有一个摄像的产品,拍照传统的水表盘面,我们需要框选水表读数,标注点传到后端,后端根据标注点自动去截取摄像表拍摄回来的图片,然后拿到大模型里面进行训练。由于同一只表拍摄的画面都是一样的,所以按此方法减少了人工标注的繁琐工作

可关注,参考另外一篇文章:利用fabricjs 实现图片的标注,把标注像素点传入到后端

解锁前端难题:亲手实现一个图片标注工具

《T 恤图案编辑器》
《T 恤图案编辑器》-源码
实现一个轻量 fabric.js 系列一(摸透 canvas)

遗留问题:
1、矩形框旋转后,鼠标悬浮在缩放标注点的位置上,鼠标的样式无法旋转角度
2、矩形框旋转后,拖动缩放的标准变了
备注:经测试,不管怎么变化,传入到后端的像素点是对的

一、效果图

请添加图片描述

二、问题分解

三、源代码

<template><div:style="{width: canvasProp.width + 'px',height: canvasProp.height + 'px',border: '1px solid #ccc'}"><canvasref="canvas":width="canvasProp.width":height="canvasProp.height"@mousedown="onMouseDown"@mousemove="onMouseMove"@mouseup="onMouseUp":style="{width: canvasProp.width + 'px',height: canvasProp.height + 'px'}"></canvas><div @click="saveData">保存数据</div><div @click="zoomBig">放大</div><div @click="zoomSmall">缩小</div></div>
</template><script>
export default {name: "images-tags",props: {// 矩形标注的数据tagsData: {type: Array,default: () => {return [{label: "基表数据",color: "#0000ff",type: "rectangle",width: 150,height: 50,rotate: 0,isInit: true,startX: 185,startY: 235}];}},// 图片路径images: {type: String,default: "/img/yejing1.jpg"}},data() {return {ctx: null,cursorClass: "",initCenterX: 0,initCenterY: 0,rotateImages: null, //旋转图标是否加载bgImage: null, //背景图是否加载canvasProp: {width: 0, // canvas的宽度height: 0, // canvas的高度scale: 1, // canvas的缩放比例scaleX: 0,scaleY: 0,translateX: 0,translateY: 0},selectedTag: null, // 当前选中的矩形框isResizing: false,isDragging: false,isRotating: false,resizeHandle: null,dragOffsetX: 0,dragOffsetY: 0,mouseDownX: 0,mouseDownY: 0,initialRotation: 0,isCanvasDraging: false};},mounted() {this.loadImageAndSetCanvas();window.addEventListener("keydown", this.handleKeyDown);window.addEventListener("wheel", this.onWheel, { passive: false });console.log("保存的数据===", this.tagsData);},beforeDestroy() {window.removeEventListener("keydown", this.handleKeyDown);window.removeEventListener("wheel", this.onWheel);},methods: {zoomBig() {this.zoom(true, this.initCenterX, this.initCenterY);},zoomSmall() {this.zoom(false, this.initCenterX, this.initCenterY);},onWheel(event) {if (event.ctrlKey) {// detect pinchevent.preventDefault(); // prevent zoomthis.zoom(event.deltaY < 0, event.offsetX, event.offsetY);}},zoom(iszoomBig, zoomCenterX, zoomCenterY) {if (iszoomBig) {console.log("Pinching 放大");if (this.canvasProp.scale < 3) {this.canvasProp.scaleX = zoomCenterX;this.canvasProp.scaleY = zoomCenterY;this.canvasProp.scale = Math.min(this.canvasProp.scale + 0.1, 3);}this.drawTags();} else {if (this.canvasProp.scale > 1) {this.canvasProp.scaleX = zoomCenterX;this.canvasProp.scaleY = zoomCenterY;this.canvasProp.scale = Math.max(this.canvasProp.scale - 0.1, 1);this.drawTags();}}},computexy(x, y) {let { scaleX, scale, scaleY, translateX, translateY } = this.canvasProp;const xy = {// x: x / scale - translateX,// y: y / scale - translateY,offsetX: (x - scaleX * (1 - scale) - translateX * scale) / scale,offsetY: (y - scaleY * (1 - scale) - translateY * scale) / scale};return xy;},computewh(width, height) {return {width: width / scale,height: height / scale};},handleKeyDown(event) {console.log("event.key", event.key);const step = 10; // 每次移动的步长switch (event.key) {case "ArrowUp":this.canvasProp.translateY -= step;break;case "ArrowDown":this.canvasProp.translateY += step;break;case "ArrowLeft":this.canvasProp.translateX -= step;break;case "ArrowRight":this.canvasProp.translateX += step;break;}this.drawTags(); // 重新绘制画布},saveData() {console.log("保存的数据", this.tagsData);let pointData = this.getPointData();console.log("pointData", pointData);this.setPointData(pointData);// this.$emit("saveData",this.setPointData(pointData));},getPointData() {const result = this.tagsData.map(tag => {const { startX, startY, width, height, rotate } = tag;const centerX = startX + width / 2;const centerY = startY + height / 2;const points = [{ x: startX, y: startY }, // Top-left{ x: startX + width, y: startY }, // Top-right{ x: startX + width, y: startY + height }, // Bottom-right{ x: startX, y: startY + height } // Bottom-left];const rotatedPoints = points.map(point => {const dx = point.x - centerX;const dy = point.y - centerY;const rotatedX =centerX +dx * Math.cos((rotate * Math.PI) / 180) -dy * Math.sin((rotate * Math.PI) / 180);const rotatedY =centerY +dy * Math.cos((rotate * Math.PI) / 180) +dx * Math.sin((rotate * Math.PI) / 180);return [Math.round(rotatedX), Math.round(rotatedY)];});return rotatedPoints;});return result;},setPointData(result) {const newTagData = result.map(points => {const [p1, p2, p3, p4] = points;const centerX = (p1[0] + p3[0]) / 2;const centerY = (p1[1] + p3[1]) / 2;const width = Math.sqrt(Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2));const height = Math.sqrt(Math.pow(p4[0] - p1[0], 2) + Math.pow(p4[1] - p1[1], 2));const rotate =Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * (180 / Math.PI);return {label: "新矩形", // 可以根据需要更改标签color: "#0000ff", // 可以根据需要更改颜色type: "rectangle",startX: centerX - width / 2,startY: centerY - height / 2,width: width,height: height,rotate: rotate,isInit: false};});console.log("newTagData", newTagData);return newTagData;//this.tagsData = newTagData;//this.drawTags();},loadImageAndSetCanvas() {const img = new Image();img.src = this.images;img.onload = () => {this.bgImage = img;this.canvasProp.width = img.width;this.canvasProp.height = img.height;this.initCenterX = img.width / 2;this.initCenterY = img.height / 2;this.ctx = this.$refs.canvas.getContext("2d");this.$nextTick(() => {this.drawTags();});};},drawTags() {this.ctx.clearRect(0, 0, this.canvasProp.width, this.canvasProp.height);this.ctx.save();if (this.bgImage) {//画布缩放this.ctx.translate(this.canvasProp.scaleX, this.canvasProp.scaleY);this.ctx.scale(this.canvasProp.scale, this.canvasProp.scale);this.ctx.translate(-this.canvasProp.scaleX, -this.canvasProp.scaleY);//画布平移this.ctx.translate(this.canvasProp.translateX,this.canvasProp.translateY);this.ctx.drawImage(this.bgImage,0,0,this.bgImage.width,this.bgImage.height);this.tagsData.forEach(tag => {if (tag.type === "rectangle") {this.drawRectangle(tag);}});}this.ctx.restore();},rotateExec(tag) {let { startX, startY, width, height, rotate } = tag;this.ctx.translate(startX + width / 2, startY + height / 2);this.ctx.rotate((rotate * Math.PI) / 180);this.ctx.translate(-(startX + width / 2), -(startY + height / 2));},//手动添加输入框的时候drawRectangle(tag) {const { label, color, width, height, rotate, isInit } = tag;if (isInit) {tag.startX = this.initCenterX - width / 2;tag.startY = this.initCenterY - height / 2;}// 旋转矩形框,平移-旋转-平移到原来this.rotateExec(tag);this.ctx.save();// Draw the rectanglethis.ctx.beginPath();this.ctx.rect(tag.startX, tag.startY, width, height);this.ctx.fillStyle = this.hexToRgba(color, 0.2);this.ctx.fill();this.ctx.lineWidth = 2;this.ctx.strokeStyle = color;this.ctx.stroke();//旋转矩形框// Draw the label textthis.ctx.font = "14px Arial";this.ctx.textAlign = "center";this.ctx.textBaseline = "middle";let textX = tag.startX + width / 2;let textY = tag.startY + height / 2;let displayText = label;if (this.ctx.measureText(label).width > width) {displayText = this.truncateText(label, width);}this.ctx.fillStyle = color;this.ctx.strokeStyle = "white";this.ctx.lineWidth = 1;this.ctx.strokeText(displayText, textX, textY);this.ctx.fillText(displayText, textX, textY);this.drawResizeHandles(tag);this.drawRotateHandle(tag);this.ctx.restore();tag.isInit = false;},drawResizeHandles(tag) {const { startX, startY, width, height, color, rotate } = tag;const handles = [{ x: startX, y: startY },{ x: startX + width / 2, y: startY },{ x: startX + width, y: startY },{ x: startX, y: startY + height / 2 },{ x: startX + width, y: startY + height / 2 },{ x: startX, y: startY + height },{ x: startX + width / 2, y: startY + height },{ x: startX + width, y: startY + height }];this.ctx.save();//this.rotateExec(tag);handles.forEach(handle => {this.ctx.beginPath();this.ctx.rect(handle.x - 2.5, handle.y - 2.5, 5, 5);this.ctx.fillStyle = "white";this.ctx.fill();this.ctx.lineWidth = 1;this.ctx.strokeStyle = color;this.ctx.stroke();//添加鼠标悬浮事件,如果鼠标悬浮在矩形框上,则鼠标样式显示为resize样式,否则显示为默认样式});this.ctx.restore();},drawRotateHandle(tag) {const { startX, startY, width, height, color, rotate } = tag;const handleX = startX + width;const handleY = startY - 12 - 5;this.ctx.save();// this.rotateExec(tag);this.ctx.beginPath();if (!this.rotateImages) {console.log("记载旋1转图片");var img = new Image();img.src = "/img/tagRotate.png";img.onload = () => {this.rotateImages = img;this.ctx.drawImage(img, handleX, handleY, 24, 24);this.ctx.restore();};} else {this.ctx.drawImage(this.rotateImages, handleX, handleY, 24, 24);this.ctx.restore();}},truncateText(text, maxWidth) {const ellipsis = "...";let truncated = text;while (this.ctx.measureText(truncated + ellipsis).width > maxWidth) {truncated = truncated.slice(0, -1);}return truncated + ellipsis;},hexToRgba(hex, alpha) {const bigint = parseInt(hex.replace("#", ""), 16);const r = (bigint >> 16) & 255;const g = (bigint >> 8) & 255;const b = bigint & 255;return `rgba(${r},${g},${b},${alpha})`;},onMouseDown(e) {const { offsetX, offsetY } = this.computexy(e.offsetX, e.offsetY);this.mouseDownX = offsetX;this.mouseDownY = offsetY;this.tagsData.forEach(tag => {const handle = this.getHandleUnderMouse(tag, offsetX, offsetY);if (handle) {this.isResizing = true; //缩放this.resizeHandle = handle;this.selectedTag = tag;return;}const rotateHandle = this.getRotateHandleUnderMouse(tag,offsetX,offsetY);if (rotateHandle) {this.isRotating = true; //旋转this.selectedTag = tag;this.initialRotation = this.selectedTag.rotate; // 保存初始旋转角度return;}if (this.isMouseInsideRectangle(tag, offsetX, offsetY)) {this.isDragging = true;this.selectedTag = tag;this.dragOffsetX = offsetX - tag.startX;this.dragOffsetY = offsetY - tag.startY;}});// if (!this.isDragging && !this.isResizing && !this.isRotating) {//   console.log("拖动canvas大小");//   this.$refs.canvas.style.cursor = "hand";//   this.isCanvasDraging = true;// }},onMouseUp() {this.isDragging = false;this.isResizing = false;this.isRotating = false;this.selectedTag = null;this.resizeHandle = null;this.isCanvasDraging = false;},onMouseMove(e) {// console.log("鼠标移动事件", e);const { offsetX, offsetY } = this.computexy(e.offsetX, e.offsetY);// if (this.isCanvasDraging) {//   this.canvasProp.translateX -= offsetX - this.mouseDownX;//   this.canvasProp.translateY -= offsetY - this.mouseDownY;//   this.drawTags();//   return;// }if (this.isDragging && this.selectedTag) {//矩形框拖动this.selectedTag.startX = offsetX - this.dragOffsetX;this.selectedTag.startY = offsetY - this.dragOffsetY;this.drawTags();} else if (this.isResizing && this.selectedTag) {//矩形框缩放const handle = this.resizeHandle;switch (handle.position) {case "top-left":this.selectedTag.width += this.selectedTag.startX - offsetX;this.selectedTag.height += this.selectedTag.startY - offsetY;this.selectedTag.startX = offsetX;this.selectedTag.startY = offsetY;break;case "top":this.selectedTag.height += this.selectedTag.startY - offsetY;this.selectedTag.startY = offsetY;break;case "top-right":this.selectedTag.width = offsetX - this.selectedTag.startX;this.selectedTag.height += this.selectedTag.startY - offsetY;this.selectedTag.startY = offsetY;break;case "left":this.selectedTag.width += this.selectedTag.startX - offsetX;this.selectedTag.startX = offsetX;break;case "right":this.selectedTag.width = offsetX - this.selectedTag.startX;break;case "bottom-left":this.selectedTag.width += this.selectedTag.startX - offsetX;this.selectedTag.height = offsetY - this.selectedTag.startY;this.selectedTag.startX = offsetX;break;case "bottom":this.selectedTag.height = offsetY - this.selectedTag.startY;break;case "bottom-right":this.selectedTag.width = offsetX - this.selectedTag.startX;this.selectedTag.height = offsetY - this.selectedTag.startY;break;}this.drawTags();} else if (this.isRotating && this.selectedTag) {//矩形旋转const centerX = this.selectedTag.startX + this.selectedTag.width / 2;const centerY = this.selectedTag.startY + this.selectedTag.height / 2;const initDeg = Math.atan2(this.mouseDownY - centerY,this.mouseDownX - centerX);const currentDeg = Math.atan2(offsetY - centerY, offsetX - centerX);// this.selectedTag.rotate = ((currentDeg - initDeg) * 180) / Math.PI;const rotationChange = ((currentDeg - initDeg) * 180) / Math.PI;this.selectedTag.rotate = this.initialRotation + rotationChange; // 根据初始旋转角度调整this.drawTags();} else {let cursorSet = false;this.tagsData.some(tag => {const handle = this.getHandleUnderMouse(tag, offsetX, offsetY);if (handle) {let cursor = this.getCursorStyle(handle);this.$refs.canvas.style.cursor = cursor;cursorSet = true;return true;}const rotateHandle = this.getRotateHandleUnderMouse(tag,offsetX,offsetY);if (rotateHandle) {this.$refs.canvas.style.cursor = "crosshair";cursorSet = true;return true;}if (this.isMouseInsideRectangle(tag, offsetX, offsetY)) {this.$refs.canvas.style.cursor = "move";cursorSet = true;return true;}return false;});if (!cursorSet) {this.$refs.canvas.style.cursor = "default";}}},getCursorCustomStyle(handle) {if (handle.position == "left" || handle.position == "right") {return `h-cursor`;} else if (handle.position === "top" || handle.position == "bottom") {return `s-cursor`;} else if (handle.position === "top-left" ||handle.position == "top-right") {return `lx-cursor`;} else if (handle.position === "bottom-left" ||handle.position == "bottom-right") {return `-cursor`;}},getCursorStyle(handle) {if (handle.position == "left") {return `w-resize`;} else if (handle.position === "top") {return `n-resize`;} else if (handle.position === "top-left") {return `nw-resize`;} else if (handle.position === "top-right") {return `ne-resize`;} else if (handle.position === "right") {return `e-resize`;} else if (handle.position === "bottom") {return `s-resize`;} else if (handle.position == "bottom-left") {return `sw-resize`;} else if (handle.position === "bottom-right") {return `se-resize`;}},getHandleUnderMouse(tag, x, y) {const handles = [{x: tag.startX,y: tag.startY,position: "top-left"},{x: tag.startX + tag.width / 2,y: tag.startY,position: "top"},{x: tag.startX + tag.width,y: tag.startY,position: "top-right"},{x: tag.startX,y: tag.startY + tag.height / 2,position: "left"},{x: tag.startX + tag.width,y: tag.startY + tag.height / 2,position: "right"},{x: tag.startX,y: tag.startY + tag.height,position: "bottom-left"},{x: tag.startX + tag.width / 2,y: tag.startY + tag.height,position: "bottom"},{x: tag.startX + tag.width,y: tag.startY + tag.height,position: "bottom-right"}];return handles.find(handle => {let { rotatedX, rotatedY } = this.rotateAfterPoint(tag, x, y);return this.isMouseOverHandle(handle, rotatedX, rotatedY);});},isMouseOverHandle(handle, x, y) {return (x >= handle.x - 2.5 &&x <= handle.x + 2.5 &&y >= handle.y - 2.5 &&y <= handle.y + 2.5);},getRotateHandleUnderMouse(tag, x, y) {let { rotatedX, rotatedY } = this.rotateAfterPoint(tag, x, y);const handleX = tag.startX + tag.width;const handleY = tag.startY - 12 - 5;if (rotatedX > handleX &&rotatedX <= handleX + 24 &&rotatedY > handleY &&rotatedY <= handleY + 24) {return true;} else {return false;}},isMouseInsideRectangle(tag, x, y) {const { startX, startY, width, height, rotate } = tag;this.ctx.save();//this.rotateExec(tag);let { rotatedX, rotatedY } = this.rotateAfterPoint(tag, x, y);const isInside =rotatedX >= startX &&rotatedX <= startX + width &&rotatedY >= startY &&rotatedY <= startY + height;this.ctx.restore();return isInside;},//解决这个问题有两个思路,一个是将旋转后矩形的四个点坐标计算出来,这种方法比较麻烦。另一个思路是逆向的,将要判断的点,以矩形的中点为中心,做逆向旋转,计算出其在 canvas 中的坐标,这个坐标,可以继续参与我们之前点在矩形内的计算rotateAfterPoint(tag, x, y) {const { startX, startY, width, height, rotate } = tag;const centerX = startX + width / 2;const centerY = startY + height / 2;let dx = x - centerX;let dy = y - centerY;// // 将鼠标点旋转回矩形未旋转前的坐标let rotatedX =dx * Math.cos((-rotate * Math.PI) / 180) -dy * Math.sin((-rotate * Math.PI) / 180) +centerX;let rotatedY =dy * Math.cos((-rotate * Math.PI) / 180) +dx * Math.sin((-rotate * Math.PI) / 180) +centerY;return { rotatedX: rotatedX, rotatedY: rotatedY };}}
};
</script><style scoped>
.h-cursor {cursor: url("/img/h-custor"), auto !important;
}
.s-cursor {cursor: url("/img/s-custor"), auto !important;
}
.lx-cursor {cursor: url("/img/lx-custor"), auto !important;
}
.yx-cursor {cursor: url("/img/yx-custor"), auto !important;
}
.rotate-cursor {cursor: url("/img/yx-custor"), auto !important;
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【时时三省】unity test 测试框架 使用 code blocks 移植

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 目录 1&#xff0c;使用 Code::Blocks 17.12 创建工程 2&#xff0c;移植文件至该工程下&#xff1a; 移入的文件为: 被移入的文件介绍&#xff1a; 更改代码&#xff1a; 向工程添加文…

k8s 部署RuoYi-Vue-Plus之ingress域名解析

可参看https://blog.csdn.net/weimeibuqieryu/article/details/140798925 搭建ingress 1.创建Ingress对象 ingress-ruoyi.yaml其中host替换为你对应域名&#xff0c;需要解析域名到服务器, 同时为后端服务添加了二级域名解析 api. 访问http://xxx.xyz/就能访问前端&#xff0…

力扣SQL50 修复表中的名字 字符串函数

Problem: 1667. 修复表中的名字 &#x1f468;‍&#x1f3eb; 参考题解 select user_id, CONCAT(UPPER(left(name, 1)), LOWER(RIGHT(name, length(name) - 1))) as name from Users order by user_id

【Linux系统编程】:进程地址空间1

1.引出进程地址空间 因为str指向的是字符串首字母的地址&#xff0c;首字母是字符常量“h”&#xff0c;地址存储在字符常量区&#xff0c;无法修改&#xff0c;故报错。 Linux进程地址空间与进程内存布局详解 - 知乎 (zhihu.com) 我们编写一段代码&#xff0c;来认识一下存储…

JavaWeb学习——mybatis

目录 一、入门学习 1、什么是mybatis&#xff1f; 2、入门使用 3、配置SQL提示 4、数据库连接池 5、lombok 二、基础操作学习 1、删除 2、新增 3、更新 4、查询 三、XML配置文件 1、映射规范 2、示例代码展示 四、动态SQL 1、学习 2、学习 3、学习 4、学习 一…

Django文件上传

【图书介绍】《Django 5企业级Web应用开发实战&#xff08;视频教学版&#xff09;》_django 5企业级web应用开发实战(视频教学版)-CSDN博客 《Django 5企业级Web应用开发实战&#xff08;视频教学版&#xff09;》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com) 本节主要介…

算法力扣刷题记录 六十三【回溯章节开篇】

前言 开始回溯章节学习。 在二叉树中预先体会了回溯。那么回溯单独来说是怎么回事&#xff1f; 一、基础知识学习 回溯基础知识参考链接 二、组合问题 2.1题目阅读 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答…

【C++入门(上)】—— 我与C++的不解之缘(一)

前言&#xff1a; 学完C语言和初阶数据结构&#xff0c;感觉自己又行了&#xff1f; 接下来进入C的学习&#xff0c;准备好接受头脑风暴吧。 一、第一个C程序 C 的第一个程序&#xff0c;梦回出学C语言&#xff0c;第一次使用C语言写代码&#xff1b;这里使用C写第一个C代码。 …

对优先级队列(堆)的理解

目录&#xff1a; 一. 优先级队列&#xff1a; 二. 优先级队列的模拟实现&#xff1a; 三.常用接口介绍: 一. 优先级队列&#xff1a; 1 概念&#xff1a; 队列是一种先进先出的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能带有优先级&#xff0c;一般出队列时…

Linux系统目录结构

Linux系统下一切皆文件 &#xff01;&#xff01;&#xff01; 系统启动必须: /boot : 存放启动Linux时所需的内核文件&#xff0c;包括压缩后的内核镜像文件(vmlinuz)、虚拟文件系统镜像文件(initrd.img)、启动引导grub的配置文件。/etc : 系统全局配置文件&#xff0c;会影…

从Excel高手到SQL大师-解锁数据分析的无限潜力 -10分钟读懂职场必备技能

目录 Excel 和 SQL&#xff1a;看似相似却大不相同的数据处理利器Excel vs SQL&#xff1a;表面相似&#xff0c;本质迥异Excel&#xff1a;直观但受限的电子表格SQL&#xff1a;强大而灵活的数据库查询语言 从 Excel 到 SQL&#xff1a;跨越鸿沟Excel 数据筛选SQL 数据筛选 结…

基于 Kafka 的经验:AutoMQ 和 MinIO 如何解决成本和弹性挑战

Apache Kafka 因其出色的设计和强大的功能而成为流式处理的事实标准。它不仅定义了现代流式处理的架构&#xff0c;而且其独特的分布式日志抽象还为实时数据流处理和分析提供了前所未有的功能。Kafka 的成功在于它能够满足高吞吐量和低延迟的数据处理需求&#xff0c;多年来&am…

论文阅读:Most Probable Densest Subgraphs

摘要 本文提出了一种在不确定图中发现最有可能稠密子图&#xff08;MPDS&#xff09;的新方法。不确定图中的每条边都有存在概率&#xff0c;使得计算稠密子图变得複杂。作者定义了稠密子图概率&#xff0c;并证明了计算该概率是#P难的。为了解决这个问题&#xff0c;设计了基…

数据科学 - 数据预处理 (数据清洗,结构化数据)

1. 前言 数据清洗与结构化数据在数据分析和机器学习项目中扮演着至关重要的角色。随着大数据时代的到来&#xff0c;数据的质量、准确性和可用性成为决定项目成功与否的关键因素。 数据清洗提高数据质量&#xff0c;保证数据集的一致性&#xff1b;促进数据分析与挖掘&#xf…

【大数据开发语言Scala的入门教程】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 🪁Scala 🪡Scala是一种功能丰富且具有强大表达能力的静态类型…

【2024蓝桥杯/C++/B组/传送阵】

题目 问题代码 #include<bits/stdc.h> using namespace std;const int N 1e610; int n; int porter[N]; int ans; int sign[N]; bool used;void dfs(int now, int cnt) {if(sign[now] && used){ans max(ans, cnt);return;}if(!sign[now]){cnt, sign[now] 1; …

成为git砖家(8): 使用 git log 查询范围内的 commit

文章目录 1. 查询 git log 的文档2. 不带任何参数: git log 啥意思&#xff1f;3. git log 最主要功能是什么&#xff1f;4. git log <commit1>..<commit2> 什么意思5. 查看最近n次commit6. References 1. 查询 git log 的文档 git help log --web市面上针对 git …

ubuntu sudo命令不需要密码

sudo vim /etc/sudoers1、注释掉 %sudo ALL(ALL:ALL) AL 2、添加 用户名 ALL(ALL:ALL) NOPASSWD:ALL保存&#xff0c;退出即可

NineData云原生智能数据管理平台新功能发布|2024年7月版

本月发布 12 项更新&#xff0c;其中性能优化 3 项、功能优化 8 项、安全性发布 1 项。 1. 性能优化 数据复制 - SQL Server 增量性能优化 调整读取和写入方式&#xff0c;让 SQL Server 增量复制的性能轻松达到 5000 RPS 以上。 数据复制 - Doris|SelectDB|StarRocks 性能优…

链式二叉树的实现

文章目录 &#x1f3af;引言&#x1f453;链式二叉树的实现1.链式二叉树的结构2.链式二叉树相关操作实现2.1源码展示2.2函数实现详解2.2.1前中后序遍历2.2.2二叉树的其他方法实现2.2.3二叉树的层序遍历和判断是否是完全二叉树 &#x1f947;结语 &#x1f3af;引言 欢迎来到Ha…