Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Morphology OPEN can detects bright structures larger that a given size. If you consider large structures as background you can use OPEN to detect background than remove it from the original image. This is same as to do MORPH_TOPHAT.

Follow a simple function to do this. Getting on load honeycomb from @Petyu's question here

image description

this is the result (minThickess=4):

image description

this is the background:

image description

and this the code

/** @brief Remove non-uniform illumination using morphology
Morphology OPEN can detects bright structures larger that a given size.
If you consider large structures as background you can use OPEN
to detect background than remove it from the original image.
This is same as to do MORPH_TOPHAT.
@param [in]src input image GRAY, BGR or BGRA.
With BGR(A) image this function uses Brightness from image HSV.
@param [out]dst destination image. If alpha channel is present in src it will be cloned in dst
@param minThickess size used by morphology operation to estimate background. Use small size to
enhance details flatting larger structures.
@c minThickess should be just larger than maximum thickness in objects you want to keep.
Example:
- Take thickest object, suppose is circle 100 * 100px
- Measure its maximum thickness let's say is 20px: In this case @c minThickess could be 20+5.
- If the circle is filled than thickness=diameter, consequently @c minThickess should be 100+5px
@param restoreMean if true, the mean of input brightness will be restored in destination image.
if false, the destination brightness will be close to darker region of input image.
@param [out]background if not NULL the removed background will be returned here.
This will be Mat(src.size(),CV_8UC1)
*/
void NonUniformIlluminationMorph(const cv::Mat &src, cv::Mat &dst, int minThickess = 5, bool restoreMean = true, cv::Mat *background=NULL)
{
    CV_Assert(minThickess >= 0);
    CV_Assert((src.type() == CV_8UC1) || (src.type() == CV_8UC3) || (src.type() == CV_8UC4));
    cv::Mat brightness, src_hsv;
    vector<cv::Mat> hsv_planes;

    // GET THE BRIGHTNESS
    if (src.type() == CV_8UC1)
        src.copyTo(brightness);
    else if (src.type() == CV_8UC3)
    {
        cv::cvtColor(src, src_hsv, cv::COLOR_BGR2HSV);
        cv::split(src_hsv, hsv_planes);
        brightness = hsv_planes[2];
    }
    else if (src.type() == CV_8UC4)
    {
        cv::cvtColor(src, src_hsv, cv::COLOR_BGRA2BGR);
        cv::cvtColor(src_hsv, src_hsv, cv::COLOR_BGR2HSV);
        cv::split(src_hsv, hsv_planes);
        brightness = hsv_planes[2];
    }

    //to restore previous brightness we need its current mean
    Scalar m;
    if (restoreMean)
        m = mean(brightness);

    // REMOVE THE BACKGROUND
    int size = minThickess / 2;
    Point anchor = Point(size, size);
    Mat element = getStructuringElement(MORPH_ELLIPSE, Size(2 * size + 1, 2 * size + 1), anchor);
    if (background != NULL) // to keep background we need to use MORPH_OPEN
    {
        //get the background
        cv::Mat bkg(brightness.size(), CV_8UC1);
        morphologyEx(brightness, bkg, MORPH_OPEN, element, anchor);
        //save the background
        (*background) = bkg;
        //remove the background
        brightness = brightness - bkg;
    }
    else //tophat(I)  <=> open(I) - I; 
    {
        //remove background
        morphologyEx(brightness, brightness, MORPH_TOPHAT, element, anchor);
    }

    // RESTORE PREVIOUS BRIGHTNESS MEAN
    if (restoreMean)
        brightness += m(0);

    // BUILD THE DESTINATION
    if (src.type() == CV_8UC1)
        dst = brightness;
    else if (dst.type() == CV_8UC3)
    {
        merge(hsv_planes, dst);
        cvtColor(dst, dst, COLOR_HSV2BGR);
    }
    // restore alpha channel from source 
    else if (dst.type() == CV_8UC4)
    {
        cv::Mat bgr;
        vector<cv::Mat> bgr_planes = { hsv_planes[0], hsv_planes[1], hsv_planes[2]};
        merge(bgr_planes, bgr);
        cvtColor(bgr, bgr, COLOR_HSV2BGR);

        int from_toA[] = { 0, 0, 1, 1, 2, 2 };
        src.copyTo(dst);
        cv::mixChannels(&bgr, 1, &dst, 1, from_toA, 3);
    }

    imshow("NonUniformIlluminationMorph:SRC", src);
    imshow("NonUniformIlluminationMorph:DST", dst);
    if ((background != NULL) && (!background->empty()))
        imshow("NonUniformIlluminationMorph:BKG", *background);
}

Morphology OPEN can detects bright structures larger that a given size. If you consider large structures as background you can use OPEN to detect background than remove it from the original image. This is same as to do MORPH_TOPHAT.

Follow Below is a simple function to do this. Getting on load honeycomb from @Petyu's question hereThis is a simple test (source and result)

image description

this is the result (minThickess=4):

image description

this is the background:

image descriptionSource Result

and this the code

/** @brief Remove non-uniform illumination using morphology
Morphology OPEN can detects bright structures larger that a given size.
If you consider large structures as background you can use OPEN
to detect background than remove it from the original image.
This is same as to do MORPH_TOPHAT.
@param [in]src input image GRAY, BGR or BGRA.
With BGR(A) image this function uses Brightness from image HSV.
@param [out]dst destination image. If alpha channel is present in src it will be cloned in dst
@param minThickess size used by morphology operation to estimate background. Use small size to
enhance details flatting larger structures.
@c minThickess should be just larger than maximum thickness in objects you want to keep.
Example:
- Take thickest object, suppose is circle 100 * 100px
- Measure its maximum thickness let's say is 20px: In this case @c minThickess could be 20+5.
- If the circle is filled than thickness=diameter, consequently @c minThickess should be 100+5px
@param restoreMean if true, the mean of input brightness will be restored in destination image.
if false, the destination brightness will be close to darker region of input image.
@param [out]background if not NULL the removed background will be returned here.
This will be Mat(src.size(),CV_8UC1)
*/
void NonUniformIlluminationMorph(const cv::Mat &src, cv::Mat &dst, int minThickess = 5, bool restoreMean = true, cv::Mat *background=NULL)
{
    CV_Assert(minThickess >= 0);
    CV_Assert((src.type() == CV_8UC1) || (src.type() == CV_8UC3) || (src.type() == CV_8UC4));
    cv::Mat brightness, src_hsv;
    vector<cv::Mat> hsv_planes;

    // GET THE BRIGHTNESS
    if (src.type() == CV_8UC1)
        src.copyTo(brightness);
    else if (src.type() == CV_8UC3)
    {
        cv::cvtColor(src, src_hsv, cv::COLOR_BGR2HSV);
        cv::split(src_hsv, hsv_planes);
        brightness = hsv_planes[2];
    }
    else if (src.type() == CV_8UC4)
    {
        cv::cvtColor(src, src_hsv, cv::COLOR_BGRA2BGR);
        cv::cvtColor(src_hsv, src_hsv, cv::COLOR_BGR2HSV);
        cv::split(src_hsv, hsv_planes);
        brightness = hsv_planes[2];
    }

    //to restore previous brightness we need its current mean
    Scalar m;
    if (restoreMean)
        m = mean(brightness);

    // REMOVE THE BACKGROUND
    int size = minThickess / 2;
    Point anchor = Point(size, size);
    Mat element = getStructuringElement(MORPH_ELLIPSE, Size(2 * size + 1, 2 * size + 1), anchor);
    if (background != NULL) // to keep background we need to use MORPH_OPEN
    {
        //get the background
        cv::Mat bkg(brightness.size(), CV_8UC1);
        morphologyEx(brightness, bkg, MORPH_OPEN, element, anchor);
        //save the background
        (*background) = bkg;
        //remove the background
        brightness = brightness - bkg;
    }
    else //tophat(I)  <=> open(I) - I; 
    {
        //remove background
        morphologyEx(brightness, brightness, MORPH_TOPHAT, element, anchor);
    }

    // RESTORE PREVIOUS BRIGHTNESS MEAN
    if (restoreMean)
        brightness += m(0);

    // BUILD THE DESTINATION
    if (src.type() == CV_8UC1)
        dst = brightness;
    else if (dst.type() == CV_8UC3)
    {
        merge(hsv_planes, dst);
        cvtColor(dst, dst, COLOR_HSV2BGR);
    }
    // restore alpha channel from source 
    else if (dst.type() == CV_8UC4)
    {
        cv::Mat bgr;
        vector<cv::Mat> bgr_planes = { hsv_planes[0], hsv_planes[1], hsv_planes[2]};
        merge(bgr_planes, bgr);
        cvtColor(bgr, bgr, COLOR_HSV2BGR);

        int from_toA[] = { 0, 0, 1, 1, 2, 2 };
        src.copyTo(dst);
        cv::mixChannels(&bgr, 1, &dst, 1, from_toA, 3);
    }

    imshow("NonUniformIlluminationMorph:SRC", src);
    imshow("NonUniformIlluminationMorph:DST", dst);
    if ((background != NULL) && (!background->empty()))
        imshow("NonUniformIlluminationMorph:BKG", *background);
}

Morphology OPEN can detects bright structures larger that a given size. If you consider large structures as background you can use OPEN to detect background than remove it from the original image. This is same as to do MORPH_TOPHAT.

MORPH_TOPHAT. Below is a simple function to do this. this.

This is the result on a simple test image (source and result)

Source Result

and Test on complex image is here while this the codeis the code:

/** @brief Remove non-uniform illumination using morphology
Morphology OPEN can detects bright structures larger that a given size.
If you consider large structures as background you can use OPEN
to detect background than remove it from the original image.
This is same as to do MORPH_TOPHAT.
@param [in]src input image GRAY, BGR or BGRA.
With BGR(A) image this function uses Brightness from image HSV.
@param [out]dst destination image. If alpha channel is present in src it will be cloned in dst
@param minThickess size used by morphology operation to estimate background. Use small size to
enhance details flatting larger structures.
@c minThickess should be just larger than maximum thickness in objects you want to keep.
Example:
- Take thickest object, suppose is circle 100 * 100px
- Measure its maximum thickness let's say is 20px: In this case @c minThickess could be 20+5.
- If the circle is filled than thickness=diameter, consequently @c minThickess should be 100+5px
@param restoreMean if true, the mean of input brightness will be restored in destination image.
if false, the destination brightness will be close to darker region of input image.
@param [out]background if not NULL the removed background will be returned here.
This will be Mat(src.size(),CV_8UC1)
*/
void NonUniformIlluminationMorph(const cv::Mat &src, cv::Mat &dst, int minThickess = 5, bool restoreMean = true, cv::Mat *background=NULL)
{
    CV_Assert(minThickess >= 0);
    CV_Assert((src.type() == CV_8UC1) || (src.type() == CV_8UC3) || (src.type() == CV_8UC4));
    cv::Mat brightness, src_hsv;
    vector<cv::Mat> hsv_planes;

    // GET THE BRIGHTNESS
    if (src.type() == CV_8UC1)
        src.copyTo(brightness);
    else if (src.type() == CV_8UC3)
    {
        cv::cvtColor(src, src_hsv, cv::COLOR_BGR2HSV);
        cv::split(src_hsv, hsv_planes);
        brightness = hsv_planes[2];
    }
    else if (src.type() == CV_8UC4)
    {
        cv::cvtColor(src, src_hsv, cv::COLOR_BGRA2BGR);
        cv::cvtColor(src_hsv, src_hsv, cv::COLOR_BGR2HSV);
        cv::split(src_hsv, hsv_planes);
        brightness = hsv_planes[2];
    }

    //to restore previous brightness we need its current mean
    Scalar m;
    if (restoreMean)
        m = mean(brightness);

    // REMOVE THE BACKGROUND
    int size = minThickess / 2;
    Point anchor = Point(size, size);
    Mat element = getStructuringElement(MORPH_ELLIPSE, Size(2 * size + 1, 2 * size + 1), anchor);
    if (background != NULL) // to keep background we need to use MORPH_OPEN
    {
        //get the background
        cv::Mat bkg(brightness.size(), CV_8UC1);
        morphologyEx(brightness, bkg, MORPH_OPEN, element, anchor);
        //save the background
        (*background) = bkg;
        //remove the background
        brightness = brightness - bkg;
    }
    else //tophat(I)  <=> open(I) - I; 
    {
        //remove background
        morphologyEx(brightness, brightness, MORPH_TOPHAT, element, anchor);
    }

    // RESTORE PREVIOUS BRIGHTNESS MEAN
    if (restoreMean)
        brightness += m(0);

    // BUILD THE DESTINATION
    if (src.type() == CV_8UC1)
        dst = brightness;
    else if (dst.type() == CV_8UC3)
    {
        merge(hsv_planes, dst);
        cvtColor(dst, dst, COLOR_HSV2BGR);
    }
    // restore alpha channel from source 
    else if (dst.type() == CV_8UC4)
    {
        cv::Mat bgr;
        vector<cv::Mat> bgr_planes = { hsv_planes[0], hsv_planes[1], hsv_planes[2]};
        merge(bgr_planes, bgr);
        cvtColor(bgr, bgr, COLOR_HSV2BGR);

        int from_toA[] = { 0, 0, 1, 1, 2, 2 };
        src.copyTo(dst);
        cv::mixChannels(&bgr, 1, &dst, 1, from_toA, 3);
    }

    imshow("NonUniformIlluminationMorph:SRC", src);
    imshow("NonUniformIlluminationMorph:DST", dst);
    if ((background != NULL) && (!background->empty()))
        imshow("NonUniformIlluminationMorph:BKG", *background);
}

Morphology OPEN can detects bright structures larger that a given size. If you consider large structures as background you can use OPEN to detect background than remove it from the original image. This is same as to do MORPH_TOPHAT. Below is a simple function to do this.

This is the result on a simple image (source thanks to and result)

Source Result

Test on complex image is here while this is the code:

[EDIT] corrected a small error

/** @brief Remove non-uniform illumination using morphology
Morphology OPEN can detects bright structures larger that a given size.
If you consider large structures as background you can use OPEN
to detect background than remove it from the original image.
This is same as to do MORPH_TOPHAT.
@param [in]src input image GRAY, BGR or BGRA.
With BGR(A) image this function uses Brightness from image HSV.
@param [out]dst destination image. If alpha channel is present in src it will be cloned in dst
@param minThickess size used by morphology operation to estimate background. Use small size to
enhance details flatting larger structures.
@c minThickess should be just larger than maximum thickness in objects you want to keep.
Example:
- Take thickest object, suppose is circle 100 * 100px
- Measure its maximum thickness let's say is 20px: In this case @c minThickess could be 20+5.
- If the circle is filled than thickness=diameter, consequently @c minThickess should be 100+5px
@param restoreMean if true, the mean of input brightness will be restored in destination image.
if false, the destination brightness will be close to darker region of input image.
@param [out]background if not NULL the removed background will be returned here.
This will be Mat(src.size(),CV_8UC1)
*/
void NonUniformIlluminationMorph(const cv::Mat &src, cv::Mat &dst, int minThickess = 5, bool restoreMean = true, cv::Mat *background=NULL)
{
    CV_Assert(minThickess >= 0);
    CV_Assert((src.type() == CV_8UC1) || (src.type() == CV_8UC3) || (src.type() == CV_8UC4));
    cv::Mat brightness, src_hsv;
    vector<cv::Mat> hsv_planes;

    // GET THE BRIGHTNESS
    if (src.type() == CV_8UC1)
        src.copyTo(brightness);
    else if (src.type() == CV_8UC3)
    {
        cv::cvtColor(src, src_hsv, cv::COLOR_BGR2HSV);
        cv::split(src_hsv, hsv_planes);
        brightness = hsv_planes[2];
    }
    else if (src.type() == CV_8UC4)
    {
        cv::cvtColor(src, src_hsv, cv::COLOR_BGRA2BGR);
        cv::cvtColor(src_hsv, src_hsv, cv::COLOR_BGR2HSV);
        cv::split(src_hsv, hsv_planes);
        brightness = hsv_planes[2];
    }

    //to restore previous brightness we need its current mean
    Scalar m;
    if (restoreMean)
        m = mean(brightness);

    // REMOVE THE BACKGROUND
    int size = minThickess / 2;
    Point anchor = Point(size, size);
    Mat element = getStructuringElement(MORPH_ELLIPSE, Size(2 * size + 1, 2 * size + 1), anchor);
    if (background != NULL) // to keep background we need to use MORPH_OPEN
    {
        //get the background
        cv::Mat bkg(brightness.size(), CV_8UC1);
        morphologyEx(brightness, bkg, MORPH_OPEN, element, anchor);
        //save the background
        (*background) = bkg;
        //remove the background
        brightness = brightness - bkg;
    }
    else //tophat(I)  <=> open(I) - I; 
    {
        //remove background
        morphologyEx(brightness, brightness, MORPH_TOPHAT, element, anchor);
    }

    // RESTORE PREVIOUS BRIGHTNESS MEAN
    if (restoreMean)
        brightness += m(0);

    // BUILD THE DESTINATION
    if (src.type() == CV_8UC1)
        dst = brightness;
    else if (dst.type() (src.type() == CV_8UC3)
    {
        merge(hsv_planes, dst);
        cvtColor(dst, dst, COLOR_HSV2BGR);
    }
    // restore alpha channel from source 
    else if (dst.type() (src.type() == CV_8UC4)
    {
        cv::Mat bgr;
        vector<cv::Mat> bgr_planes = { hsv_planes[0], hsv_planes[1], hsv_planes[2]};
        merge(bgr_planes, bgr);
        cvtColor(bgr, bgr, COLOR_HSV2BGR);

        int from_toA[] = { 0, 0, 1, 1, 2, 2 };
        src.copyTo(dst);
        cv::mixChannels(&bgr, 1, &dst, 1, from_toA, 3);
    }

    imshow("NonUniformIlluminationMorph:SRC", src);
    imshow("NonUniformIlluminationMorph:DST", dst);
    if ((background != NULL) && (!background->empty()))
        imshow("NonUniformIlluminationMorph:BKG", *background);
}