Android OpenCV(十一):图像仿射变换

仿射变换

仿射变换,又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。 仿射变换能够保持图像的“平直性”,包括旋转,缩放,平移,错切操作。一般而言,仿射变换矩阵为23的矩阵,第三列的元素起着平移的作用,前面两列的数字对角线上是缩放,其余为旋转或者错切的作用。
仿射变换是一种二维坐标(x, y)到二维坐标(u, v)的线性变换。数学表达式如下:
数学表达式

对应的齐次坐标系如下

齐次坐标系表达式
仿射变换保持了二维图形的“平直性”(直线经仿射变换后依然为直线)和“平行性”(直线之间的相对位置关系保持不变,平行线经仿射变换后依然为平行线,且直线上点的位置顺序不会发生变化)。非共线的三对对应点确定一个唯一的仿射变换。

API

计算旋转矩阵

public static Mat getRotationMatrix2D(Point center, double angle, double scale) 
  • 参数一:center,图像旋转的中心位置
  • 参数二:angle,图像旋转的角度,单位为度,正值为逆时针旋转
  • 参数三:scale,两个轴的比例因子,可以实现旋转过程中的图像缩放,不缩放输入1

生成的旋转矩阵与旋转角度和旋转中心的关系。

举例说明,若scale为2,angle为90度,则m1为旋转矩阵

计算仿射矩阵(三点)

public static Mat getAffineTransform(MatOfPoint2f src, MatOfPoint2f dst)
  • 参数一:src,原图像中的三个像素坐标
  • 参数二:dst,目标图像中的三个像素坐标
  • 返回值:2*3 的变换矩阵。透视变换是3*3的矩阵,仿射则是2*3的矩阵

仿射变换

public static void warpAffine(Mat src, Mat dst, Mat M, Size dsize, int flags, int borderMode, Scalar borderValue) 
  • 参数一:src,原图

  • 参数二:dst,透视变换后输出图像,与src数据类型相同,但是尺寸与dsize相同

  • 参数三:M,2*3变换矩阵

  • 参数四:dsize,输出图像的尺寸

  • 参数五:flags,插值方法标志

    // C++: enum InterpolationFlags
    public static final int
            INTER_NEAREST = 0,
            INTER_LINEAR = 1,
            INTER_CUBIC = 2,
            INTER_AREA = 3,
            INTER_LANCZOS4 = 4,
            INTER_LINEAR_EXACT = 5,
            INTER_MAX = 7,
            WARP_FILL_OUTLIERS = 8,
            WARP_INVERSE_MAP = 16;
    
  • 参数六:borderMode,像素边界外推方法的标志。BORDER_CONSTANT 或者BORDER_REPLICATE

边界填充作用
BORDER_CONSTANT0用特定值填充,如iiiiii|abcdefgh|iiiiiii
BORDER_REPLICATE1两端复制填充,如aaaaaa|abcdefgh|hhhhhhh
BORDER_REFLECT2倒叙填充,如fedcba|abcdefgh|hgfedcb
BORDER_WRAP3正序填充,如cdefgh|abcdefgh|abcdefg
BORDER_REFLECT_1014不包含边界值倒叙填充,gfedcb|abcdefgh|gfedcba
BORDER_TRANSPARENT5随机填充,uvwxyz|abcdefgh|ijklmno
BORDER_REFLECT1014与BORDER_REFLECT_101相同
BORDER_DEFAULT4与BORDER_REFLECT_101相同
BORDER_ISOLATED16不关心感兴趣区域之外的部分
  • 参数七:borderValue,填充边界使用的数值,默认情况下为0

操作

class AffineActivity : AppCompatActivity() {

    private lateinit var mBinding: ActivityAffineBinding
    private lateinit var mRgb: Mat

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_affine)
        mRgb = Mat()
        val bgr = Utils.loadResource(this, R.drawable.lena)
        Imgproc.cvtColor(bgr, mRgb, Imgproc.COLOR_BGR2RGB)
        bgr.release()
        showMat(mBinding.ivLena, mRgb)
    }


    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)
    }

    private fun rotateMatrix(degree: Double, scale: Double) {
        val center = Point(mRgb.width() / 2.0, mRgb.height() / 2.0)
        val matrix = Imgproc.getRotationMatrix2D(center, degree, scale)
        val size = mRgb.size()
        val dst = Mat()
        Imgproc.warpAffine(mRgb, dst, matrix, size)
        showMat(mBinding.ivResult, dst)
        dst.release()
    }

    private fun threePointsMatrix() {
        val srcPoints = arrayOfNulls<Point>(3)
        val dstPoints = arrayOfNulls<Point>(3)

        srcPoints[0] = Point(0.0, 0.0)
        srcPoints[1] = Point(0.0, mRgb.width() - 1.0)
        srcPoints[2] = Point(mRgb.height() - 1.0, mRgb.width() - 1.0)

        dstPoints[0] = Point(mRgb.width() * 0.11, mRgb.width() * 0.2)
        dstPoints[1] = Point(mRgb.width() * 0.15, mRgb.width() * 0.7)
        dstPoints[2] = Point(mRgb.width() * 0.81, mRgb.width() * 0.85)

        val transform = Imgproc.getAffineTransform(
            MatOfPoint2f(srcPoints[0], srcPoints[1], srcPoints[2]),
            MatOfPoint2f(dstPoints[0], dstPoints[1], dstPoints[2])
        )
        val dst = Mat()
        val size = mRgb.size()
        Imgproc.warpAffine(mRgb, dst, transform, size)
        showMat(mBinding.ivResult, dst)
        dst.release()
    }


    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_affine, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        title = item.title
        when (item.itemId) {
            R.id.affine_rotate_scale -> rotateMatrix(120.0, 1.2)
            R.id.affine_three_points -> threePointsMatrix()
        }
        return true
    }
}

效果

求取旋转矩阵完成仿射

通过三点求取矩阵完成仿射

源码

https://github.com/onlyloveyd/LearningAndroidOpenCV

onlyloveyd CSDN认证博客专家 Android Kotlin OpenCV
个人公众号【OpenCV or Android】,热爱Android、Kotlin、Flutter和OpenCV。毕业于华中科技大学计算机专业,曾就职于华为武汉研究所。目前在三线小城市生活,专注技术与研发。
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页