均值滤波
均值滤波也称为线性滤波,其采用的主要方法为邻域平均法。线性滤波的基本原理是用均值代替原图像中的各个像素值,即对待处理的当前像素点(x,y),选择一个模板,该模板由其近邻的若干像素组成,求模板中所有像素的均值,再把该均值赋予当前像素点(x,y),作为处理后图像在该点上的灰度g(x,y),即g(x,y)=∑f(x,y)/m m为该模板中包含当前像素在内的像素总个数。从频率域观点来看均值滤波是一种低通滤波器,高频信号将会去掉,因此可以帮助消除图像尖锐噪声,实现图像平滑,模糊等功能。
从均值滤波的定义上看,滤波操作也是进行图像卷积运算。均值滤波使用到的卷积核如下
API
public static void blur(Mat src, Mat dst, Size ksize, Point anchor, int borderType)
-
参数一:src,待均值滤波的图像,图像的数据类型必须是CV_8U、CV_16U、CV_16S、CV_32F和CV_64F这五种数据类型之一。
-
参数二:dst,均值滤波后的图像,与输入图像具有相同的尺寸和数据类型。
-
参数三:ksize,卷积核尺寸。
-
参数四:anchor,内核的基准点(锚点),其默认值为(-1,-1)代表内核基准点位于kernel的中心位置。基准点即卷积核中与进行处理的像素点重合的点,其位置必须在卷积核的内部。
-
参数五:borderType,像素外推法选择标志。默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充。
-
边界填充 值 作用 BORDER_CONSTANT 0 用特定值填充,如iiiiii|abcdefgh|iiiiiii BORDER_REPLICATE 1 两端复制填充,如aaaaaa|abcdefgh|hhhhhhh BORDER_REFLECT 2 倒叙填充,如fedcba|abcdefgh|hgfedcb BORDER_WRAP 3 正序填充,如cdefgh|abcdefgh|abcdefg BORDER_REFLECT_101 4 不包含边界值倒叙填充,gfedcb|abcdefgh|gfedcba BORDER_TRANSPARENT 5 随机填充,uvwxyz|abcdefgh|ijklmno BORDER_REFLECT101 4 与BORDER_REFLECT_101相同 BORDER_DEFAULT 4 与BORDER_REFLECT_101相同 BORDER_ISOLATED 16 不关心感兴趣区域之外的部分
操作
/**
* 均值滤波
* author: yidong
* 2020/4/11
*/
class MeanFilterActivity : AppCompatActivity() {
private lateinit var mBinding: ActivityMeanFilterBinding
private lateinit var mRgb: Mat
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_mean_filter)
val bgr = Utils.loadResource(this, R.drawable.lena)
mRgb = Mat()
Imgproc.cvtColor(bgr, mRgb, Imgproc.COLOR_BGR2RGB)
bgr.release()
showMat(mBinding.ivLena, mRgb)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_mean_filter, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_3_3 -> {
showMat(mBinding.ivLena, mRgb)
val result = Mat()
Imgproc.blur(mRgb, result, Size(3.0, 3.0))
showMat(mBinding.ivResult, result)
result.release()
}
R.id.menu_9_9 -> {
showMat(mBinding.ivLena, mRgb)
val result = Mat()
Imgproc.blur(mRgb, result, Size(9.0, 9.0))
showMat(mBinding.ivResult, result)
result.release()
}
R.id.menu_12_12 -> {
showMat(mBinding.ivLena, mRgb)
val result = Mat()
Imgproc.blur(mRgb, result, Size(12.0, 12.0))
showMat(mBinding.ivResult, result)
result.release()
}
R.id.menu_salt_pepper_noise -> {
saltPepperNoiseAndMeanFilter()
}
R.id.menu_gaussian_noise -> {
gaussianNoiseAndMeanFilter()
}
}
return true
}
private fun saltPepperNoiseAndMeanFilter() {
val source = mRgb.clone()
val number = 10000
for (k in 0..number) {
val i = (0..1000).random() % source.cols()
val j = (0..1000).random() % source.rows()
when ((0..100).random() % 2) {
0 -> {
when (source.channels()) {
1 -> {
source.put(j, i, 255.0)
}
2 -> {
source.put(j, i, 255.0, 255.0)
}
3 -> {
source.put(j, i, 255.0, 255.0, 255.0)
}
else -> {
source.put(j, i, 255.0, 255.0, 255.0, 255.0)
}
}
}
1 -> {
when (source.channels()) {
1 -> {
source.put(j, i, 0.0)
}
2 -> {
source.put(j, i, 0.0, 0.0)
}
3 -> {
source.put(j, i, 0.0, 0.0, 0.0)
}
else -> {
source.put(j, i, 0.0, 0.0, 0.0, 0.0)
}
}
}
}
}
showMat(mBinding.ivLena, source)
val result = Mat()
Imgproc.blur(source, result, Size(9.0, 9.0))
showMat(mBinding.ivResult, result)
result.release()
source.release()
}
private fun gaussianNoiseAndMeanFilter() {
val source = mRgb.clone()
val noise = Mat(source.size(), source.type())
val gaussian = Mat()
Core.randn(noise, 20.0, 50.0)
Core.add(source, noise, gaussian)
showMat(mBinding.ivLena, gaussian)
val result = Mat()
Imgproc.blur(gaussian, result, Size(9.0, 9.0))
showMat(mBinding.ivResult, result)
source.release()
noise.release()
gaussian.release()
result.release()
}
private fun showMat(view: ImageView, source: Mat) {
val bitmap = Bitmap.createBitmap(source.width(), source.height(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(source, bitmap)
view.setImageBitmap(bitmap)
}
override fun onDestroy() {
mRgb.release()
super.onDestroy()
}
}
效果
图片效果
GIF
源码
https://github.com/onlyloveyd/LearningAndroidOpenCV
扫码关注,持续更新
回复【计算机视觉】获取计算机视觉相关必备学习资料
回复【Android】获取Android,Kotlin必备学习资料