YOLO模型实现了快速目标识别的功能。 本文将介绍一下OpencvSharp 调用YOLO V5 模型的方法。
一.训练模型
模型训练使用github上的方法 https://github.com/ultralytics/yolov5 。相关项目里有着详细的介绍和使用方法说明
YOLO模型实现了快速目标识别的功能。 本文将介绍一下OpencvSharp 调用YOLO V5 模型的方法。
一.训练模型
模型训练使用github上的方法 https://github.com/ultralytics/yolov5 。相关项目里有着详细的介绍和使用方法说明
TorchSharp 是对 Torch c++的封装,基本继承了c++的全部接口。但使用中会有一些小问题,需要特别注意一些。
训练的代码可以参考github里的官方代码 https://github.com/pytorch/vision/tree/main/references/segmentation
2.模型输出
官方代码的模型 默认输出是list 虽然可以强制输出script文件,但TorchSharp 调用后会报错”Expected Tensor but got GenericDict”.因此需要修改网络
export代码如下:
import torch
import torchvision
from torch import nn
import numpy as np
class MyModel(nn.Module):
def __init__(self):
super().__init__()
self._model = torchvision.models.segmentation.lraspp_mobilenet_v3_large(num_classes=2,
aux_loss=False,
pretrained=False)
#修改了输入,将输入改为单通道图片
#如果输入的是3通道 则不需要修改
for item in self._model.backbone.items():
item[1][0] = nn.Conv2d(1, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
break
checkpoint = torch.load('model/model_119.pth', map_location='cpu')
self._model.load_state_dict(checkpoint['model'], strict=not True)
self._model.eval()
def forward(self, x):
# 修改了输出,将List修改为Tensor 并进行了 argmax 和 转float操作
result = self._model.forward(x)
return result["out"].argmax(1).flatten().float()
model = MyModel()
x = torch.rand(1,1, 240, 400)
predictions = model(x)
#使用torch.jit.trace 输出 script pt 网络文件
traced_script_module = torch.jit.trace(model, x)
traced_script_module.save('_seg.pt')
3.使用TorchSharp 进行预测
由于测试程序使用了opencvsharp,所以下面的代码使用了opencv来读取图片和简单的数据预处理,并使用opencv可视化输出
//导入网络
torch.jit.ScriptModule torch_model;
torch_model = torch.jit.load("_seg.pt");
//导入图片,并使用BlobFromImage 进行数据类型 维度 和归一化的转换
Mat temp = Cv2.ImRead("Pic_42633.bmp", ImreadModes.Grayscale);
Mat tensor_mat = OpenCvSharp.Dnn.CvDnn.BlobFromImage(temp, 1 / 255.0);
//初始化输入的tensor
float[] data_bytes = new float[tensor_mat.Total()];
Marshal.Copy(tensor_mat.Data, data_bytes, 0, data_bytes.Length);
torch.Tensor x = torch.tensor(data_bytes, torch.ScalarType.Float32);
//维度转换 和 normalize(进行normalize是因为官方代码里有这一步处理)
x = x.reshape(1, 1, 240, 400);
x = TorchSharp.torchvision.transforms.functional.normalize(x, new double[] { 0.485 }, new double[] { 0.229 });
DateTime date1 = DateTime.Now;
//进行预测
torch.Tensor _out = torch_model.forward(x);
DateTime date2 = DateTime.Now;
TimeSpan ts = date2 - date1;
Console.WriteLine("No. of Seconds (Difference) = {0}", ts.TotalMilliseconds);
Console.WriteLine(_out);
//使用opencv 将预测出的tensor输出为可视化的图片
Mat result_mat = new Mat(240, 400, MatType.CV_32FC1, _out.bytes.ToArray());
result_mat.ConvertTo(result_mat, MatType.CV_8UC1);
Cv2.ImWrite("mask.bmp", result_mat);
opencv只有cpu接口的Inpaint函数,对于需要使用CUDA进行图片处理时,反复内存显存迁移数据会影响计算速度。在不考虑填充效果十分好的情况下,可以使用如下CUDA算法,简单的进行填充。
算法来源
算法主要参考了https://github.com/Po-Ting-lin/HairRemoval.git 中的填充代码。该项目主要是去除皮肤上的毛发。对于其如何寻找需要填充的区域就不讨论了,直接使用其分析出的mask图进行填充。
该填充算法对细线和小面积的填充效果还可以 大面积的 效果就很一般了
继续阅读
opencv中有cpu 和 gpu版本的DFT函数,及傅里叶变换的函数,可以实现dft滤波。但opencv中没有DWT,及小波变换。下面将介绍一下实现的方法。
傅里叶变换与小波变换都能实现滤波,不好说那个更好。但傅里叶变换有个缺点,对于图像处理来说,其在处理图像锐利边缘时,很容易出现边缘抖动的情况。原因如下图:

傅里叶变换只能使用余弦波,很难拟合出突变。具体的解释和小波变换的特点。可以参考知乎上的文章 https://zhuanlan.zhihu.com/p/44215123
代码部分主要参考了GitHub上实现方法,经过一些简单的修改以适应vc++,和高频滤波的需要。原链接https://github.com/pierrepaleo/PDWT
继续阅读
树莓派可以通过加入 参数 “bayer=True” ,来获取CSI相机的raw图片,raw数据会紧跟在jpg图片的末尾,具体提取的方法不在累述,可以方便的google到。而Nvidia的 Jetson nano要获取raw图片的方法网上比较零散,故整理了一下。
获取raw图片的方法其实比较简单,使用的是jetson nano中自带的Libargus api。其api介绍可以参考https://docs.nvidia.com/jetson/l4t-multimedia/group__LibargusAPI.html,api架构可以参考https://docs.nvidia.com/jetson/archives/l4t-archived/l4t-3231/index.html#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/jetson_xavier_camera_soft_archi.html
相关代码在 Libargus api demo中的 argus\samples\oneShot 简单修改而来。并增加了转换为opencvmat的代码
继续阅读
opencv中的cuda模块封装了大部分常用的图像处理函数。但一些函数只提供了8bit图片的接口,没有16bit图片的接口。如果需要处理10bit 12bit或更高big的图片就需要调用CUDA的原型函数了。下面就简单举例使用opencv中的GpuMat调用cuda原函数的方法。
opencv中存储GPU图片的类型为GpuMat,不需要考虑显存的分配和释放,使用起来比较方便。而CUDA使用的是Npp8u* Npp16u*等指针,这里就涉及指针的转换。以nppiLUTPalette_16u_C1R 函数举例。
Mat img = ….. //读取一张12bit图片
cuda::GpuMat src_img; //初始化Gpu
src_img.upload(img); //上传图片到Gpu
Mat lut(1, 4096, CV_16UC1, lineardata.data()); // 导入lut数据
cuda::GpuMat gpu_lut = new cv::cuda::GpuMat(1, 4096, CV_16UC1);
gpu_lut.upload(lut); //上传lut数据到Gpu,lut数据也要放入Gpu中
cuda::GpuMat dst_gpu(src_img.size(), CV_16UC1); //初始化转化后的GpuMat
NppiSize oSizeROI = { src_img.cols, src_img.rows }; //设置ROI 这里是整张图
//使用prt获取GpuMat的指针
NppStatus status1 = nppiLUTPalette_16u_C1R(src_img.ptr(), static_cast(src_img.step), dst_gpu.ptr(), static_cast(dst_gpu.step), oSizeROI, gpu_lut.ptr(),16);
得到的dst_gpu就是经过lut后的图片,后续的使用也不需要考虑显存的分配和释放。
opencv源代码中包含了dft的demo,但没有使用cuda的demo。下文会简单给出一个cuda例程,并进行简单的高频滤波。
为方便说明,将程序分成了若干部分
1.头文件
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/core/cuda.hpp>
#include <opencv2/cudaarithm.hpp>
using namespace cv;
2.导入图片加载到GPU,为简单使用了单色图

opencv从3版本开始就已经支持CNN网络模型的预测,到4版本,主流工具tensorflow,pytorch 生成的模型文件大部分都可以支持。但其一直没有使用到CUDNN。但最新发布的4.2版本的opencv已经支持CUDNN了。以下是功能测试
一。安装编译环境
CUDNN的安装方法可以参考tensorflow中关于cudnn的安装方法https://www.tensorflow.org/install/gpu 接下来也会使用tensorflow作为例子。
二。编译opencv
编译方法参考链接https://docs.opencv.org/4.2.0/d3/d52/tutorial_windows_install.html 其中为了加入CUDNN的支持,有以下需要设置一下
1.勾选cuda和cudnn

2.设置CUDA_ARCH_BIN
cudnn不支持3.5下的显卡。需要根据自己使用的显卡来设置,列表参考https://developer.nvidia.com/cuda-gpus

网上可以找到许多TensorFlow的Mask-RCNN版本,现以github上一个Star较多的版本为例,介绍如何在C#中部署Mask-RCNN模型
1.同步Mask-RCNN的代码,地址为 https://github.com/matterport/Mask_RCNN.git
2.训练模型:
可以使用jupyter打开samples文件夹下的例子进行训练,也可以参考其例子训练自己的模型。目标是生存保存好权重的模型文件。模型每训练一个epochs 会在 model_dir=MODEL_DIR 设置好的文件夹下留下模型文件。具体训练方法就不累述了~~~
3.在VS里使用Nuget,给项目添加OpencvSharp 与 TensorflowSharp
继续阅读opencvsharp 是 opencv的c#版本,近期有项目使用了opencvsharp来进行图像处理。这个github上星级很高的项目果然是不错的,运行起来比较稳定,没有出现大的问题。但opencvsharp中没有cuda的完整支持,只有最基本的类型支持,无任何算法支持,想用就只能靠自己添加了。作者的解释如下:

大概就是说cuda需要用户自己编译opencv ,没有一个统一版本的dll提供使用,所以就删除了cuda的支持
其实也对,cuda的使用涉及cuda版本,使用的显卡算力等。使用c++版本的opencv时,也是要自己编译的。
但c#上使用就没有办法了吗?还好作者已经打好了基础,提供了GpuMat的支持,并有大量cup版本的函数进行参考。添加起来还是比较容易的。
项目目录 https://github.com/shimat/opencvsharp