首页 > 其他分享 >折腾笔记[8]-使用rust去除灰度图的畸变

折腾笔记[8]-使用rust去除灰度图的畸变

时间:2025-01-20 23:56:16浏览次数:1  
标签:text image 畸变 let distorted sophus 灰度 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. 坐标转换

首先,将图像中的像素坐标 \((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();
}

效果

原图 去畸变

标签:text,image,畸变,let,distorted,sophus,灰度,rust
From: https://www.cnblogs.com/qsbye/p/18682672

相关文章

  • 05-rust结构体-struct
    在Rust中,结构体(struct)是定义数据类型和管理复杂数据的核心工具,类似于其他语言中的类,但它不支持内置继承。Rust提供了三种基本的结构体形式:常规结构体(regularstruct)、元组结构体(tuplestruct)和单元结构体(unitstruct)。下面详细讲解:1.常规结构体(RegularStruct)常规结构体是......
  • rust学习-函数的定义与使用
    rust学习-函数的定义与使用1.函数的基本定义2.函数的参数多个参数3.返回值提前返回4.函数调用5.函数的所有权和借用传递所有权借用6.函数作为参数和返回值函数作为参数函数作为返回值7.泛型函数8.函数注释(文档注释)1.函数的基本定义在Rust中,函数使用fn......
  • [rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(05):svg图片转为png格式(暨svg部件的
    前言本文是关于iced库的部件介绍,iced库是基于rust的GUI库,作者自述是受Elm启发。iced目前的版本是0.13.1,相较于此前的0.12版本,有较大改动。本合集是基于新版本的关于分部件(widget)的使用介绍,包括源代码介绍、实例使用等。环境配置系统:window10平台:visualstudiocode语言:rust......
  • 2025年编程语言热度分析:Python领跑,Go与Rust崛起
    TIOBEIndex(TIOBE编程语言指数)是一个衡量编程语言流行度的排名系统。它通过分析多种搜索引擎、在线编程社区、技术论坛、问答网站(如Google、Bing、Yahoo、Wikipedia、StackOverflow)等的搜索和讨论数据,评估不同编程语言的受欢迎程度。TIOBEIndex每月更新一次,并根据多个因......
  • 折腾笔记[6]-使用rust绘制三维画面
    摘要使用rust绘制界面;界面包含一个三维坐标轴,使用鼠标旋转坐标轴,左侧显示对应的旋转向量,四元数等信息.UseRusttodrawtheinterface;theinterfacecontainsathree-dimensionalcoordinateaxis,whichcanberotatedusingthemouse,andthecorrespondingrotati......
  • 详解Rust 中 String 和 str 的用途与区别
    文章目录1.基本定义1.1String1.2str2.存储位置与内存模型2.1String2.2str3.用法与区别4.使用场景4.1使用String的场景4.2使用str的场景5.String和str的关系6.代码示例分析6.1从&str创建String6.2从String获取&str6.3拼接字符串6.4静态......
  • rustdesk专用服务器/月付8元、京东BGP线路10M带宽
       今天给大家推荐一夸Rustdesk专用服务器(并不是官方专门为了为了搭建rustdesk出的套餐,而是根据rustdesk搭建要求作者找到的),因为这个额服务器的带宽10M,并且还是京东机房BGP线路,所以稳定性肯定没有问题。并且我搭建了这个服务器,经过测试自己使用有时候延迟30ms左右,甚至更低。......
  • 番外-rust 可变性与所有权、借用的关系
    在Rust中,可变性(mutability)、所有权(ownership)和借用(borrowing)是三个核心概念,它们在一起决定了如何在内存中管理数据、避免数据竞争以及确保程序的安全性。这些概念互相联系,共同维护了Rust的内存安全性。让我们逐一展开这些概念,并了解它们之间的关系。1.所有权(Ownership)所......
  • [rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(04):实现窗口主题(颜色)变换(暨menu菜单
    前言本文是关于iced库的部件介绍,iced库是基于rust的GUI库,作者自述是受Elm启发。iced目前的版本是0.13.1,相较于此前的0.12版本,有较大改动。本合集是基于新版本的关于分部件(widget)的使用介绍,包括源代码介绍、实例使用等。环境配置系统:window10平台:visualstudiocode语言:rust......
  • 番外-rust基础move_semantics-移动语义
    Rust中的movesemantics(移动语义)是其所有权系统的核心特性之一。它决定了数据的所有权如何在程序中转移以及如何在程序执行过程中确保内存安全。1.基本概念:所有权(Ownership)Rust的所有权系统要求每个值都有一个所有者,并且该值在同一时间只能有一个所有者。这就引入了所有权......