前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >快速中值滤波算法之黄氏算法

快速中值滤波算法之黄氏算法

作者头像
BBuf
发布于 2019-12-04 10:05:57
发布于 2019-12-04 10:05:57
1.8K00
代码可运行
举报
文章被收录于专栏:GiantPandaCVGiantPandaCV
运行总次数:0
代码可运行

算法原理

传统的中值滤波是通过滑动窗口不断在图像上移动,求出窗口内的中值作为中心像素点的像素,在这个过程中显然存在大量的重复计算,所以效率很低。所以有人提出了一个利用直方图来做中值滤波的算法,请看下图:

可以把整个图片看成滑动窗口,当我们从左边移动到右边时,中间的粉色部分是共享的,只有黄色部分变为了蓝色部分,所以就想了利用直方图来更新的方法。

算法过程

1.读取图像I,并且设定滤波窗口大小(winX*winY),一般winX=winY,奇数。 2.设定中值滤波直方图中的阈值,Thresh=(winX*winY)/2 +1; 3.如果要考虑边界情况,可以先对原图像进行扩展,左、右边界分别扩展winX/2个像素,上下边界分别扩展winY/2个像素。 4.逐行遍历图像像素,以第一行为例:先取第一行第一个要处理的像素(窗口中心像素),建立滤波窗口,提取窗口内所有像素值(N=winX*winY个),获取N个像素的直方图Hist。从左到右累加直方图中每个灰度层级下像素点个数,记为sumCnt,直到sumCnt>=Thresh,这时的灰度值就是当前窗口内所有像素值的中值MediaValue。将MediaValue值赋值给窗口中心像素,表明第一个像素中值滤波完成。 5.此时窗口需要向右移动一个像素,开始滤波第二个像素,并且更新直方图。以第二个像素为窗口中心建立滤波窗口,从前一个窗口的灰度直方图Hist中减去窗口中最左侧的一列像素值的灰度个数,然后加上窗口最右侧一列像素值的灰度个数。完成直方图的更新。 6.直方图更新后,sumCnt值有三种变化可能:(1)减小(2)维持不变(3)增大。这三种情况与减去与加入的像素值灰度有关。此时为了求得新的中值,需要不断调整sumCnt与Thresh之间的关系。 (1)如果sumCnt值小于Thresh:说明中值在直方图当前灰度层级的右边,sumCnt就依次向右加上一个灰度层级中灰度值个数,直到满足sumCnt>=Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue,作为第二个像素的滤波后的值。 (2)维持不变:说明MediaValue值不变,直接作为第二个像素滤波后的值。 (3)如果sumCnt值大于Thresh:说明中值在直方图当前灰度层级的左边,sumCnt就依次向左减去一个灰度层级中灰度值个数,直到满足sumCnt<=Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue值,作为第二个像素的滤波后的值。 7. 窗口逐行依次滑动,求得整幅图像的中值滤波结果。

代码实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <iostream>
#include <immintrin.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

//计算中值
int getMediaValue(const int hist[], int thresh) {
  int sum = 0;
  for (int i = 0; i < 256; i++) {
    sum += hist[i];
    if (sum >= thresh) {
      return i;
    }
  }
  return 255;
}
//快速中值滤波,灰度图
Mat fastMedianBlur(Mat src, int diameter) {
  int row = src.rows;
  int col = src.cols;
  Mat dst(row, col, CV_8UC1);
  int Hist[256] = { 0 };
  int radius = (diameter - 1) / 2;
  int windowSize = diameter * diameter;
  int threshold = windowSize / 2 + 1;
  uchar *srcData = src.data;
  uchar *dstData = dst.data;
  int right = col - radius;
  int bot = row - radius;
  for (int j = radius; j < bot; j++) {
    for (int i = radius; i < right; i++) {
      //每一行第一个待滤波元素建立直方图
      if (i == radius) {
        memset(Hist, 0, sizeof(Hist));
        for (int y = j - radius; y <= min(j + radius, row); y++) {
          for (int x = i - radius; x <= min(i + radius, col); x++) {
            uchar val = srcData[y * col + x];
            Hist[val]++;
          }
        }
      }
      else {
        int L = i - radius - 1;
        int R = i + radius;
        for (int y = j - radius; y <= min(j + radius, row); y++) {
          //更新左边一列
          Hist[srcData[y * col + L]]--;
          //更新右边一列
          Hist[srcData[y * col + R]]++;
        }
      }
      uchar medianVal = getMediaValue(Hist, threshold);
      dstData[j * col + i] = medianVal;
    }
  }
  //边界直接赋值
  for (int i = 0; i < col; i++) {
    for (int j = 0; j < radius; j++) {
      int id1 = j * col + i;
      int id2 = (row - j - 1) * col + i;
      dstData[id1] = srcData[id1];
      dstData[id2] = srcData[id2];
    }
  }
  for (int i = radius; i < row - radius - 1; i++) {
    for (int j = 0; j < radius; j++) {
      int id1 = i * col + j;
      int id2 = i * col + col - j - 1;
      dstData[id1] = srcData[id1];
      dstData[id2] = srcData[id2];
    }
  }
  return dst;
}

void meanFilter(int height, int width, unsigned char * __restrict src, unsigned char * __restrict dst) {
  for (int i = 1; i < height - 1; i++) {
    for (int j = 1; j < width - 1; j++) {
      unsigned char a[9];
      a[0] = src[i * width + j];
      a[1] = src[i * width + j + 1];
      a[2] = src[i * width + j - 1];

      a[3] = src[(i + 1) * width + j];
      a[4] = src[(i + 1) * width + j + 1];
      a[5] = src[(i + 1) * width + j - 1];

      a[6] = src[(i - 1) * width + j];
      a[7] = src[(i - 1) * width + j + 1];
      a[8] = src[(i - 1) * width + j - 1];
      for (int ii = 0; ii < 5; ii++) {
        for (int jj = ii + 1; jj < 9; jj++) {
          if (a[ii] > a[jj]) {
            unsigned char temp = a[ii];
            a[ii] = a[jj];
            a[jj] = temp;
          }
        }
      }
      dst[i * width + j] = a[4];
    }
  }
  for (int i = 0; i < width; i++) {
    dst[i] = src[i];
    dst[(height - 1) * width + i] = src[(height - 1) * width + i];
  }
  for (int i = 0; i < height; i++) {
    dst[i * width] = src[i * width];
    dst[i * width + width - 1] = src[i * width + width - 1];
  }
}
Mat speed_MedianFilter(Mat src) {
  int row = src.rows;
  int col = src.cols;
  unsigned char * data = (unsigned char *)src.data;
  unsigned char *dst = new unsigned char[row * col];
  //for (int i = 0; i < row * col; i++) {
  //  printf("%d\n", data[i]);
  //}
  meanFilter(row, col, data, dst);
  Mat res(row, col, CV_8UC1, dst);
  return res;
}

Mat speed_rgb2gray(Mat src) {
  Mat dst(src.rows, src.cols, CV_8UC1);
#pragma omp parallel for num_threads(4)
  for (int i = 0; i < src.rows; i++) {
    for (int j = 0; j < src.cols; j++) {
      dst.at<uchar>(i, j) = ((src.at<Vec3b>(i, j)[0] << 18) + (src.at<Vec3b>(i, j)[0] << 15) + (src.at<Vec3b>(i, j)[0] << 14) +
        (src.at<Vec3b>(i, j)[0] << 11) + (src.at<Vec3b>(i, j)[0] << 7) + (src.at<Vec3b>(i, j)[0] << 7) + (src.at<Vec3b>(i, j)[0] << 5) +
        (src.at<Vec3b>(i, j)[0] << 4) + (src.at<Vec3b>(i, j)[0] << 2) +
        (src.at<Vec3b>(i, j)[1] << 19) + (src.at<Vec3b>(i, j)[1] << 16) + (src.at<Vec3b>(i, j)[1] << 14) + (src.at<Vec3b>(i, j)[1] << 13) +
        (src.at<Vec3b>(i, j)[1] << 10) + (src.at<Vec3b>(i, j)[1] << 8) + (src.at<Vec3b>(i, j)[1] << 4) + (src.at<Vec3b>(i, j)[1] << 3) + (src.at<Vec3b>(i, j)[1] << 1) +
        (src.at<Vec3b>(i, j)[2] << 16) + (src.at<Vec3b>(i, j)[2] << 15) + (src.at<Vec3b>(i, j)[2] << 14) + (src.at<Vec3b>(i, j)[2] << 12) +
        (src.at<Vec3b>(i, j)[2] << 9) + (src.at<Vec3b>(i, j)[2] << 7) + (src.at<Vec3b>(i, j)[2] << 6) + (src.at<Vec3b>(i, j)[2] << 5) + (src.at<Vec3b>(i, j)[2] << 4) + (src.at<Vec3b>(i, j)[2] << 1) >> 20);
    }
  }
  return dst;
}

int main() {
  Mat src = cv::imread("F:\\1.jpg");
  src = speed_rgb2gray(src);
  Mat dst1 = fastMedianBlur(src, 3);
  Mat dst2 = speed_MedianFilter(src);
  cv::imshow("dst1", dst1);
  cv::imshow("dst2", dst2);
  int row = src.rows;
  int col = src.cols;
  for (int i = 0; i < row; i++) {
    for (int j = 0; j < col; j++) {
      if (dst1.at<uchar>(i, j) != dst1.at<uchar>(i, j)) {
        printf("Failture\n");
      }
    }
  }
  cv::waitKey(0);
  return 0;
}

算法已经测试过,计算无误。

源码链接

https://github.com/BBuf/Image-processing-algorithm 已有83人标星,欢迎STAR。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GiantPandaCV 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
OpenCV图像处理专栏九 | 基于直方图的快速中值滤波算法
这是OpenCV图像处理专栏的第9篇文章,主要介绍一个基于直方图的快速中值滤波算法,希望对大家有帮助。
BBuf
2019/12/23
8660
直方图实现快速中值滤波
中值滤波能够有效去除图像中的异常点,具有去除图像噪声的作用。传统中值滤波的算法一般都是在图像中建立窗口,然后对窗口内的所有像素值进行排序,选择排序后的中间值作为窗口中心像素滤波后的值。由于这个做法在每
一棹烟波
2018/01/12
1.9K0
直方图实现快速中值滤波
OpenCV图像处理专栏十 | 利用中值滤波进行去雾
这是OpenCV图像处理专栏的第十篇文章,介绍一种利用中值滤波来实现去雾的算法。这个方法发表于国内的一篇论文,链接我放附录了。
BBuf
2019/12/26
8860
OpenCV图像处理专栏十 | 利用中值滤波进行去雾
☀️Python+opencv图像处理☀️
如今的修图软件真是厉害,专业级的ps不说,手机上的各种修图软件也是强大无比。尤其是各种厉害又好玩的滤镜真是让人感叹技术可以改变生活,让芒果忍不住好奇了解了解图像处理中的滤镜。
苏州程序大白
2022/04/13
6420
☀️Python+opencv图像处理☀️
OpenCV图像处理专栏七 | 《Contrast image correction method》 论文阅读及代码实现
这是OpenCV图像处理专栏的第七篇文章,这篇文章是在之前的推文 OpenCV图像处理专栏二 |《Local Color Correction 》论文阅读及C++复现基础上进行了改进,仍然针对数字图像的光照不均衡现象进行校正。
BBuf
2019/12/24
1.4K0
OpenCV图像处理专栏十二 |《基于二维伽马函数的光照不均匀图像自适应校正算法》
这是OpenCV图像处理专栏的第十二篇文章,今天为大家介绍一个用于解决光照不均匀的图像自适应校正算法。光照不均匀其实是非常常见的一种状况,为了提升人类的视觉感受或者是为了提升诸如深度学习之类的算法准确性,人们在解决光照不均衡方面已经有大量的工作。一起来看看这篇论文使用的算法吧,论文名为:《基于二维伽马函数的光照不均匀图像自适应校正算法》。
BBuf
2020/02/21
2.4K0
OpenCV图像处理专栏十二 |《基于二维伽马函数的光照不均匀图像自适应校正算法》
自动白平衡之完美反射算法原理及C++实现
昨天介绍的灰度世界算法是最原始的处理白平衡的算法。今天要介绍的完美反射算法也是自动白平衡常用的算法之一。一起来看看吧。
BBuf
2019/12/04
1.8K0
自动白平衡之完美反射算法原理及C++实现
OpenCV图像处理专栏十四 | 基于Retinex成像原理的自动色彩均衡算法(ACE)
这个算法是IPOL上一篇名为《Automatic Color Equalization(ACE) and its Fast Implementation》提出的,这实际上也是《快速ACE算法及其在图像拼接中的应用》这篇论文中使用的ACE算法,这个算法主要是基于Retinex成像理论做的自动彩色均衡,我用C++ OpenCV实现了,来分享一下。
BBuf
2020/02/21
1.5K0
OpenCV图像处理专栏十四 | 基于Retinex成像原理的自动色彩均衡算法(ACE)
OpenCV图像处理专栏十一 | IEEE Xplore 2015的图像白平衡处理之动态阈值法
这是OpenCV图像处理专栏的第十一篇文章,之前介绍过两种处理白平衡的算法,分别为灰度世界算法和完美反射算法。今天来介绍另外一个自动白平衡的算法,即动态阈值法,一个看起来比较厉害的名字。论文原文链接放在附录。
BBuf
2019/12/30
1K0
《Automatic Color Equalization and its Fast Implementation》图像论文复现
这篇论文实际上也是《快速ACE算法及其在图像拼接中的应用》这篇论文中的快速ACE算法,我用C++实现了,现在放出来。
BBuf
2019/12/04
7370
《Automatic Color Equalization and its Fast Implementation》图像论文复现
OpenCV图像处理专栏十七 | 清华大学《基于单幅图像的快速去雾》C++复现(有一定工程意义)
这是OpenCV图像处理算法朴素实现专栏的第17篇文章。今天为大家带来一篇之前看到的用于单幅图像去雾的算法,作者来自清华大学,论文原文见附录。
BBuf
2020/03/19
1.5K0
OpenCV图像处理专栏十七 | 清华大学《基于单幅图像的快速去雾》C++复现(有一定工程意义)
OpenCV图像处理专栏十三 | 利用多尺度融合提升图像细节
今天为大家介绍一个利用多尺度来提升图像细节的算法。这个算法来自于论文《DARK IMAGE ENHANCEMENT BASED ON PAIRWISE TARGET CONTRAST AND MULTI-SCALE DETAIL BOOSTING》,如果你想自己的图片细节看起来更加丰富的话可以尝试一下这个算法。
BBuf
2020/02/21
7890
OpenCV图像处理专栏十三 | 利用多尺度融合提升图像细节
CVPR2019 Oral 论文《Side Window Filtering》C++代码实现
作者在知乎上介绍了他这篇论文的原理,原帖见这里:https://zhuanlan.zhihu.com/p/58326095 。我这篇文章主要谈实现。
BBuf
2019/12/09
1K0
10、图像的几何变换——平移、镜像、缩放、旋转、仿射变换 OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(1)OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(2)数字图像
  图像几何变换又称为图像空间变换,它将一副图像中的坐标位置映射到另一幅图像中的新坐标位置。我们学习几何变换就是确定这种空间映射关系,以及映射过程中的变化参数。图像的几何变换改变了像素的空间位置,建立一种原图像像素与变换后图像像素之间的映射关系,通过这种映射关系能够实现下面两种计算:
vv彭
2020/10/27
4.1K0
10、图像的几何变换——平移、镜像、缩放、旋转、仿射变换
    


OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(1)OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(2)数字图像
OpenCV图像处理专栏十五 |《一种基于亮度均衡的图像阈值分割技术》
对于光照不均匀的图像,用通常的图像分割方法不能取得满意的效果。为了解决这个问题,论文《一种基于亮度均衡的图像阈值分割技术》提出了一种实用而简便的图像分割方法。该方法针对图像中不同亮度区域进行亮度补偿,使得整个图像亮度背景趋于一致后,再进行常规的阈值分割。实验结果表明,用该方法能取得良好的分割效果。关于常规的阈值分割不是我这篇推文关注的,我这里只实现前面光照补偿的部分。算法的原理可以仔细看论文。论文原文见附录。
BBuf
2020/02/21
1.1K0
OpenCV图像处理专栏十五 |《一种基于亮度均衡的图像阈值分割技术》
OpenCV图像处理专栏一 | 盘点常见颜色空间互转
今天是OpenCV传统图像处理算法的第一篇,我们来盘点一下常见的6种颜色空间互转算法,并给出了一些简单的加速方案,希望可以帮助到学习OpenCV图像处理的同学。这6种算法分别是:
BBuf
2019/12/09
1.3K0
OpenCV图像处理笔记(三):霍夫变换、直方图、轮廓等综合应用
一、霍夫直线变换 1、霍夫直线变换 Hough Line Transform用来做直线检测 前提条件 – 边缘检测已经完成 平面空间到极坐标空间转换 2、霍夫直线变换介绍 对于任意一条直线上的所有点来说 变换到极坐标中,从[0~360]空间,可以得到r的大小 属于同一条直线上点在极坐标空(r, theta)必然在一个点上有最强的信号出现,根据此反算到平面坐标中就可以得到直线上各点的像素坐标。从而得到直线 3、相关API 标准的霍夫变换 cv::HoughLines从平面坐标转换到霍夫空间,最终输出是
MiChong
2020/09/24
3.1K0
OpenCV图像处理笔记(三):霍夫变换、直方图、轮廓等综合应用
灰度世界算法原理及C++实现
前置内容 人的视觉系统具有颜色恒常性,能从变化的光照环境和成像条件下获取物体表面颜色的不变特性,但成像设备并不具有这样的调节功能,不同的光照环境会导致采集到的图像颜色与真实颜色存在一定程度的偏差,需要选择合适的颜色平衡算法去消除光照环境对颜色显示的影响。 灰度世界算法原理 灰度世界算法以灰度世界假设为基础,假设为:对于一幅有着大量色彩变化的图像,RGB3个分量的平均值趋于同一个灰度值Gray。从物理意思上讲,灰度世界算法假设自然界景物对于光线的平均反射的均值在整体上是一个定值,这个定值近似为“灰色”。颜色平衡算法将这一假设强制应用于待处理的图像,可以从图像中消除环境光的影响,获得原始场景图像。 算法步骤
BBuf
2019/12/04
1.6K0
灰度世界算法原理及C++实现
综述:图像滤波常用算法实现及原理解析
来源丨https://zhuanlan.zhihu.com/p/279602383
计算机视觉
2021/01/12
1.9K0
综述:图像滤波常用算法实现及原理解析
图像分割之分水岭算法[通俗易懂]
分水岭概念是以对图像进行三维可视化处理为基础的:其中两个是坐标,另一个是灰度级。基于“地形学”的这种解释,我们考虑三类点:
全栈程序员站长
2022/07/05
2.5K0
图像分割之分水岭算法[通俗易懂]
推荐阅读
OpenCV图像处理专栏九 | 基于直方图的快速中值滤波算法
8660
直方图实现快速中值滤波
1.9K0
OpenCV图像处理专栏十 | 利用中值滤波进行去雾
8860
☀️Python+opencv图像处理☀️
6420
OpenCV图像处理专栏七 | 《Contrast image correction method》 论文阅读及代码实现
1.4K0
OpenCV图像处理专栏十二 |《基于二维伽马函数的光照不均匀图像自适应校正算法》
2.4K0
自动白平衡之完美反射算法原理及C++实现
1.8K0
OpenCV图像处理专栏十四 | 基于Retinex成像原理的自动色彩均衡算法(ACE)
1.5K0
OpenCV图像处理专栏十一 | IEEE Xplore 2015的图像白平衡处理之动态阈值法
1K0
《Automatic Color Equalization and its Fast Implementation》图像论文复现
7370
OpenCV图像处理专栏十七 | 清华大学《基于单幅图像的快速去雾》C++复现(有一定工程意义)
1.5K0
OpenCV图像处理专栏十三 | 利用多尺度融合提升图像细节
7890
CVPR2019 Oral 论文《Side Window Filtering》C++代码实现
1K0
10、图像的几何变换——平移、镜像、缩放、旋转、仿射变换 OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(1)OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(2)数字图像
4.1K0
OpenCV图像处理专栏十五 |《一种基于亮度均衡的图像阈值分割技术》
1.1K0
OpenCV图像处理专栏一 | 盘点常见颜色空间互转
1.3K0
OpenCV图像处理笔记(三):霍夫变换、直方图、轮廓等综合应用
3.1K0
灰度世界算法原理及C++实现
1.6K0
综述:图像滤波常用算法实现及原理解析
1.9K0
图像分割之分水岭算法[通俗易懂]
2.5K0
相关推荐
OpenCV图像处理专栏九 | 基于直方图的快速中值滤波算法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验