
低通滤波与图像缩放插值
低通滤波是图像处理中最基础的平滑手段,而图像缩放与插值则是实际工程中高频使用的预处理操作。
本文将从原理讲解、API解析、Android完整源码实现三个维度,带你系统掌握均值滤波、高斯滤波、图像缩放与插值的工程落地。
核心原理
1. 低通滤波:平滑与去噪
低通滤波的核心作用是抑制图像高频成分(边缘、噪声),保留低频成分(大面积平坦区域),从而实现图像平滑、去噪效果。
(1)均值滤波(Box Filter)
- 原理:用像素邻域内所有像素的平均值替代当前像素值,邻域内每个像素权重相同。
- 以3×3邻域为例,内核为:
K=19111111111 K = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix} K=91 111111111 - 特点:实现简单、速度快,但边缘会被过度模糊,抗噪性一般。
(2)高斯滤波(Gaussian Blur)
- 原理:用高斯函数生成邻域权重,离中心越近的像素权重越高,是加权平均的一种。
- 一维高斯函数公式:
G(x)=Ae−x2/2σ2 G(x) = A e^{-x^2/2\sigma^2} G(x)=Ae−x2/2σ2
其中σ控制曲线的宽窄,σ越大,权重分布越平缓,模糊效果越强。 - 特点:边缘过渡更平滑,保留细节的同时有效去噪,是最常用的平滑滤波。
2. 图像缩放与插值
图像缩放会引入新的像素位置,插值算法决定这些新像素的值,直接影响图像质量。
(1)空间假频问题
直接下采样(如每4个像素取1个)会丢失高频细节,导致锯齿、伪影。因此下采样前必须用低通滤波去除高频成分,再进行缩放。
(2)常用插值算法
| 算法 | 原理 | 特点 |
|---|---|---|
INTER_NEAREST(最近邻) |
直接取最近邻像素值 | 速度最快,效果最差,有明显锯齿 |
INTER_LINEAR(双线性) |
用周围4个像素线性插值 | 效果较好,速度适中,是默认算法 |
INTER_CUBIC(双三次) |
用周围16个像素三次插值 | 效果最好,细节保留好,速度最慢 |
OpenCV 核心 API 解析
1. 低通滤波相关函数
(1)均值滤波
cpp
void blur(
InputArray src, // 输入图像
OutputArray dst, // 输出图像
Size ksize, // 滤波核大小(如Size(5,5))
Point anchor = Point(-1,-1) // 锚点,默认中心
);
(2)高斯滤波
cpp
void GaussianBlur(
InputArray src, // 输入图像
OutputArray dst, // 输出图像
Size ksize, // 滤波核大小(必须为奇数)
double sigmaX, // X方向σ,控制模糊程度
double sigmaY = 0 // Y方向σ,默认与X相同
);
- 若
ksize为Size(0,0),则核大小由sigmaX自动计算。
(3)获取高斯核
cpp
Mat getGaussianKernel(
int ksize, // 核大小(奇数)
double sigma, // σ值
int ktype = CV_64F // 数据类型
);
2. 图像缩放与金字塔
(1)通用缩放函数 resize
cpp
void resize(
InputArray src,
OutputArray dst,
Size dsize, // 目标尺寸
double fx = 0, // 水平缩放因子
double fy = 0, // 垂直缩放因子
int interpolation = INTER_LINEAR // 插值算法
);
(2)图像金字塔(下采样/上采样)
cpp
// 下采样:尺寸缩小一半,内部先做高斯滤波再降采样
void pyrDown(InputArray src, OutputArray dst);
// 上采样:尺寸放大一半,内部先插值再高斯滤波
void pyrUp(InputArray src, OutputArray dst);
Android 完整项目实现
1. 布局文件:activity_main.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp">
<!-- 原图 -->
<ImageView
android:id="@+id/iv_original"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="fitCenter"
android:adjustViewBounds="true"/>
<!-- 均值滤波结果 -->
<ImageView
android:id="@+id/iv_blur"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="4dp"
android:scaleType="fitCenter"
android:adjustViewBounds="true"/>
<!-- 高斯滤波结果 -->
<ImageView
android:id="@+id/iv_gaussian"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="4dp"
android:scaleType="fitCenter"
android:adjustViewBounds="true"/>
<!-- 缩放插值结果 -->
<ImageView
android:id="@+id/iv_resized"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="4dp"
android:scaleType="fitCenter"
android:adjustViewBounds="true"/>
</LinearLayout>
2. Kotlin 上层代码:MainActivity.kt
kotlin
package com.nicoli.lowpassdemo
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
companion object {
init {
System.loadLibrary("native-lib")
}
}
// JNI 接口声明
private external fun processImage(src: Bitmap, outBlur: Bitmap, outGaussian: Bitmap, outResized: Bitmap)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 加载原图
val originalBitmap = BitmapFactory.decodeResource(resources, R.drawable.castle)
// 创建输出位图
val blurBitmap = Bitmap.createBitmap(originalBitmap.width, originalBitmap.height, Bitmap.Config.ARGB_8888)
val gaussianBitmap = Bitmap.createBitmap(originalBitmap.width, originalBitmap.height, Bitmap.Config.ARGB_8888)
val resizedBitmap = Bitmap.createBitmap(originalBitmap.width/4, originalBitmap.height/4, Bitmap.Config.ARGB_8888)
// 执行处理
processImage(originalBitmap, blurBitmap, gaussianBitmap, resizedBitmap)
// 显示结果
findViewById<ImageView>(R.id.iv_original).setImageBitmap(originalBitmap)
findViewById<ImageView>(R.id.iv_blur).setImageBitmap(blurBitmap)
findViewById<ImageView>(R.id.iv_gaussian).setImageBitmap(gaussianBitmap)
findViewById<ImageView>(R.id.iv_resized).setImageBitmap(resizedBitmap)
}
}
3. C++ 核心算法:native-lib.cpp(逐行注释)
cpp
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <android/bitmap.h>
using namespace cv;
using namespace std;
// ====================== 工具函数:Bitmap ↔ Mat 转换 ======================
Mat bitmapToMat(JNIEnv *env, jobject bitmap) {
AndroidBitmapInfo info;
void* pixels;
AndroidBitmap_getInfo(env, bitmap, &info);
AndroidBitmap_lockPixels(env, bitmap, &pixels);
Mat rgba(info.height, info.width, CV_8UC4, pixels);
Mat bgr;
cvtColor(rgba, bgr, COLOR_RGBA2BGR);
AndroidBitmap_unlockPixels(env, bitmap);
return bgr;
}
void matToBitmap(JNIEnv *env, const Mat& srcMat, jobject dstBitmap) {
AndroidBitmapInfo info;
void* pixels;
AndroidBitmap_getInfo(env, dstBitmap, &info);
AndroidBitmap_lockPixels(env, dstBitmap, &pixels);
Mat rgba;
cvtColor(srcMat, rgba, COLOR_BGR2RGBA);
memcpy(pixels, rgba.data, info.width * info.height * 4);
AndroidBitmap_unlockPixels(env, dstBitmap);
}
// ====================== 低通滤波与缩放处理 ======================
void processLowPass(const Mat& srcBgr, Mat& outBlur, Mat& outGaussian, Mat& outResized) {
// 1. 均值滤波(5×5核)
blur(srcBgr, outBlur, Size(5,5));
// 2. 高斯滤波(5×5核,σ=1.5)
GaussianBlur(srcBgr, outGaussian, Size(5,5), 1.5);
// 3. 先高斯滤波去高频,再下采样1/4(避免空间假频)
Mat temp;
GaussianBlur(srcBgr, temp, Size(11,11), 2.0);
resize(temp, outResized, Size(srcBgr.cols/4, srcBgr.rows/4), 0, 0, INTER_LINEAR);
}
// ====================== JNI 接口 ======================
extern "C" JNIEXPORT void JNICALL
Java_com_nicoli_lowpassdemo_MainActivity_processImage
(JNIEnv *env, jobject thiz, jobject srcBitmap, jobject outBlur, jobject outGaussian, jobject outResized) {
// 1. 转换Bitmap为OpenCV Mat
Mat srcBgr = bitmapToMat(env, srcBitmap);
// 2. 执行低通滤波与缩放处理
Mat blurMat, gaussianMat, resizedMat;
processLowPass(srcBgr, blurMat, gaussianMat, resizedMat);
// 3. 转换结果回Bitmap
matToBitmap(env, blurMat, outBlur);
matToBitmap(env, gaussianMat, outGaussian);
matToBitmap(env, resizedMat, outResized);
}

效果与参数详解
1. 运行效果
- 原图:包含城堡、云朵、水面等细节的灰度图像。
- 均值滤波结果:整体被均匀模糊,边缘细节被平滑。
- 高斯滤波结果:平滑效果更自然,边缘过渡柔和,比均值滤波更清晰。
- 缩放结果:下采样后的图像,无明显锯齿伪影,细节保留良好。
2. 关键参数说明
- 滤波核大小:核越大,模糊效果越强,一般取3、5、7等奇数。
- 高斯σ值:σ越大,权重分布越宽,模糊效果越强;若σ为0,OpenCV会根据核大小自动计算σ。
- 插值算法选择 :
- 下采样:优先使用
INTER_LINEAR或INTER_CUBIC,避免锯齿。 - 上采样:
INTER_LINEAR速度快,效果好,是默认选择。
- 下采样:优先使用
扩展应用与优化
1. 不同滤波的选择
- 仅需快速去噪,不追求细节:用均值滤波。
- 需保留边缘,同时平滑图像:用高斯滤波。
- 椒盐噪声去除:优先使用中值滤波(
medianBlur)。
2. 图像缩放优化
- 大图像下采样:先降采样到目标尺寸的2倍,再高斯滤波,最后再次降采样,效果更稳定。
- 实时预览场景:优先使用
INTER_NEAREST或INTER_LINEAR,保证帧率。
总结
低通滤波与图像缩放是图像处理的基础操作,均值滤波简单高效,高斯滤波平滑自然,而缩放插值则直接影响图像质量。核心要点是:下采样前必须用低通滤波去除高频成分,避免空间假频,同时根据场景选择合适的插值算法,在速度与质量之间找到平衡。
