Change color of a specific region in eye image [Matlab]

前端 未结 2 961
自闭症患者
自闭症患者 2021-01-17 04:55

I am trying to change the pupil of the following image:
\"enter
This is not a featur

2条回答
  •  北海茫月
    2021-01-17 05:46

    Unfortunately, you don't have a choice but to detect where the pupil is located and fill those pixels in with your desired colour. Simply thresholding will not work because the eyelashes have roughly the same intensity distribution as the pupil.

    What we can do is do some post-processing after you threshold the image. However, what I am suggesting will require the Image Processing Toolbox in MATLAB. If you don't have this, then unfortunately my solution won't work.

    Assuming that you do have access to this, what I can suggest is use regionprops to detect unique shapes in your image. This is assuming that you threshold the image properly. I'm going to specifically look at the 'Area' and 'PixelList' attributes. Once we threshold the image, we should expect predominantly two areas of clustered pixels: The eyelashes and the pupil. The eyelashes will probably have the largest amount of area while the pupil the second largest. As such, let's take a look at the Area attribute and determine the object in your image that has the second largest area. Once we find this object, we will set these pixels where this area is located to whatever colour you wish. However, we're going to have to do a bit of post-processing to ensure that the entire pupil gets filled in. You see that there is a white spot in the middle of the pupil most likely due to the flash of the camera. We would essentially need to ensure that the entire detected pupil is a closed contour, then fill in the shape. Once we fill in this shape, we simply use these pixels and set them to whatever colour you wish in the output image.


    Now, the first step is to threshold the image properly. First, I read in the image directly from StackOverflow. However, this image is a RGB image, and I want the grayscale equivalent. We can use rgb2gray to accomplish that for us. Once that's done, I used a threshold of intensity 35, then ran through regionprops like so:

    im = imread('http://i.stack.imgur.com/d3hy5.jpg');
    im = rgb2gray(im);
    s = regionprops(im < 35, 'Area', 'PixelList');
    

    s will contain a structure of information, where each element tells you the area and the pixels that belong to each unique object that was detected in your image. Let's take a look at the area:

    areas = [s.Area].'
    
    areas =
    
           1
           2
           1
           2
           5
           3
           1
          19
           3
           1
           2
           2
           1
          23
        1224
           1
           2
          41
          12
           2
           1
           2
           1
           5
           2
          33
         480
           3
           6
           1
           2
           1
           1
           2
           1
    

    You can see that there are a lot of spurious isolated pixels that result after we threshold, which makes sense. You will also see that we have a couple of patches that have a significantly larger area than the majority of the patches. There is one patch that has an area of 1433, while another 480. The area with 1433 is most likely the pixels that belong to the eyelashes, as they more or less share the same intensity distribution as that of your pupil. The pupil will lost likely be the second highest area. As such, let's take a look at the patch that has the second highest area. To figure out where this is, use find:

    ind = find(areas == 480);
    

    Now that we know where this is located in s, let's pull this out and get out the PixelList attribute. This gives you a list of pixels that belong to the object:

    pix = s(ind).PixelList;
    

    The first column denotes the column position of each pixel belonging to the object while the second column denotes the row position. What I'm going to do next is take these pixels and create a mask that only contains these detected pixels. I will use this mask to index into the original image and set those pixels to whatever colour you want. As such:

    mask = logical(full(sparse(pix(:,2), pix(:,1), 1, size(im,1), size(im,2))));
    

    The code uses sparse to create a sparse matrix where every value is 0 except for those pixel locations defined in pix. This is an easier way of creating matrix of 1s only at specified locations and 0 otherwise. Because this is sparse, I need to change this to a full numeric matrix that we can use, and we finally need to cast using logical to ensure a proper mask.

    If we show this mask using imshow, this is what we get:

    enter image description here

    You can see that there is a gap due to the white spot of the pupil in the original image, which makes sense. You will also notice that the contour of the pupil isn't entirely closed. As such, I'm going to perform morphological closing by imclose by choosing a disk structuring element to close the gaps. Once we close the gaps, we can use imfill and choose the 'holes' flag to fill in these holes.

    We now have a fully filled in mask that we can finally use to index into the original image and set our colours. As such:

    mask = imclose(mask, strel('disk', 4, 4));
    mask = imfill(mask, 'holes');
    

    Here's what the mask looks like now:

    enter image description here

    Cool! Now the last thing you need to do is colour in those pixels with whatever colour you wish. To do this, you'll need to make a RGB version of your image. As such, simply create red, green and blue channels that replicate the grayscale content of your image and set each channel with the appropriate colour using the pixels defined in the mask.

    red = im;
    red(mask) = 255;
    green = im;
    green(mask) = 0;
    blue = im;
    blue(mask) = 0;
    out = cat(3, red, green, blue);
    imshow(out);
    

    Remember that for grayscale images, the RGB equivalent has all of the red, green and blue values the same. Once we set the colour for the pupil, stack all of these together as a 3D matrix using cat and we finally show the image. I've also set the colour of the pupil to red. You can change the RGB value to whatever you desire by changing the constant assignment (255, 0, 0) to be your desired colour.

    This is what we get:

    enter image description here


    For your copying and pasting pleasure, this is the full code from start to finish:

    im = imread('http://i.stack.imgur.com/d3hy5.jpg');
    im = rgb2gray(im);
    s = regionprops(im < 35, 'Area', 'PixelList');
    areas = [s.Area].';
    ind = find(areas == 480);
    pix = s(ind).PixelList;
    mask = logical(full(sparse(pix(:,2), pix(:,1), 1, size(im,1), size(im,2))));
    mask = imclose(mask, strel('disk', 4, 4));
    mask = imfill(mask, 'holes');
    red = im;
    red(mask) = 255;
    green = im;
    green(mask) = 0;
    blue = im;
    blue(mask) = 0;
    out = cat(3, red, green, blue);
    imshow(out);
    

提交回复
热议问题