如何使用 opencv4nodejs 设置矩阵区域
How to set a matrix region with opencv4nodejs
问题:
在opencv4nodejs中设置矩阵区域最快的方法是什么?
问题:
我正在将源图像叠加到尺寸在几千以下的更大的目标图像中。
在 Python 中,我将 get/set 大小匹配的区域:
destination[y:y+h, x:x+w] = source[:,:]
但我不确定如何在 Javascript 中执行此操作。
我尝试了几种方法,但即使是最快的方法也慢得令人望而却步。
例如我有:
- source_mat cols:2929, rows:2049
- dest_mat cols:3000, rows:6000
- offset_x:150, offset_y:150
将矩阵转换为数组,循环行和列,设置目标像素大约需要 12 秒
let debug_overlay_start = new Date().valueOf();
let source_pixels = source_mat.getDataAsArray();
for (let row_index = 0, l_1 = source_pixels.length; row_index < l_1; row_index++) {
if(row_index + offset_y < 0) continue;
if(row_index + offset_y >= dest_mat.rows) continue;
let this_col = source_pixels[row_index];
for (let col_index = 0, l_2 = this_col.length; col_index < l_2; col_index++) {
if(col_index + offset_x < 0) continue;
if(col_index + offset_x >= dest_mat.cols) continue;
dest_mat.set(row_index + offset_y, col_index + offset_x, source_pixels[row_index][col_index]);
}
}
let debug_overlay_end = new Date().valueOf();
console.log(`overlay method took ${((debug_overlay_end - debug_overlay_start)/1000).toFixed(2)}`); // overlay method took 11.63
return dest_mat;
将两者转换为数组、遍历行、拼接 cols 需要大量 82 秒:
let debug_overlay_end = new Date().valueOf();
let source_pixels = source_mat.getDataAsArray();
let new_dest_mat = dest_mat.getDataAsArray();
for (let row_index = 0, l_1 = source_pixels.length; row_index < l_1; row_index++) {
if(row_index + offset_y < 0) continue;
if(row_index + offset_y >= dest_mat.rows) continue;
let this_col = source_pixels[row_index]; // entire column of source pixels
new_dest_mat[row_index + offset_y].splice(offset_x, this_col.length, ...this_col);
}
let debug_overlay_end = new Date().valueOf();
console.log(`overlay method took ${((debug_overlay_end - debug_overlay_start)/1000).toFixed(2)}`); // 82 seconds
return new cv.Mat(new_dest_mat, dest_mat.type);
替换区域根本不起作用,抛出生命周期错误且没有额外的日志记录:
let debug_overlay_end = new Date().valueOf();
let area_to_replace = dest_mat.getRegion(new cv.Rect(x, y, source_mat.cols, source_mat.rows));
area_to_replace = source_mat.getRegion(new cv.Rect(0, 0, source_mat.cols, source_mat.rows)); // lifecycle error
console.log(`overlay method took ${((debug_overlay_end - debug_overlay_start)/1000).toFixed(2)}`);
return dest_mat;
到目前为止,使用 setAt()
和 atRaw()
最快为 8 秒:
let debug_overlay_start = new Date().valueOf();
for(let row_index = 0, row_length = source_mat.rows; row_index < row_length; row_index++){
if(row_index + offset_y < 0) continue;
if(row_index + offset_y >= dest_mat.rows) continue;
for(let col_index = 0, col_length = source_mat.cols; col_index < col_length; col_index++){
if(col_index + offset_x < 0) continue;
if(col_index + offset_x >= dest_mat.cols) continue;
dest_mat.set(row_index + offset_y, col_index + offset_x, source_mat.atRaw(row_index, col_index));
}
}
let debug_overlay_end = new Date().valueOf();
console.log(`overlay method took ${((debug_overlay_end - debug_overlay_start)/1000).toFixed(2)}`); // 8.09
return dest_mat;
我查看了文档,我很惊讶地发现这不是一个常见的用例。 node/electron 环境是否正在减慢原本快速的操作?
使用缓冲区数组设法将时间缩短到 1.1 秒。不确定还有什么方法可以加快速度。
/**
* Places a source matrix onto a dest matrix.
* Note: This replaces pixels entirely, it doesn't merge transparency
* @param {cv.Mat} source_mat matrix being copied
* @param {cv.Mat} dest_mat matrix being pasted into
* @param {number} x horizontal offset of source image
* @param {number} y vertical offset of source image
*/
function overlayOnto(source_mat, dest_mat, x, y){
if(source_mat.channels != dest_mat.channels) throw new Error('src and dst channel counts must match');
let source_uint8 = new Uint8Array( source_mat.getData() ); // WARNING 4 CHANNELS
let dest_uint8 = new Uint8Array( dest_mat.getData() ); // WARNING 4 CHANNELS
let dest_width = dest_mat.cols;
let x_count = 0; // set counters
let y_count = 0; // set counters
let channel_count = source_mat.channels;
for (let i = 0; i < source_uint8.length; i += channel_count) { // WARNING 4 CHANNELS
let dest_x = x_count + x; // add offset
let dest_y = y_count + y; // add offset
if( !( (dest_x < 0 || dest_x > dest_mat.cols-1) || (dest_y < 0 || dest_y > dest_mat.rows-1) ) ){ // pixel does not fall outside of dest mat
// write into buffer array
let dest_i = (dest_x + dest_width * dest_y); // (x + w * h) to get x/y coord in single-dimension array
let dest_buffer_i = dest_i * channel_count;
if(channel_count >= 1) dest_uint8.fill(source_uint8[i+0], dest_buffer_i+0 , dest_buffer_i+0+1);
if(channel_count >= 2) dest_uint8.fill(source_uint8[i+1], dest_buffer_i+1 , dest_buffer_i+1+1);
if(channel_count >= 3) dest_uint8.fill(source_uint8[i+2], dest_buffer_i+2 , dest_buffer_i+2+1);
if(channel_count >= 4) dest_uint8.fill(source_uint8[i+3], dest_buffer_i+3 , dest_buffer_i+3+1);
}
x_count++; // increase col
x_count = x_count % source_mat.cols; // end of col? move to start
if(x_count == 0) y_count++; // started new col? increase row
}
return new cv.Mat(dest_uint8, dest_mat.rows, dest_mat.cols, dest_mat.type);
}
问题:
在opencv4nodejs中设置矩阵区域最快的方法是什么?
问题:
我正在将源图像叠加到尺寸在几千以下的更大的目标图像中。
在 Python 中,我将 get/set 大小匹配的区域:
destination[y:y+h, x:x+w] = source[:,:]
但我不确定如何在 Javascript 中执行此操作。
我尝试了几种方法,但即使是最快的方法也慢得令人望而却步。
例如我有:
- source_mat cols:2929, rows:2049
- dest_mat cols:3000, rows:6000
- offset_x:150, offset_y:150
将矩阵转换为数组,循环行和列,设置目标像素大约需要 12 秒
let debug_overlay_start = new Date().valueOf();
let source_pixels = source_mat.getDataAsArray();
for (let row_index = 0, l_1 = source_pixels.length; row_index < l_1; row_index++) {
if(row_index + offset_y < 0) continue;
if(row_index + offset_y >= dest_mat.rows) continue;
let this_col = source_pixels[row_index];
for (let col_index = 0, l_2 = this_col.length; col_index < l_2; col_index++) {
if(col_index + offset_x < 0) continue;
if(col_index + offset_x >= dest_mat.cols) continue;
dest_mat.set(row_index + offset_y, col_index + offset_x, source_pixels[row_index][col_index]);
}
}
let debug_overlay_end = new Date().valueOf();
console.log(`overlay method took ${((debug_overlay_end - debug_overlay_start)/1000).toFixed(2)}`); // overlay method took 11.63
return dest_mat;
将两者转换为数组、遍历行、拼接 cols 需要大量 82 秒:
let debug_overlay_end = new Date().valueOf();
let source_pixels = source_mat.getDataAsArray();
let new_dest_mat = dest_mat.getDataAsArray();
for (let row_index = 0, l_1 = source_pixels.length; row_index < l_1; row_index++) {
if(row_index + offset_y < 0) continue;
if(row_index + offset_y >= dest_mat.rows) continue;
let this_col = source_pixels[row_index]; // entire column of source pixels
new_dest_mat[row_index + offset_y].splice(offset_x, this_col.length, ...this_col);
}
let debug_overlay_end = new Date().valueOf();
console.log(`overlay method took ${((debug_overlay_end - debug_overlay_start)/1000).toFixed(2)}`); // 82 seconds
return new cv.Mat(new_dest_mat, dest_mat.type);
替换区域根本不起作用,抛出生命周期错误且没有额外的日志记录:
let debug_overlay_end = new Date().valueOf();
let area_to_replace = dest_mat.getRegion(new cv.Rect(x, y, source_mat.cols, source_mat.rows));
area_to_replace = source_mat.getRegion(new cv.Rect(0, 0, source_mat.cols, source_mat.rows)); // lifecycle error
console.log(`overlay method took ${((debug_overlay_end - debug_overlay_start)/1000).toFixed(2)}`);
return dest_mat;
到目前为止,使用 setAt()
和 atRaw()
最快为 8 秒:
let debug_overlay_start = new Date().valueOf();
for(let row_index = 0, row_length = source_mat.rows; row_index < row_length; row_index++){
if(row_index + offset_y < 0) continue;
if(row_index + offset_y >= dest_mat.rows) continue;
for(let col_index = 0, col_length = source_mat.cols; col_index < col_length; col_index++){
if(col_index + offset_x < 0) continue;
if(col_index + offset_x >= dest_mat.cols) continue;
dest_mat.set(row_index + offset_y, col_index + offset_x, source_mat.atRaw(row_index, col_index));
}
}
let debug_overlay_end = new Date().valueOf();
console.log(`overlay method took ${((debug_overlay_end - debug_overlay_start)/1000).toFixed(2)}`); // 8.09
return dest_mat;
我查看了文档,我很惊讶地发现这不是一个常见的用例。 node/electron 环境是否正在减慢原本快速的操作?
使用缓冲区数组设法将时间缩短到 1.1 秒。不确定还有什么方法可以加快速度。
/**
* Places a source matrix onto a dest matrix.
* Note: This replaces pixels entirely, it doesn't merge transparency
* @param {cv.Mat} source_mat matrix being copied
* @param {cv.Mat} dest_mat matrix being pasted into
* @param {number} x horizontal offset of source image
* @param {number} y vertical offset of source image
*/
function overlayOnto(source_mat, dest_mat, x, y){
if(source_mat.channels != dest_mat.channels) throw new Error('src and dst channel counts must match');
let source_uint8 = new Uint8Array( source_mat.getData() ); // WARNING 4 CHANNELS
let dest_uint8 = new Uint8Array( dest_mat.getData() ); // WARNING 4 CHANNELS
let dest_width = dest_mat.cols;
let x_count = 0; // set counters
let y_count = 0; // set counters
let channel_count = source_mat.channels;
for (let i = 0; i < source_uint8.length; i += channel_count) { // WARNING 4 CHANNELS
let dest_x = x_count + x; // add offset
let dest_y = y_count + y; // add offset
if( !( (dest_x < 0 || dest_x > dest_mat.cols-1) || (dest_y < 0 || dest_y > dest_mat.rows-1) ) ){ // pixel does not fall outside of dest mat
// write into buffer array
let dest_i = (dest_x + dest_width * dest_y); // (x + w * h) to get x/y coord in single-dimension array
let dest_buffer_i = dest_i * channel_count;
if(channel_count >= 1) dest_uint8.fill(source_uint8[i+0], dest_buffer_i+0 , dest_buffer_i+0+1);
if(channel_count >= 2) dest_uint8.fill(source_uint8[i+1], dest_buffer_i+1 , dest_buffer_i+1+1);
if(channel_count >= 3) dest_uint8.fill(source_uint8[i+2], dest_buffer_i+2 , dest_buffer_i+2+1);
if(channel_count >= 4) dest_uint8.fill(source_uint8[i+3], dest_buffer_i+3 , dest_buffer_i+3+1);
}
x_count++; // increase col
x_count = x_count % source_mat.cols; // end of col? move to start
if(x_count == 0) y_count++; // started new col? increase row
}
return new cv.Mat(dest_uint8, dest_mat.rows, dest_mat.cols, dest_mat.type);
}