By Georg Petschnigg, June
2002
This is a detailed description of the mask creating and correction step. Go back
here.
| Steps | Detailed Description |
![]() |
![]() |
| 1. Take the best non flash image given the current lighting conditions with less than 1/30sec exposure. | This image was taken using a Nikon CoolPix 995
camera with the following image settings: CAMERA : E995V1.6 METERING : MATRIX MODE : P SHUTTER : 1/7sec APERTURE : F2.6 EXP +/- : 0.0 FOCAL LENGTH : f8.2mm(X1.0) IMG ADJUST : STANDARD SENSITIVITY : ISO800 WHITEBAL : AUTO SHARPNESS : OFF DATE : 2002.06.02 22:12 QUALITY : FULL FINE SATURATION : 0 FOCUS AREA : CENTER Note the exposure time was more than 1/30sec, because the Nikon995 currently does not support an ISO1600 or above mode. The Nikon995 is the best one can currently do with a compact camera ISO wise. Still the image exhibits all the noise artifacts one would expect from a high ISO settings image. |
![]() |
![]() |
| 2. Take the best flash image given the current lighting conditions. Steps 1 and 2 should not take longer than 1/30sec to avoid blurring and image registration. | The flash image shows the redeye problem I will
attempt to correct. CAMERA : E995V1.6 METERING : MATRIX MODE : P SHUTTER : 1/60sec APERTURE : F2.6 EXP +/- : 0.0 FOCAL LENGTH : f8.2mm(X1.0) IMG ADJUST : AUTO SENSITIVITY : AUTO WHITEBAL : AUTO SHARPNESS : AUTO DATE : 2002.06.02 22:12 QUALITY : FULL FINE SATURATION : 0 FOCUS AREA : CENTER |
![]() |
![]() |
| 3. Blur the non flash image using a 8X8 Gaussian. | Non flash image red channel before blurring. |
![]() |
![]() |
| Blurred non flash image's red channel. This step is done to remove noise and to prevent errors if images are not aligned perfectly. | |
![]() |
![]() |
| As a reference, this is what the non flash image's red channel looks like. | |
![]() |
![]() |
| 4. Convert the blurred non flash image into
YCrCb color space. Before the conversion the RGB values are raised to the power
1/3 to accentuate the red eye. This step is due to [Patti98]. |
Non flash image's Cr component. noflash_ycbcr_image = rgb2ycbcr(img_noflash .^ 0.3); |
![]() |
![]() |
| 5. Repeat step 4. for the flash image | Flash image's Cr component. flash_ycbcr_image = rgb2ycbcr(img_flash .^ 0.3); |
![]() |
![]() |
| 6. Subtract the non flash Cr channel from the flash Cr channel and exit if no significant differences are found | This is the result of subtracting the non flash
Cr image from the flash Cr image. The results of this operation lie in the range
-1 to 1. This image only shows the positive parts scaled to the range 0 to 1.
This is where the flash image was "more red" than the non flash image. The red
eye lies at the maximum difference. mask = flash_ycbcr_image(:,:,3) - noflash_ycbcr_image(:,:,3); At this point the algorithm could return if no "significant" Cr differences where detected, suggesting no red eye is present in the image. Currently the system does not detect the "absence" of redeye and expects at least one to occur. Microsoft Research in China developed a neural networked redeye detector that could be used to verify if redeyes are present. |
![]() |
![]() |
| 7. Normalize the intensity range to 0 to 1 | This image shows the result of the subtraction
normalized to the 0 to 1 range. The bright areas around the ear are due to image
misalignment. This image shows JPEG artifact blocking which hamper the correction
performance as well. mask = mask - min(mask(:)); mask = mask / max(max(mask)); |
![]() The difference image's histogram shows that the majority of the image is centered about the mean. The red eye is located at the extreme point on the right, where the Cr channel difference is the largest. |
|
![]() |
![]() |
| 7. Threshold the image against a lower bound and segment 8-connected regions into blobs | The lower threshold image estimates the largest
redeye spread. The lower threshold is computed as follows: TLower = max([ 0.4 mean(mask(:)) + var(mask(:)) ^ 0.5]); The lower threshold is determined by adding the root of the variance to the mean difference. As a sanity check this number should be at least 0.4, a bit lower than the expected value of 0.5. Finally all 8-connected points are grouped into colored blobs, which is accomplished via the Matlab function bwlabel described here following Haralick, Robert M., and Linda G. Shapiro. Computer and Robot Vision, Volume I. Addison-Wesley, 1992. pp. 28-48. low = bwlabel(mask > TLower, 8); |
![]() |
![]() |
| 8. Compute an upper threshold | The upper threshold is defined to be anything above
six times the squared root of the variance plus the mean, This should assure
that only statistically significant differences are included. At most thought
the upper threshold should be 0.8, which was determined by looking at histogram
distributions of the sample data set. TUpper = min([0.8 mean(mask(:)) + 6 * var(mask(:)) ^ 0.5]) |
![]() |
![]() |
| 9. Keep only lower threshold blobs that contain pixels from the upper threshold image | This image shows the labeled blobs that contain
larger Cr differences. Each blob is assigned a unique label, indicated in this
image by color. Obviously there are some incorrectly included blobs as well,
which will be removed in the geometry segmentation step. high = mask > TUpper; [r, c] = find(high); final_candidates = bwselect(low,c,r,8);low = bwlabel(low, 8); |
![]() |
![]() |
| 10. Perform geometric selection using blob with largest Cr difference as seed | For each selected blob compute its Area, Minimum
Bounding Rectangle and Eccentricity. Area is a good indication of size, the miminum bounding rectangle will be used to fit an ellipsoid, and the eccentricity
gives a measure of how circle or line like a blob is. Eccentricity is
described
in detail
here. The corresponding Matlab call is imfeature. Finally I use the assumption that a redeye is located where the largest Cr difference occurs to find one seed eye. I use the seed eye's statistics to eliminate too large or non ellipsoid blobs. In this image for example the seed eye has the following statistics: Area: 181 Centroid: [226.5193 169.6022] BoundingBox: [215.5000 160.5000 22 20] MajorAxisLength: 25.4041 MinorAxisLength: 10.3848 Eccentricity: 0.9126 Orientation: 36.4705 ConvexHull: [14x2 double] ConvexImage: [20x22 uint8] ConvexArea: 261 Image: [20x22 uint8] FilledImage: [20x22 uint8] FilledArea: 181 EulerNumber: 1 Extrema: [8x2 double] EquivDiameter: 15.1808 Solidity: 0.6935 Extent: 0.4114 PixelList: [181x2 double] Only blobs that are about within 70% of the seed eyes size and have an Eccentricity of less than 0.75 are kept. Those numbers were determined using the data set I took. |
![]() |
![]() |
| 11. Fit ellipsoids into the bounding box of remaining
blobs |
Using the bounding box information of each redeye
I fit an ellipsoid over the eye. The current implementation does not account
for redeye partially occluded by eyelids or which no axis aligned images.
This is a flaw. |
![]() |
![]() |
| 12. Feather the generated redeye mask | The final redeye mask. Feathering is performed
using a 3X3 Gaussian kernel. |
After the redeye mask is complete color correction can be performed |
|
![]() |
![]() |
| The segmented red eyes using the mask above | |
![]() |
![]() |
| 1. Predict red channel by averaging the red and
green channel of the red eye 2. If needed adjust the average luminance to a mid gray tone |
Result of predicting the red channel by
averaging the blue and green channel. This image then is raised to the power
of gamma which is computed below. The effect is to scale the redeye median
to about 0.1 - which seems reasonably dark. gamma = log(.15) / log(median_eye_luminace); |
![]() |
![]() |
| As a reminder the redeye image. | |
![]() |
![]() |
| 3. Composite the flash image and fixed redeye using the OVER operator and the mask as an alpha map | The final corrected image. |