这是我参与更文挑战的第 22 天,活动详情查看: 更文挑战
图像插值
何为插值?
插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值。
何为图像插值?
从低分辨率图像生成高分辨率图像的过程(放大),用以恢复图像中所丢失的信息。
API
public static void resize(Mat src, Mat dst, Size dsize, double fx, double fy, int interpolation)
-
参数一:src,输入源图像。
-
参数二:dst,输出目标图像。
-
参数三:dsize,输出目标大小。若此参数设置为零,则这个值会通过后面的fx和fy两个参数计算:
-
参数四:fx,X轴的比例因子。若此参数设置为零,则其值通过dsize计算:
-
参数五:fy,Y轴的比例因子。若此参数设置为零,则其值通过dsize计算:
-
参数六:interpolation,插值算法标志位。
public static final int INTER_NEAREST = 0, // 最近邻插值法 INTER_LINEAR = 1, // 双线性插值法 INTER_CUBIC = 2, // 双三次插值法 INTER_AREA = 3, // 使用像素区域关系进行重采样 INTER_LANCZOS4 = 4, // 8x8像素邻域的Lanczos插值 INTER_LINEAR_EXACT = 5, // 位精确双线性插值 INTER_NEAREST_EXACT = 6, // 位精确最近邻插值。这将产生与PIL,scikit-image或Matlab中的最近邻居方法相同的结果
插值方法
最近邻插值(INTER_NEAREST)
最近邻插值法, 找到与之距离最相近的邻居(原来就存在的像素点, 黑点), 赋值与其相同。
双线性插值 (INTER_LINEAR)
双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。
双三次插值(INTER_CUBIC)
双三次插值是一种更加复杂的插值方式,它能创造出比双线性插值更平滑的图像边缘。在这种方法中,函数 f 在点 (x, y) 的值可以通过矩形网格中最近的十六个采样点的加权平均得到,在这里需要使用两个多项式插值三次函数,每个方向使用一个。
如上,P点就是目标图像B在(X,Y)处对应于源图像A中的位置, 假设P的坐标为(i+u,j+v),其中i,j分别表示整数部分,u,v分别表示小数部分。找到距离p最近的16个像素的位置,在这里用a(m,n)(m,n=0,1,2,3)来表示。双立方插值的目的就是通过找到一种关系,或者说系数,可以把这16个像素对于P处像素值得影响因子找出来,从而根据这个影响因子来获得目标图像对应点的像素值,达到图像缩放的目的。基于BiCubic基函数的双三次插值法使用如下函数:
通过16个最近的点到P点的距离,求取权重系数。然后通过行列权重与各点的像素值乘积求和,获取目标图像对应位置的像素值。
区域插值(INTER_AREA )
区域插值共分三种情况,图像放大时类似于双线性插值,图像缩小(x轴、y轴同时缩小)又分两种情况,此情况下可以避免波纹出现。因此对图像进行缩小时,为了避免出现波纹现象,推荐采用区域插值方法。
Lanczos插值(INTER_LANCZOS4)
兰索斯插值,由相邻的8*8像素计算得出,公式类似于双线性。操作方式和双三次插值类似,计算距离,计算权重,计算行列乘积,求和。
操作
/**
* 图像插值
* author: yidong
* 2020/12/27
*/
class ResizeActivity : AppCompatActivity() {
private val mList = mutableListOf<ImageTextObject>()
private val mAdapter by lazy { ImageTextAdapter(this, mList) }
private val mBinding: ActivityResizeBinding by lazy {
ActivityResizeBinding.inflate(layoutInflater)
}
private val mFlags = mapOf(
Imgproc.INTER_NEAREST to "INTER_NEAREST",
Imgproc.INTER_LINEAR to "INTER_LINEAR",
Imgproc.INTER_CUBIC to "INTER_CUBIC",
Imgproc.INTER_AREA to "INTER_AREA",
Imgproc.INTER_LANCZOS4 to "INTER_LANCZOS4"
)
private val rgb: Mat by lazy {
val bgr = getBgrFromResId(R.drawable.tiny_lena)
bgr.toRgb()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
mBinding.container.adapter = mAdapter
wrapCoroutine({ before() }, { doResize() }, { after() })
}
override fun onDestroy() {
super.onDestroy()
rgb.release()
}
private fun doResize() {
val w = rgb.rows()
val h = rgb.cols()
for (i in mFlags) {
val dst = Mat()
Imgproc.resize(
rgb,
dst,
Size((w * 100).toDouble(), (h * 100).toDouble()),
0.0,
0.0,
i.key
)
mList.add(ImageTextObject(dst, mFlags.getOrElse(i.key) { "" }))
}
}
private fun before() {
mBinding.isLoading = true
}
private fun after() {
mBinding.isLoading = false
mAdapter.notifyDataSetChanged()
}
}