Question

I have an image (cv::Mat) and a ROI that can be seen as a mask. I want to show the original image with the ROI blended over it.

My mask is smaller than my origiginal image: each element represents a block in the image. Suppose my mask is this (note that my mask is NOT a rectangle)

0  0  1
1  1  1
0  0  0

then I would like to have the parts where (mask == 1) untouched and the rest blended with a color. This is the code I have

cv::Mat blocks = image.clone;
uint npixcol = 32;
uint npixrow = 32;
for (uint ri = 0; ri < 480; ++ri)
    for (uint ci = 0; ci < 640; ++ci)
        {
        if (mask[ri * 640 + ci])
            cv::rectangle(blocks, cv::Rect(ci * npixcol, ri * npixrow, npixcol, npixrow), cv::Scalar(0, 0, 0), CV_FILLED, 8, 0);
        }
cv::addWeighted(image, 0.5, blocks, 0.5, 0, image, -1);

How can I do this without the extra "clone" command since that is not very performant...

to make it more clear; this is an example of what I want (the color doesn't really matter)!enter image description here

Was it helpful?

Solution

Is your mask of constant colour? Assuming mask is the same dimension as the image(you can easily scale it) :-

//Manually instead of addWeighted()
for (uint ri = 0; ri < 480; ++ri)
    for (uint ci = 0; ci < 640; ++ci)
        {
        if (mask[ri * 640 + ci])
        {
            image.at<uchar>(ri,ci) [0] = image.at<uchar>(ri,ci) [0] * weight_blue;
            image.at<uchar>(ri,ci) [1] = image.at<uchar>(ri,ci) [1] * weight_green;
            image.at<uchar>(ri,ci) [2] = image.at<uchar>(ri,ci) [2] * weight_red;
        }

        }    

OTHER TIPS

Based on your comment, if you can make a mask with the same dimensions as the original image, you could directly modify original image pixel values using iterators. Here is a standalone example:

#include <cstdlib>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>


int
main(int argc, char *argv[])
{
  cv::Mat image = cv::imread(argv[1]);
  cv::Mat mask = cv::Mat::zeros(image.size(), CV_8U);
  // let's put some 1 in my test mask.
  cv::Mat roi = mask(cv::Rect(0,0,mask.cols/2, mask.rows/2));
  roi = 1;

  cv::Vec3b blue(255,0,0); // (B,G,R)
  float alpha = 0.5;
  // Let's have fun with iterators
  cv::MatConstIterator_<unsigned char> maskIter = mask.begin<unsigned char>();
  const cv::MatConstIterator_<unsigned char> maskIterEnd = mask.end<unsigned char>();
  cv::MatIterator_<cv::Vec3b> imageIter = image.begin<cv::Vec3b>();
  for (; maskIter != maskIterEnd; ++maskIter, ++imageIter) {
    if (*maskIter) {// mask == 1
      *imageIter = (1-alpha)*(*imageIter) + alpha*blue; // same as addWeighted 
    }
  }

  cv::namedWindow("image", 0);
  cv::imshow("image", image);
  cv::waitKey(0);
  return EXIT_SUCCESS;
}

Basically you want to have a check if your are inside the roi. Then it should return a pointer to your original image. If your are not inside you want to have some kind of colour.

Your could do that with your own wrapper for Mat.

MyMat::at(int x, int y){
    if(inRoi(x,y)){
        return original.at(x,y);
    else
        return color(0,0,0);
}

I don't think you can point a subImage of an image onto another image. (That means that i don't think you can redirect the pixel in your blue image onto your original image)

Mat blueImage;
Rect roi;
Mat roiInImage = blueImage(roi);
roiInImage.redirect = originalImage(roi); //don't think something like this is possible
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top