- Mastering OpenCV 4
- Roy Shilkrot David Millán Escrivá
- 696字
- 2021-07-02 14:47:37
Reducing the random pepper noise from the sketch image
Most of the tiny cameras in smartphones, Raspberry Pi Camera Modules, and some webcams have significant image noise. This is normally acceptable, but it has a big effect on our 5 x 5 Laplacian edge filter. The edge mask (shown as the sketch mode) will often have thousands of small blobs of black pixels called pepper noise, made of several black pixels next to each other on a white background. We are already using a median filter, which is usually strong enough to remove pepper noise, but in our case it may not be strong enough. Our edge mask is mostly a pure white background (value of 255) with some black edges (value of 0) and the dots of noise (also value of 0). We could use a standard closing morphological operator, but it will remove a lot of edges. So instead, we will apply a custom filter that removes small black regions that are surrounded completely by white pixels. This will remove a lot of noise while having little effect on actual edges.
We will scan the image for black pixels, and at each black pixel, we'll check the border of the 5 x 5 square around it to see if all the 5 x 5 border pixels are white. If they are all white, then we know we have a small island of black noise, so then we fill the whole block with white pixels to remove the black island. For simplicity in our 5 x 5 filter, we will ignore the two border pixels around the image and leave them as they are.
The following diagram shows the original image from an Android tablet on the left side, with a sketch mode in the center, showing small black dots of pepper noise and the result of our pepper noise removal shown on the right-hand side, where the skin looks cleaner:

The following code can be named the removePepperNoise() function to edit the image in place for simplicity:
void removePepperNoise(Mat &mask)
{
for (int y=2; y<mask.rows-2; y++) {
// Get access to each of the 5 rows near this pixel.
uchar *pUp2 = mask.ptr(y-2);
uchar *pUp1 = mask.ptr(y-1);
uchar *pThis = mask.ptr(y);
uchar *pDown1 = mask.ptr(y+1);
uchar *pDown2 = mask.ptr(y+2);
// Skip the first (and last) 2 pixels on each row.
pThis += 2;
pUp1 += 2;
pUp2 += 2;
pDown1 += 2;
pDown2 += 2;
for (auto x=2; x<mask.cols-2; x++) {
uchar value = *pThis; // Get pixel value (0 or 255).
// Check if it's a black pixel surrounded bywhite
// pixels (ie: whether it is an "island" of black).
if (value == 0) {
bool above, left, below, right, surroundings;
above = *(pUp2 - 2) && *(pUp2 - 1) && *(pUp2) && *(pUp2 + 1)
&& *(pUp2 + 2);
left = *(pUp1 - 2) && *(pThis - 2) && *(pDown1 - 2);
below = *(pDown2 - 2) && *(pDown2 - 1) && (pDown2) &&
(pDown2 + 1) && *(pDown2 + 2);
right = *(pUp1 + 2) && *(pThis + 2) && *(pDown1 + 2);
surroundings = above && left && below && right;
if (surroundings == true) {
// Fill the whole 5x5 block as white. Since we
// knowthe 5x5 borders are already white, we just
// need tofill the 3x3 inner region.
*(pUp1 - 1) = 255;
*(pUp1 + 0) = 255;
*(pUp1 + 1) = 255;
*(pThis - 1) = 255;
*(pThis + 0) = 255;
*(pThis + 1) = 255;
*(pDown1 - 1) = 255;
*(pDown1 + 0) = 255;
*(pDown1 + 1) = 255;
// Since we just covered the whole 5x5 block with
// white, we know the next 2 pixels won't be
// black,so skip the next 2 pixels on the right.
pThis += 2;
pUp1 += 2;
pUp2 += 2;
pDown1 += 2;
pDown2 += 2;
}
}
// Move to the next pixel on the right.
pThis++;
pUp1++;
pUp2++;
pDown1++;
pDown2++;
}
}
}
That's all! Run the app in the different modes until you are ready to port it to the embedded device!
- Java異步編程實戰
- 劍指JVM:虛擬機實踐與性能調優
- Access 數據庫應用教程
- Rust編程從入門到實戰
- Learning Informatica PowerCenter 10.x(Second Edition)
- 面向STEM的Scratch創新課程
- 游戲程序設計教程
- 名師講壇:Java微服務架構實戰(SpringBoot+SpringCloud+Docker+RabbitMQ)
- The Data Visualization Workshop
- Reactive Android Programming
- UNIX Linux程序設計教程
- 從程序員角度學習數據庫技術(藍橋杯軟件大賽培訓教材-Java方向)
- Python網絡爬蟲實例教程(視頻講解版)
- Parallel Programming with Python
- 深入實踐C++模板編程