Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extract specific area of image

I want to count only the vignette area of this image , like i have the image here

How I can count or iterate/pass through only in the area where vignette is applied and leave the other area ? I only want to apply the algorithm on the area where vignette is applied , I tried it like give scalar to red color and extract area where it found the red color but its not working like not giving the results because color become lighter when coming towards centre of the image.

The vignette darkens the corners and edges by multiplying the image intensity with the following mask: enter image description here

This is the original image here

I want to blending overlay the **only vignette part of** vignette image with the original image

Here is my code for blending overlay

void blending_overlay(Mat& img1 , Mat& img2 , Mat& out)
{
Mat result(img1.size(), CV_32FC3);
for(int i = 0; i < img1.size().height; ++i){
    for(int j = 0; j < img1.size().width; ++j){

        for (int c=0 ; c<img1.channels();c++){
            float target = (float)img1.at<uchar>(i, 3*j+c)/255. ;
            float blend =  (float)img2.at<uchar>(i, 3*j+c)/255. ;

            if(target > 0.5){
                result.at<float>(i, 3*j+c) = ((1 - (1-2*(target-0.5)) * (1-blend)));

                }
            else{
                result.at<float>(i, 3*j+c) = ((2*target) * blend);
                }
        }
    }
}
result.convertTo(out,CV_8UC3,255);
}

int main( int argc, char** argv )
{
    Mat Img1=imread("D:\\Vig.png",-1); // the vignete in the question
    Mat Img2=imread("D:\\i.png"); // the iamge in the question
    cv::resize(Img1,Img1,Img2.size());
    Img1.convertTo(Img1,CV_32FC4,1.0/255.0);
    Img2.convertTo(Img2,CV_32FC3,1.0/255.0);

    vector<Mat> ch; 
    split(Img1,ch);

    Mat mask = ch[3].clone();              // here's the vignette

    ch.resize(3);

    Mat I1,I2,result;

    cv::multiply(mask,ch[0],ch[0]);
    cv::multiply(mask,ch[1],ch[1]);
    cv::multiply(mask,ch[2],ch[2]);
    merge(ch,I1);
    vector<Mat> ch2(3);
    split(Img2, ch2);
    cv::multiply(1.0-mask,ch2[0],ch2[0]);
    cv::multiply(1.0-mask,ch2[1],ch2[1]);
    cv::multiply(1.0-mask,ch2[2],ch2[2]);
    merge(ch2,I2);
    I1.convertTo(I1,CV_8UC3,255);
    I2.convertTo(I2,CV_8UC3,255);
    result=I1+I2; // The image with the vignette in the question
    result.convertTo(result,CV_8UC4,255);
    Mat result2;
    Mat mask2;
    Mat image = imread ("D:\\i.png"); // image in the question
    blending_overlay(image,result,result2,mask2);
    imshow("Image",result2);
    waitKey(0);
}

It works for blending vignette image with the original image but I only want to blend the vignette part from vignette image with the original image

Required Result

The result i am getting is this

like image 680
AHF Avatar asked Jun 15 '14 20:06

AHF


People also ask

How do I extract an image from a mask?

We first create a binary mask from a specified ROI, which returns a matrix containing only 1 or 0. To return the selected portion of the image, we perform a logical AND of the binary mask matrix with the image using logical indexing. The resulting image will contain the extracted portion.


1 Answers

You have a couple of mistakes. Firstly you seem to be blending using the color to choose between the screen and multiply blends, but you should use the intensity. I think photoshop might do the blend in hsv colorspace, but in this case rgb seems to work as longs as you use L=(r+g+b)/3 for the intensity.

Also your code was blending the image with and alpha blend of the vignette and the image (did the code in you question match the image generated in your question?). Instead you want a "mask" that equals the vignette in the areas where you want the vignette applied, and equals 0.5 in areas where you don't want it applied.

So I take the vignette you provided (far left) which has an alpha channel (2nd from left) and do an alpha blend with gray (second from right) to get an image to use as the top image in a blend overlay (far right). Where the top image is gray, when is blended with the other image, the bottom will show though unchanged. This is so because in these two lines of code:

_result[j][c] = ((1 - (1 - 2 * (target - 0.5)) * (1 - blend)));
_result[j][c] = 2 * target * blend;

if blend = 0.5, it works out that result is set to target.

     Vignette              Alpha                 Gray              Blend Image (top)

enter image description here

I have included the image generated, and the code to do it below. The required image is shown on left, and the generated image is shown on the right. As far as I can see they are the same. An improvement in accuracy could be obtained by not converting to CV_UC3 in the middle, but passing FC3 arguments in blend_overlay().

               Required                                 Generated

enter image description here

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>     // std::cout
#include <vector>       // std::vector
using namespace std;
using namespace cv;

void blending_overlay2(Mat& bot, Mat& top, Mat& out)
{
    Mat result(bot.size(), CV_32FC3);

   // Extract the calculate I = (r + g + b) / 3
    Mat botf;
    bot.convertTo(botf, CV_32FC3, 1.0 / 255.0);
    std::vector<cv::Mat> planes(3);
    cv::split(botf, planes); 

    cv::Mat intensity_f((planes[0] + planes[1] + planes[2]) / 3.0f);
    cv::Mat L;
    intensity_f.convertTo(L, CV_8UC1, 255.0);
    //cv::imshow("L", L);


    for(int i = 0; i < bot.size().height; ++i)
    {
        // get pointers to each row
        cv::Vec3b* _bot = bot.ptr<cv::Vec3b>(i);
        cv::Vec3b* _top = top.ptr<cv::Vec3b>(i);
        cv::Vec3f* _result = result.ptr<cv::Vec3f>(i);
        uchar* _L = L.ptr<uchar>(i);

        // now scan the row
        for(int j = 0; j < bot.size().width; ++j)
        {   
            for (int c=0; c < bot.channels(); c++)
            {
                float target = float(_bot[j][c]) / 255.0f;
                float blend = float(_top[j][c]) / 255.0f;

                if(_L [j] > 128)
                {
                    _result[j][c] = 2 * (blend + target - target * blend) - 1;
                    // Why isn't the below line simplified like above?
                    //_result[j][c] = ((1 - (1 - 2 * (target - 0.5)) * (1 - blend)));
                }
                else
                {
                    _result[j][c] = 2 * target * blend;
                }
            }
        } 
    }
    result.convertTo(out, CV_8UC3, 255);
}

int main( int argc, char** argv )
{
    Mat Img1=cv::imread("kqw0D.png",-1); // the vignete in the question

    Mat Img2=cv::imread("i.png"); // the iamge in the question
    Mat image = Img2.clone();
    cv::resize(Img1,Img1,Img2.size());
    Img1.convertTo(Img1,CV_32FC4,1.0/255.0);
    Img2.convertTo(Img2,CV_32FC3,1.0/255.0);

    // split off the alpha channel from the vignette
    vector<Mat> ch; 
    split(Img1,ch);
    Mat alpha = ch[3].clone();              // here's the vignette
    Mat alpha_u;
    alpha.convertTo(alpha_u,CV_8UC1,255);
    imshow("alpha",alpha);

    // drop the alpha channel from vignette
    ch.resize(3);

    // pre-mutiply each color channel by the alpha
    // and merge premultiplied color channels into 3 channel vignette I1
    Mat I1;
    cv::multiply(alpha, ch[0], ch[0]);
    cv::multiply(alpha, ch[1], ch[1]);
    cv::multiply(alpha, ch[2], ch[2]);
    merge(ch, I1);

    // Now make the vignette = 0.5 in areas where it should not be "applied"
    Mat I2;
    vector<Mat> ch2;
    cv::Mat half = cv::Mat::ones(Img2.rows, Img2.cols, CV_32FC1) * 0.5;
    cv::multiply(1.0f - alpha, half, half);
    ch2.push_back(half);
    ch2.push_back(half);
    ch2.push_back(half);
    //split(Img2, ch2);
    //cv::multiply(1.0f - alpha, ch2[0], ch2[0]);
    //cv::multiply(1.0f - alpha, ch2[1], ch2[1]);
    //cv::multiply(1.0f - alpha, ch2[2], ch2[2]);
    merge(ch2, I2);

    // result = alpha * vignette + (1 - alpha) * gray;
    Mat top;
    top=I1+I2; 


    //  make I1 8 bit images again
    I1.convertTo(I1,CV_8UC3,255);
    I2.convertTo(I2,CV_8UC3,255);
    top.convertTo(top,CV_8UC3,255);
    //imshow("I1",I1);
    //imshow("I2",I2);
    //imshow("top",top);

    Mat result2;
    blending_overlay2(image,top, result2);
    imshow("Voila!", result2);
    imwrite("voila.png", result2);
    waitKey(0);
}
like image 168
Bull Avatar answered Oct 03 '22 12:10

Bull