How to blur an image after obtaining its mask using opencv?

Question:

I’ve followed a guide on blurring image based on colour segmentation: https://realpython.com/python-opencv-color-spaces/

But I have trouble in getting it to blur only the nemo in the original image.

What I have so far from following the guide:

import matplotlib.pyplot as plt
import cv2

nemo = cv2.imread('nemo.png')
nemo = cv2.cvtColor(nemo, cv2.COLOR_BGR2RGB)
hsv_nemo = cv2.cvtColor(nemo, cv2.COLOR_RGB2HSV)

light_orange = (1, 190, 200)
dark_orange = (18, 255, 255)
light_white = (0, 0, 200)
dark_white = (145, 60, 255)

mask = cv2.inRange(hsv_nemo, light_orange, dark_orange)
mask_white = cv2.inRange(hsv_nemo, light_white, dark_white)
final_mask = mask + mask_white

final_result = cv2.bitwise_and(nemo, nemo, mask=final_mask)
blur = cv2.blur(final_result, (15, 15), 0)

I’ve plotted out 1) nemo, 2) final_mask, and 3) blur for comparison here: Image Result

Is there a way for me to blur the nemo in the original photo such that it looks something like this: Blurred Original Photo

At the moment it only blurs nemo in the mask image.

I think I need to obtain a ROI for me to do so, but how do I form the relationship between a mask and getting the coordinates/ ROI of the nemo from the original photo? Thanks!

Asked By: Jenny

||

Answers:

If you wanted to blur the image only where the mask is true, something like this should work

blur = cv2.blur(nemo,(15,15),0)
out = nemo.copy()
out[mask>0] = blur[mask>0]

However, you may want to change how the mask has been generated, as currently it will only blur the orange and white parts of the fish.

Answered By: Rob

Here is an answer that works on a Vec3b but only looks at channel 0.
It respects the mask and only blurs where there is not a mask. This prevents "halo" effects on the edges of interior masked areas. In my case it is a ball with the outside of circle masked out and also the glare spot reflected off the ball.

    void MyPipelineCPU::MyBlur(Mat& src, Mat& mask, Mat& dst, const int diameter) {
    if (dst.empty() || dst.rows != src.rows || dst.cols != src.cols || dst.type() != src.type())
        dst = Mat::zeros(src.rows, src.cols, src.type());

    const int diamter2 = diameter * diameter;
    const int radius = diameter / 2;
    const int radius2 = radius * radius;
    const int rowmax = src.rows - radius;
    const int colmax = src.cols - radius;
    for (int r = radius; r < rowmax; r++) {
        for (int c = radius; c < colmax; c++) {
            uchar msk = mask.at<uchar>(r, c);
            if (msk) {
                Vec3b& srcP = src.at<Vec3b>(r, c);
                Vec3b& dstP = dst.at<Vec3b>(r, c);

                // It is treated as a grey image even though three channels
                int sum0 = 0;
                int count0 = 0;
                for (int dy = -radius; dy <= radius; dy++) {
                    for (int dx = -radius; dx <= radius; dx++) {
                        if (mask.at<uchar>(r + dy, c + dx)) {
                            const Vec3b& pp = src.at<Vec3b>(r + dy, c + dx);
                            sum0 += pp[0];
                            count0++;
                        }
                    }
                }

                if (count0 > 4) {
                    const int avg0 = sum0 / count0;
                    // Blur
                    dstP[0] = avg0;
                    dstP[1] = avg0;
                    dstP[2] = avg0;
                }
                else {
                    dstP[0] = 0;
                    dstP[1] = 0;
                    dstP[2] = 0;
                }
            }
            else {
                Vec3b& dstP = dst.at<Vec3b>(r, c);
                dstP[0] = 0;
                dstP[1] = 0;
                dstP[2] = 0;
            }
        }
    }
}
Answered By: Richard Keene
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.