折腾笔记[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. 坐标转换

首先,将图像中的像素坐标 (u,v) 转换为归一化相机坐标系下的坐标 (x,y)

x=ucxfx

y=vcyfy

其中,fxfy 是相机的焦距,cxcy 是图像中心点的坐标。

2. 畸变模型

径向畸变

径向畸变是由于镜头的径向曲率引起的,可以用以下公式表示:

xdistorted=x(1+k1r2+k2r4)

ydistorted=y(1+k1r2+k2r4)

其中,r 是归一化坐标到原点的距离,r=x2+y2k1k2 是径向畸变系数。

切向畸变

切向畸变是由于镜头和图像传感器不平行引起的,可以用以下公式表示:

xdistorted=xdistorted+2p1xy+p2(r2+2x2)

ydistorted=ydistorted+p1(r2+2y2)+2p2xy

其中,p1p2 是切向畸变系数。

3. 反向映射

将畸变后的坐标 (xdistorted,ydistorted) 转换回像素坐标 (udistorted,vdistorted)

udistorted=fxxdistorted+cx

vdistorted=fyydistorted+cy

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();
}

效果

原图 去畸变
posted @   qsBye  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
历史上的今天:
2023-01-20 stm32笔记[3]-OpenOCD调试
点击右上角即可分享
微信分享提示