Android OpenCV(五十一):图像插值

1,074 阅读4分钟

这是我参与更文挑战的第 22 天,活动详情查看: 更文挑战

图像插值

何为插值?

插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值。

何为图像插值?

从低分辨率图像生成高分辨率图像的过程(放大),用以恢复图像中所丢失的信息。

API

public static void resize(Mat src, Mat dst, Size dsize, double fx, double fy, int interpolation) 
  • 参数一:src,输入源图像。

  • 参数二:dst,输出目标图像。

  • 参数三:dsize,输出目标大小。若此参数设置为零,则这个值会通过后面的fx和fy两个参数计算:

    dsize = Size(round(fx*src.cols), round(fy*src.rows))\texttt{dsize = Size(round(fx*src.cols), round(fy*src.rows))}
  • 参数四:fx,X轴的比例因子。若此参数设置为零,则其值通过dsize计算:

    (double)dsize.width/src.cols\texttt{(double)dsize.width/src.cols}
  • 参数五:fy,Y轴的比例因子。若此参数设置为零,则其值通过dsize计算:

    (double)dsize.height/src.rows\texttt{(double)dsize.height/src.rows}
  • 参数六: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像素计算得出,公式类似于双线性。操作方式和双三次插值类似,计算距离,计算权重,计算行列乘积,求和。

Lanczos插值计算

操作

/**
 * 图像插值
 * 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()
    }
}

效果

INTER_NEAREST

INTER_LINEAR

INTER_CUBIC

INTER_AREA

INTER_LANSZOS4

源码

github.com/onlyloveyd/…