在OpenCV中,仿射变换(Affine Transformation)和透视变换(Perspective Transformation)是两种常用的图像几何变换方法。
变换方法 | 适用场景 |
---|---|
仿射变换 | 简单的几何变换(平移、旋转、缩放、剪切)。 |
透视变换 | 改变图像视角和模拟3D投影效果。 |
变换方法 | 解释 | 特点 | 应用场景 | 实现方法 |
---|---|---|---|---|
仿射变换 | 仿射变换是一种线性变换,它保持了图像中直线的直线性和平行线的平行性。常见的仿射变换包括平移、旋转、缩放、剪切等。 | 输入空间和输出空间之间存在线性关系。 直线和平行性在变换后保持不变,但角度和长度可能发生改变。 |
图像平移、旋转或缩放。 图像对齐(如在模板匹配中的坐标对齐)。 简单的几何变形,如剪切变换。 |
准备变换矩阵(2x3)。 使用 OpenCV 的 cv2.warpAffine() 方法进行变换。 |
透视变换 | 透视变换是一种非线性变换,用于将图像从一个平面映射到另一个平面。它允许改变图像的视角,从而获得三维的透视效果。 | 输入空间和输出空间之间是非线性的。 直线保持直线,但平行线不再平行。 需要 4 对点来定义变换关系。 |
图像校正(如将拍摄的书本照片调整为平面图)。 视角转换(如模拟3D效果或鸟瞰视图)。 投影变换(如在增强现实中的投影映射)。 |
定义输入和输出平面上的 4 个对应点。 使用 cv2.getPerspectiveTransform() 获取 3x3 的透视变换矩阵。 使用 cv2.warpPerspective() 方法进行变换。 |
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.xugroupId>
<artifactId>KotlinOpenCVartifactId>
<version>1.0version>
<properties>
<kotlin.version>2.0.0kotlin.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<kotlin.code.style>officialkotlin.code.style>
<kotlin.compiler.jvmTarget>1.8kotlin.compiler.jvmTarget>
properties>
<repositories>
<repository>
<id>mavenCentralid>
<url>https://repo1.maven.org/maven2/url>
repository>
repositories>
<dependencies>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.8.29version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-compressartifactId>
<version>1.27.0version>
dependency>
<dependency>
<groupId>org.tukaanigroupId>
<artifactId>xzartifactId>
<version>1.10version>
dependency>
<dependency>
<groupId>org.jetbrains.kotlinxgroupId>
<artifactId>kotlinx-coroutines-coreartifactId>
<version>1.9.0-RCversion>
dependency>
<dependency>
<groupId>org.bytedecogroupId>
<artifactId>opencv-platformartifactId>
<version>4.10.0-1.5.11version>
dependency>
<dependency>
<groupId>org.jetbrains.kotlingroupId>
<artifactId>kotlin-test-junit5artifactId>
<version>2.0.0version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiterartifactId>
<version>5.10.0version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.jetbrains.kotlingroupId>
<artifactId>kotlin-stdlibartifactId>
<version>2.0.0version>
dependency>
dependencies>
<build>
<sourceDirectory>src/main/kotlinsourceDirectory>
<testSourceDirectory>src/test/kotlintestSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlingroupId>
<artifactId>kotlin-maven-pluginartifactId>
<version>2.0.0version>
<executions>
<execution>
<id>compileid>
<phase>compilephase>
<goals>
<goal>compilegoal>
goals>
execution>
<execution>
<id>test-compileid>
<phase>test-compilephase>
<goals>
<goal>test-compilegoal>
goals>
execution>
executions>
plugin>
<plugin>
<artifactId>maven-surefire-pluginartifactId>
<version>2.22.2version>
plugin>
<plugin>
<artifactId>maven-failsafe-pluginartifactId>
<version>2.22.2version>
plugin>
<plugin>
<groupId>org.codehaus.mojogroupId>
<artifactId>exec-maven-pluginartifactId>
<version>1.6.0version>
<configuration>
<mainClass>MainKtmainClass>
configuration>
plugin>
plugins>
build>
project>
package com.xu.com.xu.trans
import org.bytedeco.javacpp.Loader
import org.bytedeco.javacpp.Pointer
import org.bytedeco.opencv.global.opencv_core
import org.bytedeco.opencv.global.opencv_highgui
import org.bytedeco.opencv.global.opencv_imgcodecs
import org.bytedeco.opencv.global.opencv_imgproc
import org.bytedeco.opencv.opencv_core.Mat
import org.bytedeco.opencv.opencv_core.Point2f
import org.bytedeco.opencv.opencv_core.Rect
import org.bytedeco.opencv.opencv_core.Size
object Affine {
init {
Loader.load(opencv_core::class.java)
}
@JvmStatic
fun main(args: Array<String>) {
cropping1()
}
/**
* 仿射变换 平移变换
*
* @since 2025年1月20日12点33分
*/
private fun move() {
// 读取图像
val src = opencv_imgcodecs.imread("C:\\Users\\hyacinth\\Desktop\\1.png")
if (src == null || src.empty()) {
return
}
// 创建源点矩阵三个点
val mat1 = Mat(1, 3, opencv_core.CV_32FC2)
mat1.ptr(0, 0).put<Pointer>(Point2f(0f, 0f))
mat1.ptr(0, 1).put<Pointer>(Point2f(src.cols() - 1f, 0f))
mat1.ptr(0, 2).put<Pointer>(Point2f(0f, src.rows() - 1f))
// 创建目标点矩阵三个点
val mat2 = Mat(1, 3, opencv_core.CV_32FC2)
mat2.ptr(0, 0).put<Pointer>(Point2f(100f, 100f))
mat2.ptr(0, 1).put<Pointer>(Point2f(src.cols() + 100f, 100f))
mat2.ptr(0, 2).put<Pointer>(Point2f(100f, src.rows() + 100f))
// 获取旋转矩阵
val matrix = opencv_imgproc.getAffineTransform(mat1, mat2)
// 应用透视变换
val images = Mat()
opencv_imgproc.warpAffine(src, images, matrix, src.size())
// 显示结果
opencv_highgui.imshow("MOVE", images)
opencv_highgui.waitKey(0)
}
/**
* 仿射变换 旋转变换
*
* @since 2025年1月20日12点33分
*/
private fun revolve() {
// 读取图像
val src = opencv_imgcodecs.imread("C:\\Users\\hyacinth\\Desktop\\1.png")
if (src == null || src.empty()) {
return
}
// 旋转中心
val center = Point2f((src.cols() / 2).toFloat(), (src.rows() / 2).toFloat())
// 获取旋转矩阵
val matrix = opencv_imgproc.getRotationMatrix2D(center, 45.0, 0.5)
// 应用透视变换
val images = Mat()
opencv_imgproc.warpAffine(src, images, matrix, src.size())
// 显示结果
opencv_highgui.imshow("REVOLVE", images)
opencv_highgui.waitKey(0)
}
/**
* 仿射变换 图像缩放
*
* @since 2025年1月20日12点33分
*/
private fun zoom() {
// 读取图像
val src = opencv_imgcodecs.imread("C:\\Users\\hyacinth\\Desktop\\1.png")
if (src == null || src.empty()) {
return
}
// 旋转中心
val center = Point2f((src.cols() / 2).toFloat(), (src.rows() / 2).toFloat())
// 获取旋转矩阵
val matrix = opencv_imgproc.getRotationMatrix2D(center, 0.0, 0.5)
// 应用透视变换
val images = Mat()
opencv_imgproc.warpAffine(src, images, matrix, src.size())
// 显示结果
opencv_highgui.imshow("REVOLVE", images)
opencv_highgui.waitKey(0)
}
/**
* 仿射变换 图像裁剪
*
* @since 2025年1月20日12点33分
*/
private fun cropping1() {
// 读取图像
val src = opencv_imgcodecs.imread("C:\\Users\\hyacinth\\Desktop\\1.png")
if (src == null || src.empty()) {
return
}
// 定义裁剪区域
val rect = Rect(100, 100, 400, 200)
// 应用透视变换
val images = Mat(src, rect)
// 显示结果
opencv_highgui.imshow("CROPPING", images)
opencv_highgui.waitKey(0)
}
/**
* 仿射变换 图像裁剪
*
* @since 2025年1月20日12点33分
*/
private fun cropping1(type: Int) {
// 读取图像
val src = opencv_imgcodecs.imread("C:\\Users\\hyacinth\\Desktop\\1.png")
if (src == null || src.empty()) {
return
}
val dst = Mat()
opencv_imgproc.getRectSubPix(
src,
Size(400, 200), // 裁剪大小
Point2f((src.rows() / 2.0).toFloat(), (src.cols() / 2.0).toFloat()), // 裁剪图片中心
dst
)
// 显示ROI
opencv_highgui.imshow("src", src)
opencv_highgui.imshow("dst", dst)
opencv_highgui.waitKey(0)
}
}