使用基于ONNXRuntime的Lite.AI.ToolKit 🚀🚀🌟 C++工具箱来跑RobustVideoMatting的一些案例

Overview

RobustVideoMatting.lite.ai.toolkit 🚀 🚀 🌟

1. 简介

使用Lite.AI.ToolKit C++工具箱来跑RobustVideoMatting的一些案例(https://github.com/DefTruth/lite.ai.toolkit) .


整理不易,欢迎关注, 🌟 点赞收藏~

2. C++版本源码

RobustVideoMatting C++ 版本的源码是用ONNXRuntime的C++ API实现的,可以在 lite.ai.toolkit 工具箱中找到。本项目主要介绍如何基于 lite.ai.toolkit 工具箱,直接使用RobustVideoMatting实现视频抠图和图片抠图。需要说明的是,本项目是基于MacOS下编译的 liblite.ai.toolkit.v0.1.0.dylib 来实现的,对于使用MacOS的用户,可以直接下载本项目包含的liblite.ai.toolkit.v0.1.0动态库和其他依赖库进行使用。而非MacOS用户,则需要从lite.ai.toolkit 中下载源码进行编译。lite.ai.toolkit c++工具箱的编译已经在MacOS/Linux/Windows下编译测试通过,支持CPU和GPU环境,目前包含70+流行的开源模型。

3. 模型文件

可以从我提供的链接下载 (Baidu Drive code: 8gin) , 也可以从 RobustVideoMatting 官方仓库下载。

Class Pretrained ONNX Files Rename or Converted From (Repo) Size
lite::cv::matting::RobustVideoMatting rvm_mobilenetv3_fp32.onnx RobustVideoMatting 14Mb
lite::cv::matting::RobustVideoMatting rvm_mobilenetv3_fp16.onnx RobustVideoMatting 7.2Mb
lite::cv::matting::RobustVideoMatting rvm_resnet50_fp32.onnx RobustVideoMatting 50Mb
lite::cv::matting::RobustVideoMatting rvm_resnet50_fp16.onnx RobustVideoMatting 100Mb

4. 接口文档

lite.ai.toolkit 中,RobustVideoMatting的实现类为:

class LITE_EXPORTS lite::cv::matting::RobustVideoMatting;

该类型目前包含两个公共接口,分别是detectdetect_video,前者用于图像抠图,后者用于视频抠图。

     /**
     * Image Matting Using RVM(https://github.com/PeterL1n/RobustVideoMatting)
     * @param mat: cv::Mat BGR HWC
     * @param content: types::MattingContent to catch the detected results.
     * @param downsample_ratio: 0.25 by default.
     * See https://github.com/PeterL1n/RobustVideoMatting/blob/master/documentation/inference_zh_Hans.md
     */
    void detect(const cv::Mat &mat, types::MattingContent &content,
                float downsample_ratio = 0.25f);
    /**
     * Video Matting Using RVM(https://github.com/PeterL1n/RobustVideoMatting)
     * @param video_path: eg. xxx/xxx/input.mp4
     * @param output_path: eg. xxx/xxx/output.mp4
     * @param contents: vector of MattingContent to catch the detected results.
     * @param save_contents: false by default, whether to save MattingContent.
     * @param downsample_ratio: 0.25 by default.
     * See https://github.com/PeterL1n/RobustVideoMatting/blob/master/documentation/inference_zh_Hans.md
     * @param writer_fps: FPS for VideoWriter, 20 by default.
     */
    void detect_video(const std::string &video_path,
                      const std::string &output_path,
                      std::vector
    &contents,
                      
   bool save_contents = 
   false,
                      
   float downsample_ratio = 
   0.
   25f,
                      
   unsigned 
   int writer_fps = 
   20);
  
  • detect接口输入参数说明:

    • mat: cv::Mat BGR格式图像

    • content: types::MattingContent类型,用来保存检测的结果,包含类型为cv::Mat的三个成员,分别是

      • fgr_mat: cv::Mat (H,W,C=3) BGR 格式,值范围为0~255 的 CV_8UC3, 用于保存估计的前景
      • pha_mat: cv::Mat (H,W,C=1) 值范围为0.~1.的 CV_32FC1, 用于保存估计的alpha(matte)值
      • merge_mat: cv::Mat (H,W,C=3) BGR 格式,值范围为0~255 的 CV_8UC3, 用于保存根据pha融合前景背景的合成图像
      • flag: bool 类型标志位,表示是否检测成功
    • downsample_ratio: float,下采样比率,默认0.25f,值的设置可以参考官方文档 , 如下:

      分辨率 人像 全身
      <= 512x512 1 1
      1280x720 0.375 0.6
      1920x1080 0.25 0.4
      3840x2160 0.125 0.2

      模型在内部将高分辨率输入缩小做初步的处理,然后再放大做细分处理。 建议设置 downsample_ratio 使缩小后的分辨率维持在 256 到 512 像素之间. 例如,1920x1080 的输入用 downsample_ratio=0.25,缩小后的分辨率 480x270 在 256 到 512 像素之间。 根据视频内容调整 downsample_ratio。若视频是上身人像,低 downsample_ratio 足矣。若视频是全身像,建议尝试更高的 downsample_ratio。但注意,过高的 downsample_ratio 反而会降低效果。

  • detect_video接口输入参数说明:

    • video_path: string, 输入的视频路径
    • output_path: string, 输出的视频路径
    • contents:MattingContent类型的vector,用来保存每帧检测的结果
    • save_contents:bool,是否保存每一帧的结果,默认false。当分辨率很大时,保存所有的结果将会占用非常多内存
    • downsample_ratio: float,下采样比率,默认0.25f,同上。
    • writer_fps:int 视频写出的帧率,默认20

5. 使用案例

这里测试使用的是mobilenetv3版本的rvm模型,如果你使用resnet50版本的模型,将会得到更高精度的结果。

5.1 图像抠图案例

detect(img_bgr, content, 0.25f); if (content.flag) { if (!content.fgr_mat.empty()) cv::imwrite(save_fgr_path, content.fgr_mat); // 预测的前景fgr if (!content.pha_mat.empty()) cv::imwrite(save_pha_path, content.pha_mat * 255.); // 预测的前景pha if (!content.merge_mat.empty()) cv::imwrite(save_merge_path, content.merge_mat); // 合成图 } delete rvm; } ">
#include "lite/lite.h"

// Image Matting Interface
static void test_image()
{
  std::string onnx_path = "../hub/onnx/cv/rvm_mobilenetv3_fp32.onnx";
  std::string img_path = "../examples/lite/resources/test.jpg";
  std::string save_fgr_path = "../logs/test_lite_rvm_fgr.jpg";
  std::string save_pha_path = "../logs/test_rvm_pha.jpg";
  std::string save_merge_path = "../logs/test_lite_rvm_merge.jpg";

  auto *rvm = new lite::cv::matting::RobustVideoMatting(onnx_path, 16); // 16 threads
  lite::cv::types::MattingContent content;
  cv::Mat img_bgr = cv::imread(img_path);

  // 1. image matting.
  rvm->detect(img_bgr, content, 0.25f);

  if (content.flag)
  {
    if (!content.fgr_mat.empty()) cv::imwrite(save_fgr_path, content.fgr_mat); // 预测的前景fgr
    if (!content.pha_mat.empty()) cv::imwrite(save_pha_path, content.pha_mat * 255.); // 预测的前景pha
    if (!content.merge_mat.empty()) cv::imwrite(save_merge_path, content.merge_mat); // 合成图
  }
  
  delete rvm;
}
  • 输出结果为: (依次为原图、预测的pha、预测的前景fgr、合成图)

5.2 视频抠图案例

contents; // 1. video matting. rvm->detect_video(video_path, output_path, contents, false, 0.4f); delete rvm; } ">
#include "lite/lite.h"

// Video Matting Interface
static void test_video()
{
  std::string onnx_path = "../hub/onnx/cv/rvm_mobilenetv3_fp32.onnx";
  std::string video_path = "../examples/lite/resources/tesla.mp4";
  std::string output_path = "../logs/tesla_onnx.mp4";

  auto *rvm = new lite::cv::matting::RobustVideoMatting(onnx_path, 16); // 16 threads
  std::vector
    contents;

  
   // 1. video matting.
  rvm->
   detect_video(video_path, output_path, contents, 
   false, 
   0.
   4f);

  
   delete rvm;
}

  
  • 输出结果为:

6. 编译运行

在MacOS下可以直接编译运行本项目,无需下载其他依赖库。其他系统则需要从lite.ai.toolkit 中下载源码先编译lite.ai.toolkit.v0.1.0动态库。

git clone --depth=1 https://github.com/DefTruth/RobustVideoMatting.lite.ai.toolkit.git
cd RobustVideoMatting.lite.ai.toolkit 
sh ./build.sh
  • building && testing information:
-- Generating done
-- Build files have been written to: /Users/xxx/Desktop/xxx/RobustVideoMatting.lite.ai.toolkit/examples/build
[ 50%] Building CXX object CMakeFiles/lite_rvm.dir/examples/test_lite_rvm.cpp.o
[100%] Linking CXX executable lite_rvm
[100%] Built target lite_rvm
Testing Start ...
Load ../hub/onnx/cv/rvm_mobilenetv3_fp32.onnx done!
write done! 1/774 done!
write done! 2/774 done!
write done! 3/774 done!
write done! 4/774 done!
write done! 5/774 done!
write done! 6/774 done!
...
write done! 724/774 done!
Testing Successful !

Comments
  • windows下编译失败(lite.tool.kit已编译好)

    windows下编译失败(lite.tool.kit已编译好)

    作者大佬,您好,我已经成功编译了lite.tool.kit,lite.ai.toolkit\build\lite.ai.toolkit\bin\lite_rvm应该也能正常运行,然后用build/lite.ai.toolkit替换了RobustVideoMatting.lite.ai.toolkit/lite.ai.toolkit,也修改了CMakeLists,像在linux一样也配置了环境变量 export LD_LIBRARY_PATH=./lite.ai.toolkit/lib:$LD_LIBRARY_PATH。但是sh ./build.sh的时候还是报错?您能看看哪里错了嘛? image

    opened by Richardlyq 8
  • About Linux compile

    About Linux compile

    The lite.ai.toolkit has been build from Lite.AI.ToolKit. When build this RVM demo with only onnxruntime, it looks like that executable lite_rvm has beem built, but Segmentation fault comes from error of version? Some advice?

    Attached below:

    [100%] Linking CXX executable lite_rvm [100%] Built target lite_rvm Testing Start ... The given version [9] is not supported, only version 1 to 8 is supported in this build. Segmentation fault (core dumped) Testing Successful !

    opened by LuoYingzhao 2
  • 摄像头实时视频流推理时速度越来越慢且发现内存泄漏?

    摄像头实时视频流推理时速度越来越慢且发现内存泄漏?

    你好,请教一下,我用于摄像头实时视频流推理时,随着时间的推移时速度会变慢,虽然没有观察到内存有明显的增长,但当我使用Visual Leak Detector工具检测时发现内存有一处泄漏(如下所示),问题似乎出现在opencv?谢谢。 WARNING: Visual Leak Detector detected memory leaks! ---------- Block 48 at 0x000000002E664860: 256 bytes ---------- Leak Hash: 0x3299BB0A, Count: 1, Total 256 bytes Call Stack (TID 3656): msvcrt.dll!malloc() (Module name unavailable)!Ordinal1040() + 0x5F bytes (Module name unavailable)!Ordinal1040() + 0x22C bytes (Module name unavailable)!Ordinal1040() + 0x3D5 bytes ntdll.dll!RtlActivateActivationContextUnsafeFast() + 0x11D bytes ntdll.dll!LdrGetProcedureAddressEx() + 0x2D7 bytes ntdll.dll!LdrGetProcedureAddressEx() + 0x6A bytes ntdll.dll!LdrGetProcedureAddressEx() + 0xF0 bytes ntdll.dll!RtlSwitchedVVI() + 0xD07 bytes ntdll.dll!RtlGetFullPathName_UstrEx() + 0x231E bytes ntdll.dll!RtlDosPathNameToNtPathName_U() + 0xD4 bytes ntdll.dll!LdrLoadDll() + 0xE4 bytes DtFrame64.dll!EATUninstallRaw() + 0x53B22 bytes KERNELBASE.dll!LoadLibraryExW() + 0x162 bytes TSafeDoc64.dll!0x00007FFFEF7D563C() TSafeDoc64.dll!0x00007FFFEF7CA0F3() winhadnt64.dll!InitUPDLShareMemory() + 0x41230 bytes DtFrame64.dll!EATUninstallRaw() + 0x3C129 bytes combase.dll!StringFromGUID2() + 0xB0DD bytes combase.dll!StringFromGUID2() + 0xB026 bytes combase.dll!StringFromGUID2() + 0xADF8 bytes combase.dll!StringFromGUID2() + 0xAA3A bytes combase.dll!CoCreateInstance() + 0x2C30 bytes combase.dll!Ordinal67() + 0xB5D bytes combase.dll!Ordinal67() + 0x4D6 bytes combase.dll!InternalCoIsSurrogateProcess() + 0x6DD0 bytes combase.dll!CoTaskMemRealloc() + 0x5E7C bytes combase.dll!CoDisableCallCancellation() + 0x128 bytes combase.dll!Ordinal167() + 0x500 bytes combase.dll!CoTaskMemRealloc() + 0x2A31 bytes combase.dll!CoDisableCallCancellation() + 0x4E0 bytes combase.dll!InternalCoIsSurrogateProcess() + 0x6DD0 bytes combase.dll!InternalCoIsSurrogateProcess() + 0x303F bytes combase.dll!InternalCoIsSurrogateProcess() + 0x6DD0 bytes combase.dll!CoCreateInstance() + 0x190A bytes combase.dll!CoCreateInstance() + 0x3BA bytes combase.dll!CoCreateInstance() + 0x10C bytes winhadnt64.dll!InitUPDLShareMemory() + 0x43458 bytes DtFrame64.dll!EATUninstallRaw() + 0x5E5FF bytes (Module name unavailable)!DllCanUnloadNow() + 0xE84D bytes (Module name unavailable)!DllCanUnloadNow() + 0xF2F0 bytes (Module name unavailable)!DllCanUnloadNow() + 0x1F66 bytes (Module name unavailable)!DllCanUnloadNow() + 0x125D bytes (Module name unavailable)!0x00007FFFB5BB3F5D() MFCORE.DLL!MFCreateDeviceSource() + 0x490 bytes (Module name unavailable)!DllUnregisterServer() + 0xDA8 bytes (Module name unavailable)!DllUnregisterServer() + 0x6C6 bytes (Module name unavailable)!0x00007FFFB91A39F8() (Module name unavailable)!DllUnregisterServer() + 0x21C2 bytes (Module name unavailable)!DllUnregisterServer() + 0x24BF bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x3171E18 bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x3178DF3 bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x3178371 bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x31784DB bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x3174AFB bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x316D722 bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x316CAAD bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x316D181 bytes opencv_world455d.dll!cv::Matx_SubOp::operator=() + 0x316D0A9 bytes Data: F0 11 EC 8A FF 7F 00 00 E0 11 EC 8A FF 7F 00 00 ........ ........ 30 12 EC 8A FF 7F 00 00 90 12 EC 8A FF 7F 00 00 0....... ........ 80 13 EC 8A FF 7F 00 00 90 13 EC 8A FF 7F 00 00 ........ ........ F0 13 EC 8A FF 7F 00 00 90 14 EC 8A FF 7F 00 00 ........ ........ B0 12 EC 8A FF 7F 00 00 00 A7 EC 8A FF 7F 00 00 ........ ........ D0 12 EC 8A FF 7F 00 00 40 14 EC 8A FF 7F 00 00 ........ @....... 60 14 EC 8A FF 7F 00 00 B0 14 EC 8A FF 7F 00 00 `....... ........ D0 14 EC 8A FF 7F 00 00 F0 14 EC 8A FF 7F 00 00 ........ ........ 20 15 EC 8A FF 7F 00 00 E0 15 EC 8A FF 7F 00 00 ........ ........ 40 10 EC 8A FF 7F 00 00 50 10 EC 8A FF 7F 00 00 @....... P....... 30 10 EC 8A FF 7F 00 00 E0 A6 EC 8A FF 7F 00 00 0....... ........ C0 A6 EC 8A FF 7F 00 00 F0 A6 EC 8A FF 7F 00 00 ........ ........ D0 A6 EC 8A FF 7F 00 00 E0 64 EC 8A FF 7F 00 00 ........ .d...... B0 64 EC 8A FF 7F 00 00 F0 12 EC 8A FF 7F 00 00 .d...... ........ A0 13 EC 8A FF 7F 00 00 B0 13 EC 8A FF 7F 00 00 ........ ........ 00 66 EC 8A FF 7F 00 00 20 14 EC 8A FF 7F 00 00 .f...... ........

    opened by li-wenquan 1
  • Macos下编译失败

    Macos下编译失败

    作者,您好,查看了一下,上面都是一样的,但是下面多出了很多东西。 Macos下编译出错 还有,您说的把ffmpeg的库和opencv的库放在一起,是将ffmpeg下的lib文件放在RobustVideoMatting.lite.ai.toolkit\lite.ai.toolkit\lib里面嘛?在macos也要这样嘛?macos下我看您就写了两行代码呀,一行是git clone,一行是sh ./build.sh呀,还需要其他什么操作呀?(小白,望大佬多包含,~ ~)

    opened by Richardlyq 1
  • ./build.sh: line 10: 56666 Illegal instruction: 4  ./lite_rvm

    ./build.sh: line 10: 56666 Illegal instruction: 4 ./lite_rvm

    我想把 lite.ai.toolkit 打包成一个库,在我的 mac app 中使用,我 git 下来项目后执行 sh ./build.sh 报错,./build.sh: line 10: 56666 Illegal instruction: 4 ./lite_rvm, 我的最终目的是想把 lite.ai.toolkit 打包成一个库,能直接放到我的 xcode 项目中使用,我对 CMakeLists 不熟悉,还是得请教一下您,怎么实现我的最终目标。

    opened by Tang-Hai 1
  • Running de FP16 version

    Running de FP16 version

    Thank you for your great work.

    I would like to run the FP16 with CUDA execution provider.

    The model rvm_mobilenetv3_fp16.onnx seems to load without problem but when running the inference: auto output_tensors = ort_session->Run(Ort::RunOptions{ nullptr }, input_node_names.data(), input_tensors.data(), num_inputs, output_node_names.data(), num_outputs);

    I get an exception.

    Do you think that it may be necessary adapt the "create_tensor" helper functions to change the CV_32F opencv mats with CV_16F or make any other change to run the FP16 version?

    Best regards.

    opened by livingbeams 1
  • Issue for extracting fgr and phr from NCNN model

    Issue for extracting fgr and phr from NCNN model

    Hello How are you? Thanks for contributing to this project. I am going to use your ncnn version of rvm model on Windows C++. But there is an issue when extracting fgr and phr from your any NCNN model. I have a question. Which version of NCNN did u use? Could u check this again? Thanks

    opened by rose-jinyang 0
  • 模型转换的问题

    模型转换的问题

    你好,我看官方提供了TensorFlow ,Pytorch,ONNX的模型文件,然后MNN也提供了用MNNConvert转模型的方法。 请问是如何转成rvm_mobilenetv3_fp32-480-640.mnn这种480*640的模型的呢,不知道是模型转换的哪个参数呢? 然后我看模型转换里面还有--fp16 可以将将conv/matmul/LSTM的float32参数保存为float16,模型将减小一半,精度基本无损。这是不是意味着可以用--fp16参数,然后得到的结果也就是16位精度的结果呢?

    例如我现在想弄一个rvm_mobilenetv3_fp32-360-480.mnn,请问该输入什么参数呢?

    image

    opened by yyl9510 5
Owner
DefTruth
保持学习 ☕️😜🙃
DefTruth
Lite.AI.ToolKit 🚀🚀🌟: A lite C++ toolkit of awesome AI models such as RobustVideoMatting🔥, YOLOX🔥, YOLOP🔥 etc.

Lite.AI.ToolKit ?? ?? ?? : A lite C++ toolkit of awesome AI models which contains 70+ models now. It's a collection of personal interests. Such as RVM, YOLOX, YOLOP, YOLOR, YoloV5, DeepLabV3, ArcFace, etc.

DefTruth 2.3k Nov 29, 2022
Zenotech 6 Oct 21, 2022
Insight Toolkit (ITK) is an open-source, cross-platform toolkit for N-dimensional scientific image processing, segmentation, and registration

ITK: The Insight Toolkit C++ Python Linux macOS Windows Linux (Code coverage) Links Homepage Download Discussion Software Guide Help Examples Issue tr

Insight Software Consortium 1.1k Dec 4, 2022
Microsoft Cognitive Toolkit (CNTK), an open source deep-learning toolkit

The Microsoft Cognitive Toolkit is a unified deep learning toolkit that describes neural networks as a series of computational steps via a directed graph.

Microsoft 17.3k Dec 5, 2022
MaixPy3 is a Python3 toolkit based on cpython

MaixPy3 is a Python3 toolkit based on cpython, which simplifies the development of applications on Linux AI edge devices through Python programming.

Sipeed 138 Dec 2, 2022
🐸 Coqui STT is an open source Speech-to-Text toolkit which can run in real time on devices ranging from a Raspberry Pi 4 to high power GPU servers

Coqui STT ( ?? STT) is an open-source deep-learning toolkit for training and deploying speech-to-text models. ?? STT is battle tested in both producti

Coqui.ai 1.6k Nov 29, 2022
DyNet: The Dynamic Neural Network Toolkit

The Dynamic Neural Network Toolkit General Installation C++ Python Getting Started Citing Releases and Contributing General DyNet is a neural network

Chris Dyer's lab @ LTI/CMU 3.3k Dec 3, 2022
OpenVINO™ Toolkit repository

This toolkit allows developers to deploy pre-trained deep learning models through a high-level C++ Inference Engine API integrated with application logic.

OpenVINO Toolkit 3.8k Dec 2, 2022
A Modern C++ Data Sciences Toolkit

MeTA: ModErn Text Analysis Please visit our web page for information and tutorials about MeTA! Build Status (by branch) master: develop: Outline Intro

null 659 Nov 26, 2022
VNOpenAI 28 Nov 30, 2022
C++ Live Toolkit are tools subset used to perform on-the-fly compilation and running of cpp code

C++ Live Toolkit CLT (C++ Live Toolkit) is subset of tools that are very light in size, and maintained to help programmers in compiling and executing

MondeO 1 Jan 4, 2022
An Open-Source Analytical Placer for Large Scale Heterogeneous FPGAs using Deep-Learning Toolkit

DREAMPlaceFPGA An Open-Source Analytical Placer for Large Scale Heterogeneous FPGAs using Deep-Learning Toolkit. This work leverages the open-source A

Rachel Selina Rajarathnam 23 Sep 15, 2022
ClanLib is a cross platform C++ toolkit library.

ClanLib ClanLib is a cross platform toolkit library with a primary focus on game creation. The library is Open Source and free for commercial use, und

Kenneth Gangstø 306 Nov 24, 2022
Wt, C++ Web Toolkit

What is Wt ? Wt is a C++ library for developing web applications. It consists of: libwt, a widget/rendering library libwthttp, an (async I/O) HTTP/Web

null 1.5k Nov 26, 2022
Mobile Robot Programming Toolkit (MRPT) provides C++ libraries aimed at researchers in mobile robotics and computer vision

The MRPT project 1. Introduction Mobile Robot Programming Toolkit (MRPT) provides C++ libraries aimed at researchers in mobile robotics and computer v

MRPT 1.6k Dec 1, 2022
A powerful and versatile dynamic instrumentation toolkit.

MIGI Migi(My Ideas Got Incepted) is a powerful and versatile dynamic instrumentation toolkit. How it works By injecting Python scripts into target hos

nomads 5 Oct 22, 2022
Gesture Recognition Toolkit (GRT) is a cross-platform, open-source, C++ machine learning library designed for real-time gesture recognition.

Gesture Recognition Toolkit (GRT) The Gesture Recognition Toolkit (GRT) is a cross-platform, open-source, C++ machine learning library designed for re

Nicholas Gillian 791 Nov 27, 2022
YOLO5Face.lite.ai.toolkit

???? YOLO5Face 2021 with MNN/NCNN/TNN/ONNXRuntime C++ !

DefTruth 36 Nov 11, 2022