Rust和OpenCV-600学习网

600学习网终身会员188,所有资源无秘无压缩-购买会员

我们都知道为什么Rust这么好。然而,与C/C++和其他老巨人相比,它有点太新颖和闪亮了。我们经常需要在没有适当文档的情况下使用C++绑定。

出身背景

现在,让我们先回答这个问题。为什么我们关心在Rust中运行OpenCV?为什么不直接使用C++.Java或Python?

C++是一个古老的冠军。与Rust或Go相比,编译C++代码并不有趣。对于在Python中成长的年轻一代来说,使用C++安装包似乎是中世纪的。

谁愿意花时间安装软件包?特别是今天,有这么多优秀而有权势的人。Rust的包管理器Cargo很棒。

在Python中使用OpenCV很容易。易于在大型社区中安装和使用。如果你真的想把工作做好,Python是最好的选择。虽然Python语言非常慢,但实际上,很少有代码行是Python代码的主要特姓。

如果你只是想做一些需要for循环的额外函数呢?或者你想并行运行这些东西?Python可以做到这一点,但使用起来并不容易。

Rust的OpenCV

入门-安装(MacOS)慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

对于Mac用户,您可以遵循下面的简短教程。

让我们从安装OpenCV开始。不幸的是,OpenCV不是另一个Rust包。它要求在您的计算机上安装OpenCV(C++)。然而,在Rust中,没有必要痛苦地链接和编写CMake文件。Rust中的OpenCV实际上比C++更容易,当你想引入许多依赖项(大量的CMake文件)时,它不会让你头疼。

它很容易在macOS上安装。如果你有brew,你只需要运行:

brew安装opencv

然后加上你的货物。汤姆勒

〔依赖关系〕

opencv=”0.63.0″#或任何最新版本

您可以根据opencv信任存储库获得完整的安装帮助:

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

在安装它时,我们在编译过程中遇到了问题,但我们可以根据故障排除部分轻松修复它们。所以,如果你有问题,一定要在抓头发之前检查一下这个部位。

这个OpenCV Rust绑定到了C++API(这很好,因为C已经被放弃了)。

由于Rust可以直接与C接口,所以C++封装在一个额外的C层中,然后暴露于Rust。

简单代码

第一个例子是一个基于Makeitnow的视频教程:

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

这对于经验丰富的OpenCV用户来说非常简单。

使用任意方式处理结果:

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

因此将使用它来代替opencv::Result。

让我们来写代码!

无论如何使用::结果;//自动处理错误类型

使用opencv::{

序曲::*

视频

高gui

}; // 请注意,OpenCV的名称空间已更改(更好或更糟)。它不再是一个巨大的名称空间。

fn main()->结果<()>{//注意,无论如何::结果

//打开GUI窗口

highgui::命名为_window(“window”,highgui::window_FULLSCREEN)

//打开网络摄像机(假设你有一台)

让mut cam=videoio::VideoCapture::new(0,videoio::CAP _ ANY)

let mut frame=Mat::default();//此数组将存储网络摄像头数据

//读相机

//并在窗口中显示

循环{

cam.read(&mut frame)

highgui::imshow(“窗口”,&frame)

let key=highgui::wait_key(1)

如果键==113{//用q退出

打破

确定(())

太棒了!我们可以打开网络摄像头并将生成的帧放入帧变量。

代码应该是不言自明的。否则请观看视频!

PS:这将是等效的Python代码

导入cv2

vid=cv2.VideoCapture(0)

当为True时:

ret,帧=vid.read()

cv2.imshow(“窗口”,框架)

如果cv2.waitKey(1)和0xFF==ord(‘q’):

打破

视频发布()

cv2.销毁所有Windows()

使用OpenCV–Rust绑定变得更热

让我们:

·从文件读取图像

·使用SIFT&ORB检测关键点

·使用不同颜涩绘制关键点

·绘制矩形

·将图像转换为数组(快速)

·将数组转换为image::RgbImage(用于测试上述步骤是否按预期工作)

·保存图片

在这里,首先将代码作为一个块删除,然后逐步分解。

使用anyhow::anyhow

无论如何使用::Result

使用image::RgbImage

使用ndarray::{Array1,ArrayView1,Array View3}

使用opencv::{self作为cv,preparde::*}

fn main()->结果<()>{

//读取图像

让img=cv::imgcodecs::imread(“./assets/demo_img.png”,cv:;imgcodecs::imread_COLOR)

//使用球

让mut orb=<dyn cv::features2d::orb>::create(

500,

1.2,

8.

31,

0

2.

简历::features2d::ORB_分数类型::哈里斯_分数

31,

20,

)?;

让mut orb_关键点=cv::core::Vector::default()

让mut orb_desc=cv::core::Mat::default()

让mut dst_img=cv::core::Mat::default()

let mask=cv::core::Mat::default()

orb.检测和计算(&img,&mask,&mut orb_关键点,&mut-orb_desc,false)

cv::features2d::绘制_个关键点(

&img中

&orb_个关键点

&mut dst_ img

cv::core::VecN([0.,255.,0.,255.])

cv::features2d::DrawMatchesFlags::默认

)?;

cv::imgproc::矩形(

&mut dst_ img

cv::core::Rect::from_points(cv::core::Point::new(0,0),cv:core::Point::new(50,50))

cv::core::VecN([255.,0.,0.])

-1,

cv::imgproc::LINE_8

0

)?;

//使用SIFT

让mut sift=cv::features2d::sift::create(0,3,0.04,10.,1.6)

让mut sift_关键点=cv::core::Vector::default()

让mut sift_desc=cv::core::Mat::default()

sift。检测和计算(&img,&mask,&mut-sift,关键点,&mut sift,desc,false)

cv::features2d::绘制_个关键点(

&dst_img.clone()

筛选关键点

&mut dst_ img

cv::core::VecN([0.,0.,255.,255.])

cv::features2d::DrawMatchesFlags::默认

)?;

//使用OpenCV写入图像

cv::imgcodecs::imwrite(“./tmp.png”,&dst_img,&cv:核心::矢量::默认())

//转换::cv::core::Mat->ndarray::ArrayView3

设a=dst_img。try_as_array()

//转换::ndarray::ArrayView3->RgbImage

//注意,这需要拷贝,因为RgbImage将拥有数据

让test _ image=数组_到_ image(a)

//注意,颜涩将互换(BGR<->RGB)

//之前需要交换频道

//转换为RGBImage

//但由于这只是一个演示

//它确实可以转换cv::core::Mat->ndarray::ArrayView3

//我会听其自然

test_image.save(“out.png”)

确定(())

trait AsArray{

fn try_as_array(&self)->结果<ArrayView3<u8>>

cv::core::Mat的impl AsArray{

fn try_as_array(&self)->结果<ArrayView3<u8>>{

如果自我是连续的

return Err(无论如何!(“Mat不连续”)

let bytes=self.data_bytes()

let size=self.size()

让a=ArrayView3::from_shape((size.height as usize,size.width as usiz,3),bytes)

好的(a)

//来自堆栈溢出:https://stackoverflow.com/问题/56762026/如何-保存-ndarray-in-rust-as-image

fn数组_到_图像(arr:ArrayView3<u8>)->RgbImage{

明确肯定(arr.is_标准_布局())

let(高度,宽度,_)=arr.dim()

let raw=arr.to_slice().expect(“未能从数组中提取切片”)

RgbImage::从__raw(宽度为u32,高度为u32)到__vec()

.expect(“容器的尺寸应与图像尺寸相符”)

阅读图片

阅读图像非常简单。您可能需要检查所有这些图像是否已成功加载。如果找不到图像,OpenCV将不会抛出错误。不要被Rust的结果弄糊涂了。它不会检查图像是否正确加载。

//读取图像

让img=opencv::imgcodecs::imread(“./assets/demo_img.png”,cv:imgcodecs::imread_COLOR)

C++

cv::Mat I=cv:imread(“./assets/demo_img.png”,0)

蟒蛇

img:np.ndarray=cv2.imread(“./assets/demo_img.png)

关键点检测和绘制

ORB和SIFT代码非常相似,因此只对ORB部分进行注释。因此,首先创建检测器Rustlet mut orb=<dyn cv::features2d::orb>:create(500,1.2,8,31,0,2,cv:,features2d::orb_ScoreType::HARRIS_SCORE,31,20,)?;

C++

cv::Ptr<cv::ORB>orbPtr=cv:;ORB::create()

这与C++非常相似。需要提供一些不同的名称空间和参数。请注意,所有默认变量都可以在Rust的文档中找到。只需将鼠标悬停在”Create”函数上,您就会看到文档[VSCode]。

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

在VSCode中可以看到C++默认参数。如果您使用另一个IDE,您可以简单地转到函数定义并读取文档字符串

计算关键点

让mut orb_关键点=cv::core::Vector::default()让mut orb_desc=cv::core::Mat::default()orb.检测和计算(&img,&mask,&mut orb_关键点,&mut-orb_desc,false)

C++

std::vector<cv::KeyPoint>关键点

orbPtr->检测(图像.关键点)

cv::材料描述

orbPtr->计算(图像.关键点.描述)

再说一遍,没有太大区别。一开始,可能很难知道如何初始化密钥和描述符。但一旦你看到了它,就很容易了。从上面的代码来看,我们不能说Rust代码比C++更复杂。

绘制关键点

让mut dst_img=cv::core::Mat::default()

cv::features2d::绘制_个关键点(&img,&orb_keypoints,&mut dst _img,cv:核心::VecN([0.,255.,0.,255.]),cv::feature 2d::DrawMatchesFlags::默认,)

C++

cv::材料dst _ img

cv::drawKeypoints(图像,关键点,dst_img)

弄清楚Rust的类型有点棘手。使用类型推断和一点直觉来找到它!

侦探工作-画一个矩形(更具启发姓的步骤)

由于OpenCV Rust绑定的文档很少,所以它是一款侦探游戏。我决定展示C++代码是有原因的。

我们可以看到,大多数Rust代码都可以从C++中推断出来(在某种程度上)。有了今天令人难以置信的IDE,我们就有了机会。

此外,Rust有一个很好的文档系统。该策略取决于opencv信任文档

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

以及C++。因此,让我们使用这个策略来计算如何绘制矩形。

首先,我们应该弄清楚该做什么,即:

·绘制一个矩形(cv::C++中的矩形),然后转到opencv信任文档

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

现在,我们要做的就是找出类型(说起来容易做起来难)。在这里,我们将使用IDE(在我的例子中是VSCode)。使用LSP.Nvim或Emacs也可以。

第一个参数应该很明显,图像。这将是cv::core::Mat。我们可以通过直觉把它弄清楚。Mat是存储图像数据的默认类型,因此应该是Mat。Mat已经实现了ToInputOutputArray功能,一切都很好。

接下来,什么是Rect类型?

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

看来我们可以在核心找到它。那太好了。然而,我怎样才能建造一个Rect?

使用LSP,我们可以找到适当构造函数的自动完成。在这里,一点直觉和运气可以很快找到它(尽管与Rust相比,C++代码的完成远不容易掌握)。如果你被困在C++中,你通常会在网上找到解决方案,这不是很愉快。

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

好的,让我们看看from_points构造函数在这里说什么

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

我们需要两点但什么是点?????

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

核心看起来像这样!让LSP为我们做更多!

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

因此,”new”看起来很有前途,但”from_vec2″也是如此!我们可以使用其中的任何一个。但让我们选择”新”。

慕课、黑马、极客时间、小码哥、拉钩、尚硅谷、开课吧等千套课程打包VIP套餐,IT课程一网打尽

让我们在其中输入两个整数,看看会发生什么。现在我们已经找到了第二个参数

cv::core::Rect::from_points(

cv::core::Point::new(0,0)

cv::core::Point::new(50,50

)

(现在其他方法相同…)。这有点无聊,但一旦你开始了解类型,工作就会变得容易。在那个阶段,感觉很自然。您不会介意使用Rust而不是C++。

ndarray

看起来darray是Rust上最合适的矩阵库(对于为Python的NumPy包编写绑定的人来说,darray就是最好的选择)。还有一篇关于用Python和NumPy绑定Rust的文章!

这有点棘手。现在我们需要将C++类型转换为Rust类型。我们知道我们在处理矩阵类型。它们通常是第一行:https://en。维基百科。org/wiki/Row-_and_column-major_order

OpenCV Mat和数组数组(View)是行优先的。数据按顺序存储在底层缓冲区中。为了确保在我们的情况下,我们将检查Mat是否连续。

如果材料是连续的

return Err(无论如何!(“Mat不连续:(“))

我们可以使用这些知识将cv::Mat快速转换为array::array。

但是,必须注意,数组将指向Mat中存储的数据。因此,当Mat被丢弃时,数组也必须被丢弃。否则,我们(可能)指向释放的内存,这是糟糕的!但看起来Rust为我们处理了这件事!

首先,我们将提取Mat的数据字节。由于图像存储为8位(u8)无符号整数,我们可以直接读取数据而无需类型转换。

et data字节:&[u8]=mat.data字节()?;//<-这是按顺序排列的图像数据注意,它指向mat中的数据

接下来,我们需要计算出这些数据的大小。当我们得到数据字节时,我们并没有得到我们想要的形状,而是按照一个长的顺序。

let size=mat.size()

设h:i32=尺寸高度

设w:i32=size.width

现在我们可以构造ArrayView3

设a=ArrayView3::来自_形状((h为usize,w为usiz,3),数据为_字节)?;//3是因为我们有bgr。对于灰度图像,这将是1

good我们已经获得了一种将Mat转换为ArrayView3<u8>的方法。

请注意,这仅适用于连续数组。

作为一个功能,它看起来像

trait AsArray{

fn try_as_array(&self)->结果<ArrayView3<u8>>

cv::core::Mat的impl AsArray{

fn try_as_array(&self)->结果<ArrayView3<u8>>{

如果自我是连续的

return Err(无论如何!(“Mat不连续”)

let bytes=self.data_bytes()

let size=self.size()

让a=ArrayView3::from_shape((size.height as usize,size.width as usiz,3),bytes)

好的(a)

}}

要快速将Mat转换为Array,我们现在可以调用:

let array:ArrayView<u8>=mat.try_as_array()

结论

Rust中的OpenCV必须是可能的。它需要更深入的知识才能在不同类型之间进行转换,并且需要意志力来确定绑定。但它起作用了!

因为Rust包管理器Cargo非常好,它鼓励使用其他人的包(例如cv convert(https://crates.io/crates/cv转换),用于在许多流行的板条箱之间转换图像类型)。

我希望我们将来能看到更多的酷包。一些Rust GPU包已经开始出现。谁知道,将来有可能将Rust直接编译到SPIR-V中,以实现真正的快速计算!那将是多么美好的未来啊!

免责声明: 1、本站信息来自网络,版权争议与本站无关 2、本站所有主题由该帖子作者发表,该帖子作者与本站享有帖子相关版权 3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和本站的同意 4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责 5、用户所发布的一切软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。 6、您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。 7、请支持正版软件、得到更好的正版服务。 8、如有侵权请立即告知本站,本站将及时予与删除 9、本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章和视频仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。
600学习网 » Rust和OpenCV-600学习网