摘要
使用rust的image
库,实现去畸变算法从而去除灰度图的畸变.
Use the image
library of Rust; manually implement the distortion removal method to remove the distortion of the grayscale image.
关键词
rust;image;
关键信息
[package]
name = "exp65-rust-ziglang-slambook2"
version = "0.1.0"
edition = "2021"
[dependencies]
env_logger = { version = "0.11.6", default-features = false, features = [
"auto-color",
"humantime",
] }
rand = "0.8.5"
# 线性代数
nalgebra = { version = "0.33.2",features = ["rand"]}
# winit
wgpu = "23.0.1"
winit = "0.30.8"
# egui
eframe = "0.30.0"
egui = { version = "0.30.0", features = [
"default"
]}
egui_extras = {version = "0.30.0",features = ["default", "image"]}
# three_d
three-d = {path = "./static/three-d" , features=["egui-gui"] }
three-d-asset = {version = "0.9",features = ["hdr", "http"] }
# sophus
sophus = { version = "0.11.0" }
sophus_autodiff = { version = "0.11.0" }
sophus_geo = "0.11.0"
sophus_image = "0.11.0"
sophus_lie = "0.11.0"
sophus_opt = "0.11.0"
sophus_renderer = "0.11.0"
sophus_sensor = "0.11.0"
sophus_sim = "0.11.0"
sophus_spline = "0.11.0"
sophus_tensor = "0.11.0"
sophus_timeseries = "0.11.0"
sophus_viewer = "0.11.0"
tokio = "1.43.0"
approx = "0.5.1"
bytemuck = "1.21.0"
thingbuf = "0.1.6"
# rust-cv
cv = { version = "0.6.0" , features = ["default"] }
cv-core = "0.15.0"
cv-geom = "0.7.0"
cv-pinhole = "0.6.0"
akaze = "0.7.0"
eight-point = "0.8.0"
lambda-twist = "0.7.0"
image = "0.25.5"
# 依赖覆盖
[patch.crates-io]
pulp = { path = "./static/pulp" }
原理简介
image库简介
[https://docs.rs/image/0.25.5/image/]
[https://github.com/image-rs/image]
This crate provides basic image processing functions and methods for converting to and from various image formats.
All image processing functions provided operate on types that implement the GenericImageView and GenericImage traits and return an ImageBuffer.
去畸变简介
去畸变的过程主要是通过纠正图像中的畸变来恢复原始的图像信息。畸变通常是由于相机镜头的几何形状引起的,主要包括径向畸变和切向畸变。以下是去畸变的主要数学原理:
1. 坐标转换
首先,将图像中的像素坐标 \((u, v)\) 转换为归一化相机坐标系下的坐标 \((x, y)\):
\[x = \frac{u - c_x}{f_x} \]\[y = \frac{v - c_y}{f_y} \]其中,\(f_x\) 和 \(f_y\) 是相机的焦距,\(c_x\) 和 \(c_y\) 是图像中心点的坐标。
2. 畸变模型
径向畸变
径向畸变是由于镜头的径向曲率引起的,可以用以下公式表示:
\[x_{\text{distorted}} = x(1 + k_1 r^2 + k_2 r^4) \]\[y_{\text{distorted}} = y(1 + k_1 r^2 + k_2 r^4) \]其中,\(r\) 是归一化坐标到原点的距离,\(r = \sqrt{x^2 + y^2}\),\(k_1\) 和 \(k_2\) 是径向畸变系数。
切向畸变
切向畸变是由于镜头和图像传感器不平行引起的,可以用以下公式表示:
\[x_{\text{distorted}} = x_{\text{distorted}} + 2p_1 x y + p_2(r^2 + 2x^2) \]\[y_{\text{distorted}} = y_{\text{distorted}} + p_1(r^2 + 2y^2) + 2p_2 x y \]其中,\(p_1\) 和 \(p_2\) 是切向畸变系数。
3. 反向映射
将畸变后的坐标 \((x_{\text{distorted}}, y_{\text{distorted}})\) 转换回像素坐标 \((u_{\text{distorted}}, v_{\text{distorted}})\):
\[u_{\text{distorted}} = f_x x_{\text{distorted}} + c_x \]\[v_{\text{distorted}} = f_y y_{\text{distorted}} + c_y \]4. 插值
最后,使用插值方法(如最近邻插值)将畸变图像中的像素值赋值给去畸变图像的对应位置。如果畸变后的坐标超出了图像边界,则将该位置的像素值设置为0。
实现
原始cpp代码:[https://github.com/gaoxiang12/slambook2/blob/master/ch5/imageBasics/undistortImage.cpp]
#include <opencv2/opencv.hpp>
#include <string>
using namespace std;
string image_file = "./distorted.png"; // 请确保路径正确
int main(int argc, char **argv) {
// 本程序实现去畸变部分的代码。尽管我们可以调用OpenCV的去畸变,但自己实现一遍有助于理解。
// 畸变参数
double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;
// 内参
double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;
cv::Mat image = cv::imread(image_file, 0); // 图像是灰度图,CV_8UC1
int rows = image.rows, cols = image.cols;
cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1); // 去畸变以后的图
// 计算去畸变后图像的内容
for (int v = 0; v < rows; v++) {
for (int u = 0; u < cols; u++) {
// 按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted)
double x = (u - cx) / fx, y = (v - cy) / fy;
double r = sqrt(x * x + y * y);
double x_distorted = x * (1 + k1 * r * r + k2 * r * r * r * r) + 2 * p1 * x * y + p2 * (r * r + 2 * x * x);
double y_distorted = y * (1 + k1 * r * r + k2 * r * r * r * r) + p1 * (r * r + 2 * y * y) + 2 * p2 * x * y;
double u_distorted = fx * x_distorted + cx;
double v_distorted = fy * y_distorted + cy;
// 赋值 (最近邻插值)
if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {
image_undistort.at<uchar>(v, u) = image.at<uchar>((int) v_distorted, (int) u_distorted);
} else {
image_undistort.at<uchar>(v, u) = 0;
}
}
}
// 画图去畸变后图像
cv::imshow("distorted", image);
cv::imshow("undistorted", image_undistort);
cv::waitKey();
return 0;
}
重构为rust:
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_imports)]
#![allow(unused_mut)]
#![allow(unused_assignments)]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#![allow(rustdoc::missing_crate_level_docs)]
#![allow(unsafe_code)]
#![allow(clippy::undocumented_unsafe_blocks)]
#![allow(unused_must_use)]
use image::{
GenericImageView, ImageBuffer, Rgba, RgbaImage,
DynamicImage, ImageFormat, imageops::FilterType
};
use std::time::Instant;
use rand::Rng;
fn main() {
// 畸变参数
let k1 = -0.28340811;
let k2 = 0.07395907;
let p1 = 0.00019359;
let p2 = 1.76187114e-05;
// 内参
let fx = 458.654;
let fy = 457.296;
let cx = 367.215;
let cy = 248.375;
// 读取图片
let image_path = "./assets/ch5-distorted.png";
let original_image = image::open(image_path).expect("failed to open image file");
let (rows, cols) = (original_image.height(), original_image.width());
let mut image_undistort = ImageBuffer::new(cols as u32, rows as u32);
// 计算去畸变后图像的内容
for v in 0..rows {
for u in 0..cols {
// 按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted)
let x = (u as f64 - cx) / fx;
let y = (v as f64 - cy) / fy;
let r = (x * x + y * y).sqrt();
let x_distorted = x * (1.0 + k1 * r * r + k2 * r * r * r * r) + 2.0 * p1 * x * y + p2 * (r * r + 2.0 * x * x);
let y_distorted = y * (1.0 + k1 * r * r + k2 * r * r * r * r) + p1 * (r * r + 2.0 * y * y) + 2.0 * p2 * x * y;
let u_distorted = fx * x_distorted + cx;
let v_distorted = fy * y_distorted + cy;
// 赋值 (最近邻插值)
if u_distorted >= 0.0 && v_distorted >= 0.0 && u_distorted < cols as f64 && v_distorted < rows as f64 {
let pixel = original_image.get_pixel(u_distorted as u32, v_distorted as u32);
image_undistort.put_pixel(u as u32, v as u32, pixel);
} else {
image_undistort.put_pixel(u as u32, v as u32, Rgba([0, 0, 0, 0]));
}
} // end for u
} // end for v
// 保存去畸变后的图像
image_undistort.save("./undistorted.png").unwrap();
}
效果
原图 | 去畸变 |
---|---|