Hi
What is the best way to get an image from another by giving a rectangle ?
Thanks a lot
Hey,
You can do the following to get a subregion of an image:
const roi = new cv.Rect(x, y, width, height)
const region = mat.getRegion(roi)
getRegion will return a Mat pointing to the subregion, meaning if you change the data of the "region" Mat, the changes will reflect on the original Mat "mat".
If you don't want to mutate the original Mat you can copy the region into a new Mat:
const regionCopy = region.copy()
So clear ! I thank you very much :)
Hi @justadudewhohacks ,
I tried the above suggestion as:
const img = cv.imread('./face.jpg');
console.log('Image loaded...');
//image dimensions
const rows = img.rows;
const cols = img.cols;
console.log('Image size: (' + rows + ', ' + cols + ')')
//define the coordinates for face region
//this should be obtained from user input
const x1 = 420;
const y1 = 458;
const w = 504 - x1 + 1;
const h = 544 - y1 + 1;
//crop the region of interest
let roi = img.getRegion(new cv.Rect(x1, y1, w, h));
console.log('Cropped the roi...')
console.log('Cropped image size: (' + roi.rows + ', ' + roi.cols + ')');
//blur the cropped roi
roi = roi.gaussianBlur(new cv.Size(21, 21), 2);
console.log('Blurred the roi...');
console.log('Blurred image size: (' + roi.rows + ', ' + roi.cols + ')');
cv.imwriteAsync('./final_result.jpg', img, (err) => {
})
But the saved image is not modified at all! Any suggestions where I am going wrong? New to JS realm. Thanks.
Okay, so the way I did it is:
//crop the region of interest
const roi = img.getRegion(new cv.Rect(x1, y1, w, h));
console.log('Cropped the roi...')
console.log('Cropped image size: (' + roi.rows + ', ' + roi.cols + ')');
//blur the cropped roi
const blurredRoi = roi.gaussianBlur(new cv.Size(21, 21), 2);
console.log('Blurred the roi...');
console.log('Blurred image size: (' + blurredRoi.rows + ', ' + blurredRoi.cols + ')');
//this is a much better and faster solution as compared to looping through the
//entire big image
for (col = 0; col < blurredRoi.cols; col++) {
for (row = 0; row < blurredRoi.rows; row++) {
[b, g, r] = blurredRoi.atRaw(row, col);
roi.set(row, col, [b, g, r]);
}
}
Is there any better (mostly faster runtime) way to do this?
@bkanaki
I've found using .fill() is somehow faster over time than using .set()
Came across this issue while searching for speedy region _setting_ so I'll leave this (messy) implementation here for future googlers. Function to overlay one mat onto another:
/**
* Places a source matrix onto a dest matrix.
* Note: This replaces pixels entirely, it doesn't merge transparency.
* Assumes 4 channels or fewer
* @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
// only write pixels inside dest mat boundaries
if( !( (dest_x < 0 || dest_x > dest_mat.cols-1) || (dest_y < 0 || dest_y > dest_mat.rows-1) ) ){
// get buffer index relative to coordinates
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;
// for some reason, using set is faster for first time function is called, but then slower for each preceeding one???
// let this_pixel = [];
// for(let ii=0, l=channel_count; ii<=l; ii++){
// this_pixel[ii] = source_uint8[i+ii]
// }
// dest_uint8.set(this_pixel, dest_buffer_i);
// fill into buffer
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);
}
Most helpful comment
So clear ! I thank you very much :)