7

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

AHF
  • 1,070
  • 2
  • 15
  • 47
  • Can you simply extract the portion of the image you want, process that, then copy those pixels back to the original image? Also, forgive my ignorance but what is a **vignette**? – rayryeng Jun 15 '14 at 20:28
  • this is vignette http://www.lifeclever.com/wp-content/uploads/2007/01/vignette_edge_no_image.jpg – AHF Jun 15 '14 at 20:31
  • i want the portion of the image after applying vignette – AHF Jun 15 '14 at 20:31
  • 1
    What do you mean by applying? You're really not making it clear for me – rayryeng Jun 15 '14 at 22:26
  • Since The vignette is applied to the whole image, the question doesn't make sense. – Bull Jun 16 '14 at 05:53
  • Just add a second out2 Mat to `blending_overlay()` that contains a mask of where you applied the vignette. `if(target > 0.5)` set the mask to 1, otherwise 0. Then whatever called `blending_overlay()` knows exactly where the vignette was applied, without having to know the internal logic. – Bull Jun 21 '14 at 12:43
  • @B... add it in the answer so we can solve it – AHF Jun 22 '14 at 10:22
  • you asking for something like this ' vector ch; split(Img,ch); Mat mask = ch[3].clone(); // vignette ch.resize(3);' – AHF Jun 22 '14 at 10:25
  • @AHF In `blending_overlay()` you are assuming that `img2` is 3 channel. But you are passing in something with 4 channels, so your assignment to `blend` is calculated from the wrong pixel. Also I am still unclear about what you are trying to achieve. – Bull Jun 22 '14 at 22:41
  • @B... I change it in my tool but forget to update it here. – AHF Jun 23 '14 at 11:13
  • @AHF, could you explain how you generated the "Required Result", and what is wrong with the actual result? Now my understanding is that you generate an alpha blend of the vignette and the image to get `result`, then you do an overlay blend of that on top of the original image. My trouble is that I don't understand why you do the last bit and what you oping to achieve by it. – Bull Jun 24 '14 at 06:18
  • @B... I generate the required result through photoshop , and the difference is very minor between my required result and the result i am getting , if you see the both images closely then you understand that the centre part of the image of required result is same as original image , blending mode only blend the vignette part not the centre part of the image but in my result my blending mode also blend the centre part of the image and due to this little brightness effect the result – AHF Jun 24 '14 at 10:09
  • @AHF You can adapt both answers from http://stackoverflow.com/q/22187205/2521214 to do what you need. first find the area (line from top and botom) and then iterate between them via vertical lines (or do it the other way find left,right boundaries and iterate with horizontal lines) – Spektre Jun 28 '14 at 06:39

1 Answers1

8

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);
}
Bull
  • 11,771
  • 9
  • 42
  • 53
  • Actually image already contain vignette , like the image i read is tha image above in my question , so how i read them separately like you write `Mat image` and `Mat vignette` – AHF Jun 19 '14 at 18:20
  • @AHF now I don't really understand what you are trying to achieve. If you have an access to only the image to which the vignette was applied, but not the vignette or the original image, getting a solution will require some assumptions. You need tp spell out in your question what you are really trying to do. I.e. give the image you provided, what output do you want? – Bull Jun 20 '14 at 01:18
  • I think given a single vignetted image you want to regenerate the vignette and the original image? – Bull Jun 20 '14 at 01:26
  • @AHF it would have been very helpful if you had if you linked to this related question: http://stackoverflow.com/questions/23220686/trying-to-translate-formula-for-blending-mode/23222729?noredirect=1#23222729 and also mentioned that you were emulating Photoshop's Blendoverlay. Also, I have seen that blend formula elsewhere, just wondering why it is isn't written in simplified form? Also, I got rid of the `at<>()` pixel addressing as it is very slow. – Bull Jun 22 '14 at 13:27
  • still not getting the required result , it changes the whole image , i update the whole code – AHF Jun 22 '14 at 20:17
  • @AHF not sure what you mean by `it changes the whole image` – Bull Jun 22 '14 at 22:32
  • @B... I update my question with my original images and all code , required result , result i am getting , original image etc , i will award you bounty for it , i you cover all aspect and give me a better understadning , Thank you – AHF Jun 23 '14 at 11:13
  • Your answer is not giving the required result – AHF Jun 26 '14 at 20:20
  • @AHF Your question is not providing sufficient information to enable calculation of the required result. You said it is generated by Photoshop, but exactly what operations did you do in Photoshop. – Bull Jun 26 '14 at 21:48
  • i take the photo , put red vignette on it , and apply blending overlay on it with original photo !! – AHF Jun 28 '14 at 14:54
  • @AHF, I now understand what you are trying to do, and I have updated my code to generate the desired result. Can I have the bounty? – Bull Jun 29 '14 at 07:36
  • @B... Can i ask the related question , because i am not understanding one thing , like look at the images you provided in the row like `vignette , alpha , gray and blend image` , what i am not understanding is , when we convert vignette into alpha why it become black from centre and white from corners , and why we need to use only gray ? why not other colors Thanks – AHF Jul 01 '14 at 13:15
  • 1
    @AHF the alpha channel is as provided in the fourth channel of Vig.png, vignette is the other three channels in Vig.png. Any place the top layer is not equal to 0.5 (i.e. gray) the result will not be equal to the bottom layer. You can experiment with other colors, but you will not get the result you specified. – Bull Jul 01 '14 at 13:30
  • @B... so for other vignette colours i need to test colours other than gray , right ? – AHF Jul 01 '14 at 13:41
  • @AHF, sorry I don't understand what you are trying achieve. The gray color is something internal to the code I posted, it is created from the alpha channel on the vignette. You can make the colors in Vig.png whatever you like. – Bull Jul 01 '14 at 14:27