理论
边界补零,滤波框长宽为奇数,取框内中值为当前像素的值
C++实践
- manual
cv::Mat MedianFilterMaunal(const cv::Mat & src, int ksize = 3)
{
assert((ksize > 0) && (1 == (ksize % 2)) && (!src.empty()));
cv::Mat dst = cv::Mat::zeros(src.size(), src.type());
// 确定核中心点
int origin = floor(ksize / 2);
// 滤波操作
for (int y = 0; y < src.rows; ++y)
{
for (int x = 0; x < src.cols; ++x)
{
for (int c = 0; c < src.channels(); ++c)
{
std::vector<uchar> values(ksize * ksize);
int index = 0;
for (int dy = -origin; dy < origin + 1; ++dy)
{
for (int dx = -origin; dx < origin + 1; ++dx)
{
if (((dx + x) >= 0) && ((dy + y) >= 0))
{
values[index] = src.at<cv::Vec3b>(y + dy, x + dx)[c];
}
++index;
}
}
std::sort(values.begin(), values.end()); // 升序
//std::sort(values.begin(), values.end(), std::greater<uchar>()); // 降序
//std::sort(values.begin(), values.end(), std::less<uchar>()); // 升序
dst.at<cv::Vec3b>(y, x)[c] = values[std::floor(values.size() / 2) + 1];
}
}
}
return dst;
}
- OpenCV
cv::Mat MedianFilterOpenCV(const cv::Mat & src, int ksize)
{
assert((ksize > 0) && (1 == (ksize % 2)) && (!src.empty()));
cv::Mat dst = cv::Mat::zeros(src.size(), src.type());
cv::medianBlur(src, dst, ksize);
return dst;
}
Python实践
- manual
def median_filter_manual(src, k_size=3):
"""
:param src:
:param k_size:
:return:
"""
if len(src.shape) == 2:
src = np.expand_dims(src, axis=-1) # 给灰度图增加一个通道
h, w, c = src.shape
# 边界补零
padding = k_size // 2
dst = np.zeros((h + padding * 2, w + padding * 2, c), dtype=np.float)
dst[padding: h + padding, padding: w + padding] = src.copy().astype(np.float)
# 滤波
for y in range(h):
for x in range(w):
for ch in range(c):
dst[y, x, ch] = np.median(dst[y: y + k_size, x: x + k_size, ch])
# 剪切
dst = dst[: h, :w]
return dst.astype(np.uint8)- OpenCV
def median_filter_opencv(src, ksize=3):
"""
:param src:
:param ksize:
:return:
"""
return cv.medianBlur(src, ksize)性能及结果
Python的手动实现超级慢

版权声明:本文为weixin_39662594原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。