Affine transformation
Affine transformation, also known as affine mapping, is the transformation of a vector space into another vector space by a linear transformation followed by a translation in geometry. Affine transformation can maintain the “flatness” of the image, including rotation, scaling, translation, and tangent operation. Generally speaking, if the affine transformation matrix is 23, the elements in the third column play the role of translation, the first two columns are scaled on the diagonal, and the rest are rotated or miscut. Affine transformation is a linear transformation from two dimensional coordinates (x, y) to two dimensional coordinates (u, v). The mathematical expression is as follows:
The corresponding homogeneous coordinate system is as follows
Affine transformation preserves the “flatness” and “parallelism” of a two-dimensional graph (a straight line is still a straight line after affine transformation) (the relative position relation between lines remains unchanged, parallel lines are still parallel after affine transformation, and the position order of points on the line does not change). The three noncollinear pairs of corresponding points determine a unique affine transformation.
API
Compute the rotation matrix
public static Mat getRotationMatrix2D(Point center, double angle, double scale)
Copy the code
- Parameter 1: Center, the central position of image rotation
- Parameter 2: Angle, the Angle of image rotation, in degrees, and the positive value is counterclockwise rotation
- Parameter 3: scale, the scale factor of two axes, can realize image scaling in the rotation process, without scaling input 1
The generated rotation matrix is related to rotation Angle and rotation center.
For example, if scale is 2 and Angle is 90 degrees, m1 is the rotation matrix
Calculate affine matrix (three points)
public static Mat getAffineTransform(MatOfPoint2f src, MatOfPoint2f dst)
Copy the code
- Parameter 1: SRC, three pixel coordinates in the original image
- Parameter two: DST, three pixel coordinates in the target image
- Return value: 2*3 transformation matrix. Perspective transformation is a 3 by 3 matrix, affine is a 2 by 3 matrix
Affine transformation
public static void warpAffine(Mat src, Mat dst, Mat M, Size dsize, int flags, int borderMode, Scalar borderValue)
Copy the code
-
Parameter 1: SRC, original image
-
Parameter 2: DST, output image after perspective transformation, same data type as SRC, but same size as Dsize
-
Parameter 3: M, 2*3 transformation matrix
-
Parameter 4: Dsize, output image size
-
Parameter 5: flags, interpolation method 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;
Copy the code -
Parameter 6: borderMode, the flag of the pixel boundary extrapolation method. BORDER_CONSTANT or BORDER_REPLICATE
Boundary fill | value | role |
---|---|---|
BORDER_CONSTANT | 0 | Filled with specific values, such as iiiiii | abcdefgh | iiiiiii |
BORDER_REPLICATE | 1 | Replication fill on both ends, such as aaaaaa | abcdefgh | HHHHHHH |
BORDER_REFLECT | 2 | Flashback filling, such as fedcba | abcdefgh | hgfedcb |
BORDER_WRAP | 3 | Positive sequence filling, such as cdefgh | abcdefgh | abcdefg |
BORDER_REFLECT_101 | 4 | Does not contain boundary value flashbacks filling, gfedcb | abcdefgh | gfedcba |
BORDER_TRANSPARENT | 5 | Random fill, uvwxyz | abcdefgh | ijklmno |
BORDER_REFLECT101 | 4 | The same as the BORDER_REFLECT_101 |
BORDER_DEFAULT | 4 | The same as the BORDER_REFLECT_101 |
BORDER_ISOLATED | 16 | Don’t care about what’s outside the area of interest |
- Parameter seven: borderValue, the number used to fill the border, 0 by default
operation
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(a) {
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
}
}
Copy the code
The effect
The source code
Github.com/onlyloveyd/…
This article is formatted using MDNICE