折腾笔记[8]-使用rust去除灰度图的畸变
摘要
使用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. 坐标转换
首先,将图像中的像素坐标
其中,
2. 畸变模型
径向畸变
径向畸变是由于镜头的径向曲率引起的,可以用以下公式表示:
其中,
切向畸变
切向畸变是由于镜头和图像传感器不平行引起的,可以用以下公式表示:
其中,
3. 反向映射
将畸变后的坐标
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();
}
效果
原图 | 去畸变 |
---|---|
![]() |
![]() |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2023-01-20 stm32笔记[3]-OpenOCD调试