Opencv4nodejs: How to set a region (roi) ?

Created on 10 Nov 2017  路  5Comments  路  Source: justadudewhohacks/opencv4nodejs

Hi
What is the best way to get an image from another by giving a rectangle ?
Thanks a lot

Most helpful comment

So clear ! I thank you very much :)

All 5 comments

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);
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

bgsuello picture bgsuello  路  6Comments

karlbernard2 picture karlbernard2  路  4Comments

SatoshiKawabata picture SatoshiKawabata  路  5Comments

djipco picture djipco  路  7Comments

seanquijote picture seanquijote  路  6Comments