当前位置: 首页 > news >正文

前端图像处理(一)

目录

一、上传

1.1、图片转base64

二、图片样式

2.1、图片边框【border-image】

三、Canvas

3.1、把canvas图片上传到服务器

3.2、在canvas中绘制和拖动矩形

3.3、图片(同色区域)点击变色

一、上传

1.1、图片转base64

传统上传:

客户端选择图片,将图片上传到服务端,等服务端返回一个url,客户端再将url设置到图片的src里。图片在渲染时,通过url去请求服务器,服务端就会回馈url,客户端就可以看到预览图了。

优化上传:

客户端选择图片后立刻展示,然后继续上传到服务端保存,他俩互不影响。

了解:

url【统一资源定位符】: 协议://主机名[:端口号]/路径名[?查询字符串][#片段标识符]。

MIME【多用途互联网邮件扩展】: 指示数据的内容类型。

MIME类型内容表示含义
文本类型text/html超文本标记语言,用于网页
图像类型image/pngPNG 图像格式,支持透明度
 音频类型audio/mpegMP3 音频格式
 应用类型application/jsonJSON 数据格式,用于数据交换
多部分类型multipart/form-data用于 HTTP 表单数据,特别是文件上传

示例:

<body><!-- 运行后页面会弹出 alert(123)--><script src="data:application/javascript,alert(123)"></script>
</body>

base64:二进制数据转为ASCII 字符串

科普:在js中:

btoa() 函数用于将字符串进行 Base64 编码【btoa('alert(123)')】

atob() 函数用于将 Base64 编码的字符串解码回原始字符串【atob('YWxlcnQoMTIzKQ==')】

进入正题:

	<body><input type="file" /><img src="" alt="" id="preview" /><script src="./1.base64.js"></script></body>
const inp = document.querySelector("input");
inp.onchange = function () {const file = inp.files[0];//多文件,所以是数组const reader = new FileReader();//创建了一个FileReader 对象,用于读取文件内容reader.onload = (e) => {preview.src = e.target.result;// e.target.result 以 Data URL 格式表示,并赋值console.log(file,'转化后',e.target.result)};reader.readAsDataURL(file);//告诉FileReader 以 Data URL 格式读取文件内容
};

后端有时要FormData格式并添加其他参数,而不是原始的二进制格式,可以参考下:

    formatImage(type, file) {if (!this.fileUrl) {this.$message.warning('请上传图片')return false}for (let i = 0; i < 5; i++) {const form = new FormData()form.append('matting_type', i + 1)form.append('hd_type', i + 1)form.append('file', file)waterAxios.post('/oss/upload', form).then((res) => {if (res.code == 200) {this.$message.success('上传ok')}})}},

二进制格式上传的消息格式:application/octet-stream

FormData格式上传的消息格式:multipart/form-data

二、图片样式

2.1、图片边框【border-image】

	<style>body {background-color: black;}.bdr-img {color: white;text-align: center;padding: 5rem;margin: 2rem auto;width: 50%;border: 50px solid #fff;border-image: url(./stamp.svg) 50 round;/* 相当于下面三行代码的组合 *//* border-image-source: url(./stamp.svg);border-image-slice: 50;border-image-repeat: round; */}</style><body><div class="bdr-img"><p>Hello, My name is [Your Name], and I am a [Your Profession] with [Numberof Years] years of experience in [Your Industry]. I specialize in [YourArea of Expertise] and have a strong background in [Relevant Skills orTechnologies].</p></div></body>

三、Canvas

3.1、把canvas图片上传到服务器

  let base64 = canvas.toDataURL()//canvas指canvas格式的图片let imgUrlBlob = dataURLToBlob(base64)var file = new window.File([imgUrlBlob], 'image.png', { type: 'image/png' })let fd = new FormData()fd.append('image', file)

3.2、在canvas中绘制和拖动矩形

	<body><div><input type="color" /></div><canvas></canvas><script src="./canvas.js"></script></body>
//============================canvas.js==================
const collorPicker = document.querySelector("input");
const cvs = document.querySelector("canvas");
const ctx = cvs.getContext("2d");
function init() {const w = 500,h = 300;cvs.width = w * devicePixelRatio;cvs.height = h * devicePixelRatio;cvs.style.width = w + "px";cvs.style.height = h + "px";cvs.style.backgroundColor = "gray";
}
init();
const shapes = [];
// 绘制矩形
// 矩形分为起始坐标和结束坐标,最初结束坐标就是起始坐标,结束坐标随着绘制发生改变
// 告诉canvas左上角是起始坐标,确定最小值和最大值
class Rectangle {constructor(color, startX, startY) {this.color = color;this.startX = startX;this.startY = startY;this.endX = startX;this.endY = startY;}//访问器属性get minX() {return Math.min(this.startX, this.endX);}get minY() {return Math.min(this.startY, this.endY);}get maxX() {return Math.max(this.startX, this.endX);}get maxY() {return Math.max(this.startY, this.endY);}draw() {ctx.beginPath();ctx.moveTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio); //左上角(起始坐标)ctx.lineTo(this.maxX * devicePixelRatio, this.minY * devicePixelRatio); //从左上角到右上角ctx.lineTo(this.maxX * devicePixelRatio, this.maxY * devicePixelRatio); //从右上角到右下角ctx.lineTo(this.minX * devicePixelRatio, this.maxY * devicePixelRatio); //从右下角到左下角ctx.lineTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio); //从左下角到左上角ctx.fillStyle = this.color;ctx.fill(); //颜色填充ctx.strokeStyle = "#fff"; //画笔颜色ctx.lineCap = "square"; //线条交界处变圆润ctx.lineWidth = 3 * devicePixelRatio; //画笔宽度ctx.stroke(); //完成边框的绘制}
}
// 自己随意画一个矩形
// const rect = new Rectangle("red", 100, 100);
// rect.endX = 200;
// rect.endY = 200;
// rect.draw();
// 鼠标按下确定起始位置,鼠标移动确定结束位置,鼠标抬起结束事件
cvs.onmousedown = (e) => {const bouding = cvs.getBoundingClientRect();const rect = new Rectangle(collorPicker.value, e.offsetX, e.offsetY);// 进行判断const shape = getShape(e.offsetX, e.offsetY);if (shape) {const { startX, startY, endX, endY } = shape;const moveX = e.offsetX;const moveY = e.offsetY;window.onmousemove = (e) => {//拖动矩形const disX = e.clientX - bouding.left - moveX;const disY = e.clientY - bouding.top - moveY;shape.startX = startX + disX;shape.startY = startY + disY;shape.endX = endX + disX;shape.endY = endY + disY;};window.onmouseup = () => {window.onmousemove = null;window.onmouseup = null;};} else {shapes.push(rect); //将每个矩形数据加进去window.onmousemove = (e) => {rect.endX = e.clientX - bouding.left;rect.endY = e.clientY - bouding.top;};window.onmouseup = () => {window.onmousemove = null;window.onmouseup = null;};}
};
// 辅助函数:判断鼠标按下时是否落在某个矩形内?是:执行移动 否:执行新建矩形
function getShape(x, y) {// 从后往前遍历矩形数组,找到最上面的那个矩形for (let i = shapes.length - 1; i >= 0; i--) {if (x >= shapes[i].minX &&x <= shapes[i].maxX &&y >= shapes[i].minY &&y <= shapes[i].maxY) {return shapes[i];}}return null;
}
// 将shapes依次渲染出来
function draw() {requestAnimationFrame(draw);ctx.clearRect(0, 0, cvs.width, cvs.height); //画完清空一下for (const shape of shapes) {shape.draw();}
}
draw(); //初始化执行一次,后续在每一帧里执行“画”这个动作,前提:数据shapes已经有了

3.3、图片(同色区域)点击变色

	<body><canvas></canvas><script src="./index.js"></script></body>
const cvs = document.querySelector("canvas");
const ctx = cvs.getContext("2d", { willReadFrequently: true }); //获取 Canvas 上下文function init() {const img = new Image();img.onload = () => {cvs.width = img.width;cvs.height = img.height;ctx.drawImage(img, 0, 0, img.width, img.height);}; //当图片加载完成时:将图片绘制到画布上img.src = "./redhat.png";
}
init(); //初始化时加载图片cvs.addEventListener("click", (e) => {const x = e.offsetX,y = e.offsetY;// 1、获取点击位置的颜色: imgData.data就是目标对象所有的颜色信息const imgData = ctx.getImageData(0, 0, cvs.width, cvs.height); //开始范围,结束范围const clickColor = getColor(x, y, imgData.data); //点击位置// 2、改变颜色const targetColor = [46, 139, 87, 255]; // 改变后颜色为绿色,透明度为不透明const visited = new Set(); // 记录访问过的像素点changeColor(x, y, targetColor, imgData.data, clickColor, visited); //点击的像素点改变了ctx.putImageData(imgData, 0, 0);
});function pointIndex(x, y) {return (y * cvs.width + x) * 4;
}function getColor(x, y, imgData) {const index = pointIndex(x, y);return [imgData[index],imgData[index + 1],imgData[index + 2],imgData[index + 3],]; //分别对应:r、g、b、a
}// 使用BFS来代替递归
function changeColor(x, y, targetColor, imgData, clickColor, visited) {const queue = [[x, y]]; // 用队列保存待处理的像素点const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; // 上下左右四个方向visited.add(`${x},${y}`); // 初始像素点标记为已访问while (queue.length > 0) {const [cx, cy] = queue.shift(); // 从队列中取出一个像素点const index = pointIndex(cx, cy);const curColor = getColor(cx, cy, imgData);// 如果颜色差异大于100或当前像素已经是目标颜色,就跳过if (diff(clickColor, curColor) > 100 || diff(curColor, targetColor) === 0) {continue;}// 修改颜色imgData.set(targetColor, index);// 对周围的像素点进行处理(上下左右)for (const [dx, dy] of directions) {const nx = cx + dx, ny = cy + dy;// 检查边界if (nx >= 0 && nx < cvs.width && ny >= 0 && ny < cvs.height) {const key = `${nx},${ny}`;if (!visited.has(key) && diff(clickColor, getColor(nx, ny, imgData)) <= 100) {visited.add(key); // 标记为已访问queue.push([nx, ny]); // 将该像素点加入队列}}}}
}function diff(color1, color2) {return (Math.abs(color1[0] - color2[0]) +Math.abs(color1[1] - color2[1]) +Math.abs(color1[2] - color2[2]) +Math.abs(color1[3] - color2[3]));
} //计算颜色差异

第三个案例总结:

最初使用无穷递归来实现:

    // 递归找相同的像素点(上下左右)changeColor(x + 1, y, targetColor, imgData, clickColor, visited);changeColor(x - 1, y, targetColor, imgData, clickColor, visited);changeColor(x, y + 1, targetColor, imgData, clickColor, visited);changeColor(x, y - 1, targetColor, imgData, clickColor, visited);

但是导致了Maximum call stack size exceeded。最后使用广度优先搜索(BFS)来替代递归:

优势:

(1)使用队列实现BFS:保存待处理的像素点,避免递归带来的栈溢出;

(2)逐层处理:通过 queue.shift() 从队列中取出当前像素点,检查它的上下左右四个方向,并将符合条件的邻接像素点加入队列。

(3)避免重复访问:通过 visited 集合避免重复访问已处理过的像素点。

......待更新

相关文章:

前端图像处理(一)

目录 一、上传 1.1、图片转base64 二、图片样式 2.1、图片边框【border-image】 三、Canvas 3.1、把canvas图片上传到服务器 3.2、在canvas中绘制和拖动矩形 3.3、图片(同色区域)点击变色 一、上传 1.1、图片转base64 传统上传&#xff1a; 客户端选择图片&#xf…...

unity中:超低入门级显卡、集显(功耗30W以下)运行unity URP管线输出的webgl程序有那些地方可以大幅优化帧率

删除Global Volume&#xff1a; 删除Global Volume是一项简单且高效的优化措施。实测表明&#xff0c;这一改动可以显著提升帧率&#xff0c;甚至能够将原本无法流畅运行的场景变得可用。 更改前的效果&#xff1a; 更改后的效果&#xff1a; 优化阴影和材质&#xff1a; …...

ftdi_sio应用学习笔记 4 - I2C

目录 1. 查找设备 2. 打开设备 3. 写数据 4. 读数据 5. 设置频率 6 验证 6.1 遍历设备 6.2 开关设备 6.3 读写测试 I2C设备最多有6个&#xff08;FT232H&#xff09;&#xff0c;其他为2个。和之前的设备一样&#xff0c;定义个I2C结构体记录找到的设备。 #define FT…...

如何更好的把控软件测试质量

如何更好的把控软件测试质量 在软件开发过程中&#xff0c;测试是确保软件质量、稳定性和用户体验的重要环节。随着需求的不断变化以及技术的不断进步&#xff0c;如何更好的把控软件测试质量已成为一个不可忽视的话题。本文将从几个维度探讨确保软件质量的方法和方案&#xf…...

“漫步北京”小程序及“气象景观数字化服务平台”上线啦

随着科技的飞速发展&#xff0c;智慧旅游已成为现代旅游业的重要趋势。近日&#xff0c;北京万云科技有限公司联合北京市气象服务中心&#xff0c;打造的“气象景观数字化服务平台“和“漫步北京“小程序已经上线&#xff0c;作为智慧旅游的典型代表&#xff0c;以其丰富的功能…...

SOL链上的 Meme 生态发展:从文化到创新的融合#dapp开发#

一、引言 随着区块链技术的不断发展&#xff0c;Meme 文化在去中心化领域逐渐崭露头角。从 Dogecoin 到 Shiba Inu&#xff0c;再到更多细分的 Meme 项目&#xff0c;这类基于网络文化的加密货币因其幽默和社区驱动力吸引了广泛关注。作为近年来备受瞩目的区块链平台之一&…...

身份证实名认证API接口助力电商购物安全

亲爱的网购达人们&#xff0c;你们是否曾经因为网络上的虚假信息和诈骗而感到困扰&#xff1f;在享受便捷的网购乐趣时&#xff0c;如何确保交易安全成为了我们共同关注的话题。今天&#xff0c;一起来了解一下翔云身份证实名认证接口如何为电子商务保驾护航&#xff0c;让您的…...

【过程控制系统】第6章 串级控制系统

目录 6. l 串级控制系统的概念 6.1.2 串级控制系统的组成 6.l.3 串级控制系统的工作过程 6.2 串级控制系统的分析 6.2.1 增强系统的抗干扰能力 6.2.2 改善对象的动态特性 6.2.3 对负荷变化有一定的自适应能力 6.3 串级控制系统的设计 6.3.1 副回路的选择 2.串级系…...

YOLOv11融合针对小目标FFCA-YOPLO中的FEM模块及相关改进思路

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 YOLOv11改进汇总贴&#xff1a;YOLOv11及自研模型更新汇总 《FFCA-YOLO for Small Object Detection in Remote Sensing Images》 一、 模块介绍 论文链接&#xff1a;https://ieeexplore.ieee.org/document/10…...

qt+opengl 三维物体加入摄像机

1 在前几期的文章中&#xff0c;我们已经实现了三维正方体的显示了&#xff0c;那我们来实现让物体的由远及近&#xff0c;和由近及远。这里我们需要了解一个概念摄像机。 1.1 摄像机定义&#xff1a;在世界空间中位置、观察方向、指向右侧向量、指向上方的向量。如下图所示: …...

day05(单片机高级)PCB基础

目录 PCB基础 什么是PCB&#xff1f;PCB的作用&#xff1f; PCB的制作过程 PCB板的层数 PCB设计软件 安装立创EDA PCB基础 什么是PCB&#xff1f;PCB的作用&#xff1f; PCB&#xff08;Printed Circuit Board&#xff09;&#xff0c;中文名称为印制电路板&#xff0c;又称印刷…...

全球天气预报5天-经纬度版免费API接口教程

接口简介&#xff1a; 获取全球任意地区未来5天天气预报&#xff0c;必须传经纬度参数。可先调用【位置坐标】分类下相关接口获取地区经纬度坐标。 请求地址&#xff1a; https://cn.apihz.cn/api/tianqi/tqybjw5.php 请求方式&#xff1a; POST或GET。 请求参数&#xff1a…...

Shell编程8

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…...

python语言基础-5 进阶语法-5.5 上下文管理协议(with语句)

声明&#xff1a;本内容非盈利性质&#xff0c;也不支持任何组织或个人将其用作盈利用途。本内容来源于参考书或网站&#xff0c;会尽量附上原文链接&#xff0c;并鼓励大家看原文。侵删。 5.5 上下文管理协议&#xff08;with语句&#xff09;&#xff08;参考链接&#xff1…...

自动驾驶3D目标检测综述(三)

前两篇综述阅读理解放在这啦&#xff0c;有需要自行前往观看&#xff1a; 第一篇&#xff1a;自动驾驶3D目标检测综述&#xff08;一&#xff09;_3d 目标检测-CSDN博客 第二篇&#xff1a;自动驾驶3D目标检测综述&#xff08;二&#xff09;_子流行稀疏卷积 gpu实现-CSDN博客…...

【GESP】C++三级练习 luogu-B3661, [语言月赛202209] 排排

三级知识点一维数组练习&#xff0c;除了应用了数组以外&#xff0c;其余逻辑比较简单&#xff0c;适合初学者。 题目题解详见&#xff1a;https://www.coderli.com/gesp-3-luogu-b3661/ 【GESP】C三级练习 luogu-B3661, [语言月赛202209] 排排队 | OneCoder三级知识点一维数…...

【PPTist】添加PPT模版

前言&#xff1a;这篇文章来探索一下如何应用其他的PPT模版&#xff0c;给一个下拉菜单&#xff0c;列出几个项目中内置的模版 PPT模版数据 &#xff08;一&#xff09;增加菜单项 首先在下面这个菜单中增加一个“切换模版”的菜单项&#xff0c;点击之后在弹出框中显示所有的…...

大疆上云api开发

目前很多公司希望使用上云api开发自己的无人机平台,但是官网资料不是特别全,下面浅谈一下本人开发过程中遇到的一系列问题。 本人使用机场为大疆机场2&#xff0c;飞机为M3TD&#xff0c;纯内网使用 部署 链接: 上云api代码. 首先从github上面拉去代码 上云api代码github. 后…...

IDEA2023 SpringBoot整合MyBatis(三)

一、数据库表 CREATE TABLE students (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100) NOT NULL,age INT,gender ENUM(Male, Female, Other),email VARCHAR(100) UNIQUE,phone_number VARCHAR(20),address VARCHAR(255),date_of_birth DATE,enrollment_date DATE,cours…...

【Apache Paimon】-- 6 -- 清理过期数据

目录 1、简要介绍 2、操作方式和步骤 2.1、调整快照文件过期时间 2.2、设置分区过期时间 2.2.1、举例1 2.2.2、举例2 2.3、清理废弃文件 3、参考 1、简要介绍 清理 paimon (表)过期数据可以释放存储空间,优化资源利用并提升系统运行效率等。本文将介绍如何清理 Paim…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...