前端计算机视觉:使用 OpenCV.js 在浏览器中实现图像处理

一、OpenCV.js 简介与环境搭建

OpenCV(Open Source Computer Vision Library)是一个强大的计算机视觉库,广泛应用于图像和视频处理领域。传统上,OpenCV 主要在后端使用 Python 或 C++ 等语言。但随着 WebAssembly (Wasm) 技术的发展,OpenCV 也有了 JavaScript 版本 ——OpenCV.js,它可以直接在浏览器中高效运行,为前端开发者提供了前所未有的计算机视觉能力。

1.1 引入 OpenCV.js

在浏览器中使用 OpenCV.js 有多种方式,最简单的是通过 CDN 引入:

<script async src="https://docs.opencv.org/4.5.5/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>

这种方式适合快速测试和开发。另一种方式是将 OpenCV.js 下载到本地项目中:

npm install @techstark/opencv-js

然后在 HTML 中引入:

<script async src="node_modules/@techstark/opencv-js/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>

1.2 初始化与加载检查

由于 OpenCV.js 是一个较大的库,需要异步加载。我们可以通过以下方式确保库加载完成后再执行相关代码:

function onOpenCvReady() {document.getElementById('status').innerHTML = 'OpenCV.js 已加载完成';// 在这里开始使用 OpenCV.jscvVersion = cv.getVersion();console.log('OpenCV 版本:', cvVersion);
}

在 HTML 中添加状态显示元素:

<body><div id="status">正在加载 OpenCV.js...</div><!-- 其他页面内容 -->
</body>

二、基本图像处理操作

2.1 图像读取与显示

OpenCV.js 主要处理 cv.Mat 对象(矩阵),这是存储图像数据的核心结构。下面是一个从 HTML Image 元素读取图像并显示的完整示例:

<!DOCTYPE html>
<html>
<head><title>OpenCV.js 图像读取与显示示例</title><script async src="https://docs.opencv.org/4.5.5/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script><style>.container {display: flex;flex-direction: column;align-items: center;margin-top: 20px;}.canvas-container {display: flex;gap: 20px;margin-top: 20px;}canvas {border: 1px solid #ccc;}</style>
</head>
<body><div class="container"><h2>OpenCV.js 图像读取与显示</h2><div id="status">正在加载 OpenCV.js...</div><img id="imageSrc" src="example.jpg" alt="示例图片" crossorigin="anonymous" style="display: none;"><div class="canvas-container"><div><p>原始图像</p><canvas id="inputCanvas"></canvas></div><div><p>处理后图像</p><canvas id="outputCanvas"></canvas></div></div></div><script>let src, dst, inputCanvas, outputCanvas;function onOpenCvReady() {document.getElementById('status').innerHTML = 'OpenCV.js 已加载完成';// 初始化画布和图像矩阵inputCanvas = document.getElementById('inputCanvas');outputCanvas = document.getElementById('outputCanvas');// 等待图像加载完成const img = document.getElementById('imageSrc');img.onload = function() {// 设置画布大小inputCanvas.width = img.width;inputCanvas.height = img.height;outputCanvas.width = img.width;outputCanvas.height = img.height;// 读取图像到 Mat 对象src = cv.imread(img);dst = new cv.Mat();// 在输入画布上显示原始图像cv.imshow(inputCanvas, src);// 示例:复制图像到输出画布src.copyTo(dst);cv.imshow(outputCanvas, dst);// 释放资源// 注意:在实际应用中,当不再需要 Mat 对象时应及时释放// src.delete();// dst.delete();}// 如果图像已经加载if (img.complete) {img.onload();}}</script>
</body>
</html>

这个示例展示了 OpenCV.js 的基本工作流程:加载图像、创建 Mat 对象、处理图像、显示结果。需要注意的是,OpenCV.js 使用的内存需要手动管理,通过调用 delete () 方法释放不再使用的 Mat 对象。

2.2 颜色空间转换

颜色空间转换是图像处理中的常见操作。例如,将彩色图像转换为灰度图像:

// 假设 src 是已经加载的彩色图像
dst = new cv.Mat();
// 使用 COLOR_RGB2GRAY 标志进行转换
cv.cvtColor(src, dst, cv.COLOR_RGB2GRAY);
// 显示灰度图像
cv.imshow(outputCanvas, dst);

也可以在不同的颜色空间之间进行转换,比如从 RGB 到 HSV:

cv.cvtColor(src, dst, cv.COLOR_RGB2HSV);

2.3 图像滤波

图像滤波是平滑图像、去除噪声或增强特定特征的常用技术。以下是几种常见的滤波操作:

2.3.1 高斯模糊
// 定义核大小,必须是奇数
let ksize = new cv.Size(5, 5);
// 定义标准差
let sigmaX = 0;
let sigmaY = 0;
cv.GaussianBlur(src, dst, ksize, sigmaX, sigmaY, cv.BORDER_DEFAULT);
2.3.2 中值滤波
// 定义核大小,必须是大于 1 的奇数
let ksize = 5;
cv.medianBlur(src, dst, ksize);
2.3.3 双边滤波
// 定义参数
let d = 9; // 过滤时使用的像素领域直径
let sigmaColor = 75; // 颜色空间滤波器的sigma值
let sigmaSpace = 75; // 坐标空间中滤波器的sigma值
cv.bilateralFilter(src, dst, d, sigmaColor, sigmaSpace);

2.4 边缘检测

边缘检测是计算机视觉中的重要任务,常用于特征提取和图像分割。

2.4.1 Canny 边缘检测
// 转换为灰度图像
let gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGB2GRAY);// 应用 Canny 边缘检测
let edges = new cv.Mat();
let threshold1 = 100;
let threshold2 = 200;
let apertureSize = 3;
let L2gradient = false;
cv.Canny(gray, edges, threshold1, threshold2, apertureSize, L2gradient);// 显示结果
cv.imshow(outputCanvas, edges);// 释放资源
gray.delete();
edges.delete();
2.4.2 Sobel 算子
// 转换为灰度图像
let gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGB2GRAY);// 创建输出矩阵
let sobelx = new cv.Mat();
let sobely = new cv.Mat();
let abs_sobelx = new cv.Mat();
let abs_sobely = new cv.Mat();
let sobel_edges = new cv.Mat();// 计算 x 和 y 方向的梯度
cv.Sobel(gray, sobelx, cv.CV_16S, 1, 0, 3, 1, 0, cv.BORDER_DEFAULT);
cv.Sobel(gray, sobely, cv.CV_16S, 0, 1, 3, 1, 0, cv.BORDER_DEFAULT);// 转换为 8 位无符号整数
cv.convertScaleAbs(sobelx, abs_sobelx);
cv.convertScaleAbs(sobely, abs_sobely);// 合并两个方向的梯度
cv.addWeighted(abs_sobelx, 0.5, abs_sobely, 0.5, 0, sobel_edges);// 显示结果
cv.imshow(outputCanvas, sobel_edges);// 释放资源
gray.delete();
sobelx.delete();
sobely.delete();
abs_sobelx.delete();
abs_sobely.delete();
sobel_edges.delete();

三、特征提取与描述

3.1 Harris 角点检测

角点是图像中重要的局部特征,Harris 角点检测是一种经典的角点检测方法:

// 转换为灰度图像
let gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGB2GRAY);// 创建输出矩阵
let dstHarris = new cv.Mat();
let dstNorm = new cv.Mat();
let dstNormScaled = new cv.Mat();// 应用 Harris 角点检测
let blockSize = 2;
let apertureSize = 3;
let k = 0.04;
cv.cornerHarris(gray, dstHarris, blockSize, apertureSize, k, cv.BORDER_DEFAULT);// 归一化结果
cv.normalize(dstHarris, dstNorm, 0, 255, cv.NORM_MINMAX, cv.CV_32FC1, new cv.Mat());
cv.convertScaleAbs(dstNorm, dstNormScaled);// 在原图上绘制角点
for (let j = 0; j < dstNorm.rows; j++) {for (let i = 0; i < dstNorm.cols; i++) {if (parseInt(dstNorm.ptr(j, i)[0]) > 100) {cv.circle(dstNormScaled, new cv.Point(i, j), 5, [0, 255, 0], 2, 8, 0);}}
}// 显示结果
cv.imshow(outputCanvas, dstNormScaled);// 释放资源
gray.delete();
dstHarris.delete();
dstNorm.delete();
dstNormScaled.delete();

3.2 ORB (Oriented FAST and Rotated BRIEF)

ORB 是一种结合了 FAST 特征点检测和 BRIEF 特征描述子的高效特征提取方法:

// 转换为灰度图像
let gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGB2GRAY);// 创建 ORB 检测器
let orb = new cv.ORB();// 检测关键点并计算描述符
let keypoints = new cv.KeyPointVector();
let descriptors = new cv.Mat();
orb.detectAndCompute(gray, new cv.Mat(), keypoints, descriptors);// 在原图上绘制关键点
let output = new cv.Mat();
cv.cvtColor(gray, output, cv.COLOR_GRAY2BGR);
cv.drawKeypoints(gray, keypoints, output, [0, 255, 0], 0);// 显示结果
cv.imshow(outputCanvas, output);// 释放资源
gray.delete();
orb.delete();
keypoints.delete();
descriptors.delete();
output.delete();

四、图像分割

4.1 阈值分割

阈值分割是最简单的图像分割方法,根据像素值与阈值的比较将图像分为不同区域:

// 转换为灰度图像
let gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGB2GRAY);// 应用阈值分割
let dst = new cv.Mat();
let thresholdValue = 127;
let maxValue = 255;
let thresholdType = cv.THRESH_BINARY;
cv.threshold(gray, dst, thresholdValue, maxValue, thresholdType);// 显示结果
cv.imshow(outputCanvas, dst);// 释放资源
gray.delete();
dst.delete();

4.2 自适应阈值分割

自适应阈值分割根据像素周围区域的局部特性计算阈值,适合处理光照不均匀的图像:

// 转换为灰度图像
let gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGB2GRAY);// 应用自适应阈值分割
let dst = new cv.Mat();
let maxValue = 255;
let adaptiveMethod = cv.ADAPTIVE_THRESH_GAUSSIAN_C;
let thresholdType = cv.THRESH_BINARY;
let blockSize = 11;
let C = 2;
cv.adaptiveThreshold(gray, dst, maxValue, adaptiveMethod, thresholdType, blockSize, C);// 显示结果
cv.imshow(outputCanvas, dst);// 释放资源
gray.delete();
dst.delete();

4.3 基于轮廓的分割

轮廓检测可以识别图像中的连续区域,常用于物体分割:

// 转换为灰度图像
let gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGB2GRAY);// 应用阈值处理
let thresh = new cv.Mat();
cv.threshold(gray, thresh, 127, 255, cv.THRESH_BINARY);// 查找轮廓
let contours = new cv.MatVector();
let hierarchy = new cv.Mat();
cv.findContours(thresh, contours, hierarchy, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE);// 在原图上绘制轮廓
let drawing = cv.Mat.zeros(thresh.size(), cv.CV_8UC3);
for (let i = 0; i < contours.size(); i++) {let color = new cv.Scalar(Math.random() * 255, Math.random() * 255, Math.random() * 255);cv.drawContours(drawing, contours, i, color, 2, cv.LINE_8, hierarchy, 0);
}// 显示结果
cv.imshow(outputCanvas, drawing);// 释放资源
gray.delete();
thresh.delete();
contours.delete();
hierarchy.delete();
drawing.delete();

五、视频处理

OpenCV.js 也可以处理视频流,包括摄像头实时视频。以下是一个简单的视频处理示例:

<!DOCTYPE html>
<html>
<head><title>OpenCV.js 视频处理示例</title><script async src="https://docs.opencv.org/4.5.5/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script><style>.container {display: flex;flex-direction: column;align-items: center;margin-top: 20px;}.video-container {display: flex;gap: 20px;margin-top: 20px;}video, canvas {border: 1px solid #ccc;width: 640px;height: 480px;}button {margin-top: 10px;padding: 10px 20px;font-size: 16px;}</style>
</head>
<body><div class="container"><h2>OpenCV.js 视频处理</h2><div id="status">正在加载 OpenCV.js...</div><div class="video-container"><div><p>原始视频</p><video id="inputVideo" autoplay muted playsinline></video></div><div><p>处理后视频</p><canvas id="outputCanvas"></canvas></div></div><button id="startButton">开始</button><button id="stopButton" disabled>停止</button></div><script>let video, outputCanvas, outputContext;let src, dst, gray;let processing = false;let requestId;function onOpenCvReady() {document.getElementById('status').innerHTML = 'OpenCV.js 已加载完成';video = document.getElementById('inputVideo');outputCanvas = document.getElementById('outputCanvas');outputContext = outputCanvas.getContext('2d');// 获取摄像头访问权限navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then(function(stream) {video.srcObject = stream;video.onloadedmetadata = function(e) {video.play();document.getElementById('startButton').disabled = false;};}).catch(function(err) {console.error('摄像头访问错误: ' + err);document.getElementById('status').innerHTML = '无法访问摄像头';});// 按钮事件处理document.getElementById('startButton').addEventListener('click', startProcessing);document.getElementById('stopButton').addEventListener('click', stopProcessing);}function startProcessing() {if (processing) return;// 初始化 OpenCV 矩阵src = new cv.Mat(video.height, video.width, cv.CV_8UC4);dst = new cv.Mat(video.height, video.width, cv.CV_8UC4);gray = new cv.Mat(video.height, video.width, cv.CV_8UC1);processing = true;document.getElementById('startButton').disabled = true;document.getElementById('stopButton').disabled = false;// 开始处理视频帧processVideo();}function stopProcessing() {if (!processing) return;processing = false;document.getElementById('startButton').disabled = false;document.getElementById('stopButton').disabled = true;// 释放资源if (src) src.delete();if (dst) dst.delete();if (gray) gray.delete();// 取消动画帧请求if (requestId) {cancelAnimationFrame(requestId);}}function processVideo() {if (!processing) return;try {// 从视频帧读取数据到 srccv.imread(video, src);// 示例处理:转换为灰度图cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY);cv.cvtColor(gray, dst, cv.COLOR_GRAY2RGBA);// 在处理后的帧上绘制文字let text = 'OpenCV.js 视频处理';let org = new cv.Point(10, 30);let fontFace = cv.FONT_HERSHEY_SIMPLEX;let fontScale = 1;let color = new cv.Scalar(255, 0, 0, 255);let thickness = 2;cv.putText(dst, text, org, fontFace, fontScale, color, thickness);// 将处理结果显示在 canvas 上cv.imshow(outputCanvas, dst);// 继续处理下一帧requestId = requestAnimationFrame(processVideo);} catch (err) {console.error('处理视频帧时出错:', err);stopProcessing();}}</script>
</body>
</html>

这个示例展示了如何捕获摄像头视频流并使用 OpenCV.js 进行实时处理。你可以根据需要修改 processVideo 函数中的处理逻辑,实现更复杂的视频处理效果。

六、实际应用案例

6.1 实时人脸检测

结合 OpenCV.js 和 Haar 级联分类器,可以实现浏览器中的实时人脸检测:

// 加载人脸检测模型
let faceCascade = new cv.CascadeClassifier();
let utils = new Utils('errorMessage');// 加载预训练的人脸检测模型
utils.createFileFromUrl('haarcascade_frontalface_default.xml','haarcascade_frontalface_default.xml',() => {faceCascade.load('haarcascade_frontalface_default.xml');document.getElementById('status').innerHTML = '人脸检测模型已加载';},() => {document.getElementById('status').innerHTML = '模型加载失败';});// 在视频处理循环中添加人脸检测逻辑
function processVideo() {if (!processing) return;try {// 从视频帧读取数据到 srccv.imread(video, src);// 转换为灰度图以提高检测速度cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY);// 检测人脸let faces = new cv.RectVector();let msize = new cv.Size(0, 0);// 检测参数:scaleFactor=1.1, minNeighbors=3, flags=0, minSize=msizefaceCascade.detectMultiScale(gray, faces, 1.1, 3, 0, msize);// 在原图上绘制检测到的人脸for (let i = 0; i < faces.size(); i++) {let face = faces.get(i);let point1 = new cv.Point(face.x, face.y);let point2 = new cv.Point(face.x + face.width, face.y + face.height);cv.rectangle(src, point1, point2, [255, 0, 0, 255], 2);}// 显示结果cv.imshow(outputCanvas, src);// 释放资源faces.delete();// 继续处理下一帧requestAnimationFrame(processVideo);} catch (err) {console.error('处理视频帧时出错:', err);stopProcessing();}
}

6.2 图像匹配

使用 OpenCV.js 进行图像匹配,可以在一个图像中查找另一个图像的位置:

// 加载源图像和模板图像
let src = cv.imread('sourceImage');
let templ = cv.imread('templateImage');// 创建结果矩阵
let result = new cv.Mat();
let result_cols = src.cols - templ.cols + 1;
let result_rows = src.rows - templ.rows + 1;
result.create(result_rows, result_cols, cv.CV_32FC1);// 应用模板匹配
let method = cv.TM_CCOEFF_NORMED;
cv.matchTemplate(src, templ, result, method);// 找到最佳匹配位置
let minMaxLoc = cv.minMaxLoc(result);
let matchLoc;
if (method === cv.TM_SQDIFF || method === cv.TM_SQDIFF_NORMED) {matchLoc = minMaxLoc.minLoc;
} else {matchLoc = minMaxLoc.maxLoc;
}// 在原图上绘制匹配区域
let point1 = new cv.Point(matchLoc.x, matchLoc.y);
let point2 = new cv.Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows);
cv.rectangle(src, point1, point2, [0, 255, 0, 255], 2);// 显示结果
cv.imshow('outputCanvas', src);// 释放资源
src.delete();
templ.delete();
result.delete();

七、性能优化与最佳实践

7.1 内存管理

在使用 OpenCV.js 时,正确的内存管理非常重要。每个 cv.Mat 对象都占用内存,不再使用时应调用 delete () 方法释放:

// 创建 Mat 对象
let mat = new cv.Mat();// 使用 mat 对象进行各种操作// 不再使用时释放内存
mat.delete();

对于在循环中创建的临时 Mat 对象,更要特别注意及时释放,避免内存泄漏。

7.2 异步处理

对于复杂的图像处理任务,考虑使用 Web Workers 进行异步处理,避免阻塞主线程:

// main.js
// 创建 Web Worker
const worker = new Worker('worker.js');// 发送图像数据到 worker
worker.postMessage({ imageData: imageData }, [imageData.data.buffer]);// 接收处理结果
worker.onmessage = function(e) {// 在 canvas 上显示处理结果outputContext.putImageData(e.data.processedImageData, 0, 0);
};// worker.js
self.onmessage = function(e) {// 加载 OpenCV.jsimportScripts('https://docs.opencv.org/4.5.5/opencv.js');self.cv['onRuntimeInitialized'] = function() {// 处理图像let src = cv.matFromImageData(e.data.imageData);let dst = new cv.Mat();// 执行图像处理操作cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);// 转换回 ImageDatalet imageData = new ImageData(new Uint8ClampedArray(dst.data),dst.cols,dst.rows);// 发送结果回主线程self.postMessage({ processedImageData: imageData }, [imageData.data.buffer]);// 释放资源src.delete();dst.delete();};
};

7.3 优化处理参数

对于计算密集型操作,如特征检测或视频处理,可以通过调整参数来平衡性能和精度:

// 调整 Canny 边缘检测参数以提高性能
let threshold1 = 100;
let threshold2 = 200;
let apertureSize = 3; // 可以增大以减少计算量
let L2gradient = false; // 使用更简单的梯度计算方法
cv.Canny(src, dst, threshold1, threshold2, apertureSize, L2gradient);

八、局限性与挑战

尽管 OpenCV.js 提供了强大的功能,但在前端使用仍有一些局限性:

  1. 性能限制:WebAssembly 虽然比纯 JavaScript 快得多,但对于复杂的计算机视觉任务,仍然可能比原生实现慢。

  2. 内存管理:与原生 OpenCV 相比,JavaScript 环境中的内存管理更加复杂,需要开发者手动释放资源。

  3. 模型加载:预训练模型(如 Haar 级联分类器)体积较大,加载时间较长。

  4. 浏览器兼容性:不同浏览器对 WebAssembly 和 OpenCV.js 的支持程度可能不同。

  5. 长时间运行任务:长时间运行的计算密集型任务可能导致页面无响应,需要使用 Web Workers 进行优化。

九、总结与未来展望

OpenCV.js 为前端开发者打开了计算机视觉的大门,使我们能够在浏览器中实现图像和视频处理功能,而无需依赖后端服务。从简单的图像处理到复杂的实时视频分析,OpenCV.js 提供了丰富的功能和工具。

随着 WebAssembly 技术的不断发展和浏览器性能的提升,我们可以期待 OpenCV.js 在未来会有更好的表现和更广泛的应用场景。例如,增强现实 (AR)、实时视频编辑、智能监控等领域都可能受益于 OpenCV.js 的发展。

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

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

相关文章

开发在线商店:基于Vue2+ElementUI的电商平台前端实践

Hi&#xff0c;我是布兰妮甜 &#xff01;在当今数字化时代&#xff0c;电子商务已成为商业领域的重要组成部分。开发一个功能完善、用户友好的在线商店应用对于企业拓展市场至关重要。本文将详细介绍如何使用Vue2框架配合ElementUI组件库开发一个完整的在线商店应用。 文章目录…

vue3 随手笔记9--组件通信方式9/2--自定义事件

一、什么是自定义事件&#xff1f; 自定义事件是 Vue 组件间通信的一种机制。子组件通过 this.$emit(事件名, 数据) 触发一个事件。父组件监听这个事件并执行相应的逻辑。 二、基本使用 准备工作 demo 继续使用笔记8中的 链接为demo 在views文件夹下 创建新的文件夹为cust…

深入理解Reactor调试模式:Hooks.onOperatorDebug() vs ReactorDebugAgent.init()

在现代Java开发中&#xff0c;调试Reactor流是确保应用程序性能和稳定性的关键步骤。Reactor调试模式提供了多种初始化方法&#xff0c;其中最常用的两种是Hooks.onOperatorDebug()和ReactorDebugAgent.init()。本文将深入探讨这两种方法的区别&#xff0c;帮助开发者选择最适合…

QT6 源(151)模型视图架构里的表格窗体视图 QTableWidget 篇一:先学习俩属性以及 public 权限的公共成员函数,

&#xff08;1&#xff09;本篇的内容因为是子类&#xff0c;内容较视图基类简单了一些。又因为时间紧迫&#xff0c;不再详细举例了。详细的测试可以满足好奇心&#xff0c;也可以增强写代码的自信心。奈何时间不够。不完美&#xff0c;就不完美了。以后有机会&#xff0c;再补…

ffmpeg 下载、安装、配置、基本语法、避坑指南(覆盖 Windows、macOS、Linux 平台)

ffmpeg 下载、安装、配置、基本语法、避坑指南&#xff08;覆盖 Windows、macOS、Linux 平台&#xff09; 本文是一篇面向初学者的超详细 FFmpeg 教程&#xff0c;包括 FFmpeg 下载、安装、配置、基本语法 与 避坑指南。覆盖 Windows、macOS、Linux 平台的安装方式与 环境变量…

Kotlin 安装使用教程

一、Kotlin 简介 Kotlin 是 JetBrains 开发的一种现代、静态类型的编程语言&#xff0c;完全兼容 Java&#xff0c;主要应用于 Android 开发、后端服务开发、前端 Web 开发&#xff08;Kotlin/JS&#xff09;和多平台开发&#xff08;Kotlin Multiplatform&#xff09;。 二、…

day08-Elasticsearch

黑马商城作为一个电商项目&#xff0c;商品的搜索肯定是访问频率最高的页面之一。目前搜索功能是基于数据库的模糊搜索来实现的&#xff0c;存在很多问题。 首先&#xff0c;查询效率较低。 由于数据库模糊查询不走索引&#xff0c;在数据量较大的时候&#xff0c;查询性能很…

transformers 笔记:自定义模型(配置+模型+注册为AutoCLass+本地保存加载)

Transformers 模型设计上是可定制的。每个模型的代码都包含在 Transformers 仓库的 model 子文件夹中&#xff08;transformers/src/transformers/models at main huggingface/transformers&#xff09;&#xff0c;每个模型文件夹通常包含&#xff1a; modeling.py&#xff1…

Java工具类,对象List提取某个属性为List,对象List转为对象Map其中某个属性作为Key值

Java工具类package org.common;import lombok.extern.slf4j.Slf4j;import java.util.*; import java.util.stream.Collectors;Slf4j public final class CollectorHelper {/*** param element* param propertyName* param <E>* return*/public static <E> List toL…

ATE FT ChangeKit学习总结-20250630

目录 一、基本概念 二、主要特点 三、BOM LIST Shuttle Hot Plate Dock Plate Contactor 四、设计要点 五、参考文献与链接 一、基本概念 Change Kit在半导体封装测试领域中是一个重要的组件,它作为Handler(自动化分类机)的配套治具,在芯片测试过程中发挥着关键作…

【网络协议安全】任务14:路由器DHCP_AAA_TELNET配置

本文档将详细介绍在华为 eNSP 仿真环境中&#xff0c;实现路由器 DHCP 服务器功能、AAA 认证以及 TELNET 远程登录配置的完整步骤&#xff0c;适用于华为 VRP 系统路由器。 一、配置目标 路由器作为 DHCP 服务器&#xff0c;为局域网内的设备自动分配 IP 地址、子网掩码、网关…

深度探索:现代翻译技术的核心算法与实践(第一篇)

引言:翻译技术的演进之路 从早期的基于规则的机器翻译(RBMT)到统计机器翻译(SMT),再到如今主导行业的神经机器翻译(NMT),翻译技术已经走过了漫长的发展道路。现代翻译系统不仅能够处理简单的句子,还能理解上下文、识别领域术语,甚至捕捉微妙的文化差异。 本系列文章将带…

玩转Docker | 使用Docker部署NotepadMX笔记应用程序

玩转Docker | 使用Docker部署NotepadMX笔记应用程序 前言一、NotepadMX介绍工具简介主要特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署NotepadMX服务下载NotepadMX镜像编辑部署文件创建容器检查容器状态检查服务端口安全设置四、访问NotepadMX服务访…

Web前端:not(否定伪类选择器)

not&#xff08;否定伪类选择器&#xff09;CSS中的 :not() 选择器是⼀个否定伪类选择器&#xff0c;它⽤于选择不符合给定选择器的元素。这是⼀种排除特定元素的⽅法&#xff0c;可以⽤来简 化复杂的选择器&#xff0c;提⾼ CSS 规则的灵活性和精确性。:not() 选择器的基本语法…

【BTC】比特币网络

目录 一、比特币网络架构 1.1 节点加入与离开 二、消息传播方式 三、交易处理机制 四、网络传播问题 五、实际应用问题及解决 本章节讲比特币网络的工作原理&#xff0c;讲解新创建的区块是如何在比特币网络中传播的。 一、比特币网络架构 比特币工作在应用层&#xff…

Clickhouse 的历史发展过程

20.5.3 开始支持多线程20.6.3 支持explainmysql 20.8 实时同步mysql&#x1f4cc; ‌一、早期版本阶段&#xff08;1.1.x系列&#xff09;‌‌版本范围‌&#xff1a;1.1.54245&#xff08;2017-07&#xff09;→ 1.1.54394&#xff08;2018-07&#xff09;‌核心特性‌&#x…

玩转n8n工作流教程(一):Windows系统本地部署n8n自动化工作流(n8n中文汉化)

在Windows系统下使用 Docker 本地部署N8N中文版的具体操作&#xff0c;进行了详尽阐述&#xff0c;玩转n8n工作流教程系列内容旨在手把手助力从0开始一步一步深入学习n8n工作流。想研究n8n工作流自动化的小伙伴们可以加个关注一起学起来。后续也会持续分享n8n自动化工作流各种玩…

mini-program01の系统认识微信小程序开发

一、官方下载并安装 1、下载&#xff08;I选了稳定版&#xff09; https://developers.weixin.qq.com/miniprogram/dev/devtools/download.htmlhttps://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 2、安装&#xff08;A FEW MOMENT LATER&#xff09;…

如何将 Java 项目打包为可执行 JAR 文件

如何将 Java 项目打包为可执行 JAR 文件我将详细介绍将 Java 项目打包为可执行 JAR 文件的完整流程&#xff0c;包括使用 IDE 和命令行两种方法。方法一&#xff1a;使用 IntelliJ IDEA 打包步骤 1&#xff1a;配置项目结构打开项目点击 File > Project Structure在 Project…

【Starrocks 异常解决】-- mysql flink sync to starrocks row error

1、异常信息 flink 1.20 starrocks 3.3.0 mysql 8.0 errorLog: Error: Target column count: 35 doesnt match source value column count: 28. Column separator: \t, Row delimiter: \n. Row: 2025-05-22 6 23400055 214 dssd 1 1 1928 mm2er 360 20000.00000000 1…