const Jimp = require('jimp');
const cv = require('opencv4nodejs');
function pil_to_cv2(img) {
const buffer = Buffer.from(img.bitmap.data);
const imgMat = cv.imdecode(buffer);
return imgMat;
}
function cv2_open(img, flag = null) {
if (Buffer.isBuffer(img)) {
img = cv.imdecode(img);
} else if (typeof img === 'string') {
img = cv.imread(img);
} else if (img instanceof cv.Mat) {
img = img;
} else if (img instanceof Jimp) {
img = pil_to_cv2(img);
} else {
throw new Error(`无法解析的图片类型: ${typeof img}`);
}
if (flag !== null) {
img = img.cvtColor(flag);
}
return img;
}
async function get_distance(bg, tp, save_path = null) {
// 读取图片并转为JPEG格式
const bg_jimp = await Jimp.read(bg);
const tp_jimp = await Jimp.read(tp);
const bg_buffer = await bg_jimp.getBufferAsync(Jimp.MIME_JPEG);
const tp_buffer = await tp_jimp.getBufferAsync(Jimp.MIME_JPEG);
// 读取图片
const bg_gray = cv2_open(bg_buffer, cv.COLOR_BGR2GRAY);
const tp_gray = cv2_open(tp_buffer, cv.COLOR_BGR2GRAY);
// 边缘检测
const tp_grayCanny = tp_gray.canny(255, 255);
const bg_grayCanny = bg_gray.canny(255, 255);
// 目标匹配
const result = bg_gray.matchTemplate(tp_gray, cv.TM_CCOEFF_NORMED);
// 解析匹配结果
const { minValue, maxValue, minLoc, maxLoc } = result.minMaxLoc();
const distance = maxLoc.x;
if (save_path || im_show) {
// 需要绘制的方框高度和宽度
const [tp_height, tp_width] = tp_gray.sizes;
// 矩形左上角点位置
const x = maxLoc.x;
const y = maxLoc.y;
// 矩形右下角点位置
const _x = x + tp_width;
const _y = y + tp_height;
// 绘制矩形
const bg_img = cv2_open(bg_buffer);
bg_img.drawRectangle(new cv.Rect(x, y, tp_width, tp_height), new cv.Vec3(0, 0, 255), 2);
// 保存缺口识别结果到背景图
if (save_path) {
const savePath = save_path.replace(/\.(\w+)$/, `.${distance}.$1`);
await new Jimp({
width: bg_img.cols,
height: bg_img.rows,
data: bg_img.getData()
}).writeAsync(savePath);
}
// 显示缺口识别结果
// if (im_show) {
// imshow(bg_img);
// }
}
return distance;
}
(async () => {
const d = await get_distance(
'./bg.jpg',
'./tp.png',
'./bg.jpg'
);
console.log(d);
})();
这段代码识别出来的缺口位置为什么偏差这么多~而且图片颜色也改变掉了
根据你的描述,效果图和代码来看,可能出现了以下问题:
颜色被改变:在读取 Jimp 图片后,进行了 JPEG 格式编码,在编码过程中可能会对颜色产生影响。可以尝试直接读取 PNG 格式图片,或者将 Jimp.MIME_JPEG
改为 Jimp.MIME_PNG
。
缺口位置偏差太多:在目标匹配时使用的是 cv.TM_CCOEFF_NORMED
方法,它只能粗略地识别缺口的大致位置。如果需要更准确地识别缺口位置,可以尝试使用模板匹配方法中的其他算法,比如 cv.TM_CCORR
、cv.TM_SQDIFF
等,并调整阈值等参数以获得更好的匹配结果。
关于修改方案,你可以参考以下示例代码:
const Jimp = require('jimp');
const cv = require('opencv4nodejs');
/**
* 将 Jimp 图片对象转换成 opencv 的 Mat 对象
* @param {*} img Jimp 图片对象
*/
function pil_to_cv2(img) {
const buffer = Buffer.from(img.bitmap.data);
const imgMat = cv.imdecode(buffer);
return imgMat;
}
/**
* 打开一个图片文件并返回对应的 Mat 对象,支持 PNG 和 JPG 两种格式
* @param {*} img 待打开的图片文件路径或者数据流(Buffer)
* @param {*} flag Mat 对象颜色空间转换标识
*/
function cv2_open(img, flag = null) {
if (Buffer.isBuffer(img)) { // 如果 img 是 Buffer
img = cv.imdecode(img); // 直接解码为 Mat 对象
} else if (typeof img === 'string') { // 如果是图片路径字符串
img = cv.imread(img); // 读取图片得到 Mat 对象
} else if (img instanceof cv.Mat) { // 如果已经是 Mat 对象了
img = img;
} else if (img instanceof Jimp) { // 如果是 Jimp 图片对象
img = pil_to_cv2(img);
} else {
throw new Error(`无法解析的图片类型: ${typeof img}`);
}
if (flag !== null) { // 判断是否需要进行颜色空间变换
img = img.cvtColor(flag);
}
return img;
}
/**
* 检测缺口位置并进行相关操作
* @param {*} bg 背景图路径或数据流
* @param {*} tp 缺口模板图路径或数据流
* @param {*} save_path 结果保存路径
* @returns 返回检测出的缺口位置
*/
async function get_distance(bg, tp, save_path = null) {
// 读取图片并转为 PNG 格式
const bg_jimp = await Jimp.read(bg);
const tp_jimp = await Jimp.read(tp);
const bg_buffer = await bg_jimp.getBufferAsync(Jimp.MIME_PNG); // 使用 PNG 格式,避免颜色被修改
const tp_buffer = await tp_jimp.getBufferAsync(Jimp.MIME_PNG);
// 将图片转换成 Mat 对象
const bg_gray = cv2_open(bg_buffer, cv.COLOR_BGR2GRAY);
const tp_gray = cv2_open(tp_buffer, cv.COLOR_BGR2GRAY);
// 边缘检测
const tp_grayCanny = tp_gray.canny(100, 200); // 调整宽松的边缘检测阈值
const bg_grayCanny = bg_gray.canny(100, 200);
// 目标匹配
const result = bg_gray.matchTemplate(tp_gray, cv.TM_COEFF_NORMED);
// 解析匹配结果
const { minValue, maxValue, minLoc, maxLoc } = result.minMaxLoc();
const distance = maxLoc.x;
if (save_path) {
// 需要绘制的方框高度和宽度
const [tp_height, tp_width] = tp_gray.sizes;
// 矩形左上角点位置
const x = maxLoc.x;
const y = maxLoc.y;
// 矩形右下角点位置
const _x = x + tp_width;
const _y = y + tp_height;
// 绘制矩形
const bg_img = cv2_open(bg_buffer);
bg_img.drawRectangle(new cv.Rect(x, y, tp_width, tp_height), new cv.Vec3(0, 0, 255), 2);
// 保存结果到文件
const savePath = save_path.replace(/\.(\w+)$/, `.${distance}.$1`);
cv.imwrite(savePath, bg_img);
}
return distance;
}
// 测试用例
(async () => {
const d = await get_distance(
'./bg.jpg',
'./tp.png',
'./result.png'
);
console.log(d);
})();
在这个示例中,我们对代码进行了以下修改: