使用ML.NET+ONNX預訓練模型整活B站經典《華強買瓜》

前言

最近在看微軟開源的機器學習框架ML.NET使用別人的預訓練模型(開放神經網絡交換格式.onnx)來識別圖像,然后逛github發現一個好玩的repo。決定整活一期博客。

首先還是稍微科普一下機器學習相關的知識,這一塊.NET雖然很早就開源了ML.NET框架,甚至在官方的ML.NET開源之前,就有一些三方社區的開源實現比如早期的AForge.NET實現。以及后來的基于python著名的神經網絡框架tensorflow遷移的tensorflow.net亦或者是pytorch遷移的torchsharp來實現C#版本的深度學習,但是畢竟C#確實天生并不適合用來搞機器學習/深度學習,AI這一塊也一直都是python的基本盤。但是不適合并不代表沒有方案,現在AI逐漸普及的今天,我們普通的開發者依然可以使用一些別人訓練好的模型來做一些應用落地。

環境準備

今天我們會用到一些訓練好的模型來實現我們的目的,需要準備以下環境和工具:

1、安裝有.NET5或者6的windows開發環境

2、netron 用于解析模型的參數。
下載地址:https://t.ly/8iEk8

3、ffmpeg 用于視頻處理
下載地址:https://t.ly/ro0G

4、onnx預訓練udnie、super-resolution

udnie模型
下載地址:https://t.ly/0cUt

super-resolution模型
下載地址:https://t.ly/rnsi(需要解壓提取內部的onnx文件)

操作流程

1、首先我們將目標視頻(我這里就用B站經典短視頻《華強買瓜》為例)通過ffmpeg轉換成普通的一幀一幀的圖片

2、通過ML.NET加載【神經風格轉換預訓練模型】將每一幀原圖遷移到新的風格(藝術風格:udnie,抽象主義)。

3、由于2只能將圖片遷移到固定的240240格式,所以我們還需要通過ML.NET加載【超分辨率預訓練模型】將每一幀圖片進行超分辨率放大得到一張672672的圖片

4、通過ffmpeg將新的圖片合并成新的視頻

首先先看看成品(這里我轉換成gif方便演示):

原版視頻《華強買瓜》 1280*720

e2583168caff98d140cebf38da6fcfee.gif

遷移后的抽象藝術版本 224*224

1e1e451b100ebb31398222438f3f58b3.gif

超分辨放大后的版本 672*672

6d5b841265242509ee9bd3c99850c000.gif

接著我們看看如何一步一步來實現這個流程的

首先我們新建一個空白文件夾,將下載好的ffmpeg.exe和準備要處理的mp4視頻文件放進這個空白文件夾

接著我們需要從視頻中分離音頻文件,用于后期合成視頻時把音頻合成回去,否則視頻會沒有聲音,打開控制臺CD到剛才的目錄,執行命令:

b9ab8a617523d7d6d0a128a89ec02a4e.png

然后我們從視頻中將每一幀拆解成一張一張的jpg圖片,這里首先要創建一個img子文件夾,否則會報錯。另外我選擇的r 25意思就是每秒25幀。如果你的視頻不是每秒25幀(右鍵-屬性-詳細信息-幀速率)則自行根據文件調整,最后合成的時候也需要按照這個幀率合成新的視頻:

d53ee08d34d1be006ab5d29a476f5be9.png

到這里為止,我們就將圖片和音頻拆解出來了,接下來準備編碼,首先我們打開VS創建一個控制臺程序,引入nuget包:

57c8626cf415db659e528d9720073b46.png

接著我們創建一個一個類文件用于加載模型以及完成相應的圖片處理,在此之前我們需要使用安裝好的netron來打開這兩個onnx模型,查詢他們的輸入輸出值,打開netron選擇file-open,然后選擇第一個模型udnie-9.onnx,點擊input,可以看到右邊已經展示出了這個模型的輸入和輸出項,接著我們創建類的時候,這里需要這一些數字。

00b22c33b2a67e05ea19a447a1530ea0.png

接著我們打開VS創建好的項目,把我們的兩個onnx模型引入進去。接著編寫如下代碼:

首先定義一個session用于加下onnx模型

static?InferenceSession?styleTransferSession?=?new?InferenceSession("model/udnie-9.onnx");

接著我們創建一個方法調用這個模型

public?static?Bitmap?ProcessStyleTransfer(Bitmap?originBmp)
{//根據netron得到的input,我們在這里構建對應的輸入張量var?input?=?new?DenseTensor<float>(new[]?{?1,?3,?224,?224?});//將bitmap轉換成inputTool.BitmapToTensor(originBmp,?224,?224,?ref?input,?true);//接著調用模型得到遷移后的張量outputusing?var?results?=?styleTransferSession.Run(new[]?{?NamedOnnxValue.CreateFromTensor("input1",?input)?});if?(results.FirstOrDefault()?.Value?is?not?Tensor<float>?output)throw?new?ApplicationException("無法處理圖片");//由于模型輸出的是3*224*224的張量,所以這里只能構建出224*224的圖片return?Tool.TensorToBitmap(output,?224,?224);
}

其實到這一步神經風格遷移就完成了,最后的bitmap就是遷移后的新圖片,我們只需要調用bitmap.save即可保存到磁盤上

接著我們創建超分辨率模型的方法來,其實同上面的調用非常類似的代碼

這里唯一需要注意的是超分辨率提取并非采用RGB直接放大,而是用了YCbCr來放大,所以這里需要有一個轉換,原文在這里:https://github.com/onnx/models/tree/main/vision/super_resolution/sub_pixel_cnn_2016

static?InferenceSession?superResolutionSession?=?new?InferenceSession("model/super_resolution.onnx");
public?static?Bitmap?ProcessSuperResolution(Bitmap?originBmp)
{//根據netron得到的input,我們在這里構建對應的輸入張量,由于該模型并非采用RGB而是YCbCr,所以中間會做一些轉換,不過整體流程和上一個類似var?input?=?new?DenseTensor<float>(new[]?{?1,?1,?224,?224?});//將bitmap轉換成inputTool.BitmapToTensor(originBmp,?224,?224,?ref?input,?true);//由于模型處理Y值,剩下的Cb和Cr需要我們單獨調用System.Drawing.Common雙三次插值算法放大得到對應的Cb和Cr值var?inputCbCr?=?new?DenseTensor<float>(new[]?{?1,?672,?672?});inputCbCr?=?Tool.ResizeGetCbCr(originBmp,?672,?672);//接著調用模型得到超分重建后的張量outputusing?var?results?=?superResolutionSession.Run(new[]?{?NamedOnnxValue.CreateFromTensor("input",?input)?});if?(results.FirstOrDefault()?.Value?is?not?Tensor<float>?output)throw?new?ApplicationException("無法處理圖片");//創建一個新的bitmap用于填充遷移后的像素,這里需要通過Y+CbCr轉換為RGB填充return?Tool.TensorToBitmap(output,?224,?224,false,?inputCbCr);
}

其實基本上到這兩步,我們的整個核心代碼就完成了。剩余的部分只是一些圖片處理的代碼。接著我們要做的就是在Program.cs調用它得到遷移后的圖片

Directory.CreateDirectory("new?img?path");
foreach?(var?path?in?Directory.GetFiles("old?img?path"))
{//由于ffmpeg拆幀后的圖片就是按照幀率從1開始排序好的圖片,所以我們只需要將上一層的文件夾名字修改一下即可得到要替換的新文件路徑?like:?D://img/1.jpeg?->?D://newimg/1.jpegvar?newpath?=?path.Replace("old?img?path",?"new?img?path");using?var?originBitmap?=?new?Bitmap(Image.FromFile(path));using?var?transferBitmap?=?OnnxModelManager.ProcessStyleTransfer(originBitmap);using?var?reSizeBitmap?=?OnnxModelManager.ProcessSuperResolution(transferBitmap);reSizeBitmap.Save(newpath);
}

接著F5 run,然后靜待,一般要轉換20分鐘左右(cpu i5)基本就轉換完成了。最后我們只需要再使用工具合成新的視頻(或者gif)

./ffmpeg?-f?image2?-i?newimg/%d.jpeg?-i?1.aac?-map?0:0?-map?1:a?-r?25?-shortest?output.mp4
1?internal?class?Tool2?????{3?????????///?<summary>4?????????///?將bitmap轉換為tensor5?????????///?</summary>6?????????///?<param?name="bitmap"></param>7?????????///?<returns></returns>8?????????public?static?void?BitmapToTensor(Bitmap?originBmp,?int?resizeWidth,?int?resizeHeight,?ref?DenseTensor<float>?input,?bool?toRGB)9?????????{10?????????????using?var?inputBmp?=?new?Bitmap(resizeWidth,?resizeHeight);11?????????????using?Graphics?g?=?Graphics.FromImage(inputBmp);12?????????????g.DrawImage(originBmp,?0,?0,?resizeWidth,?resizeHeight);13?????????????g.Save();14?????????????for?(var?y?=?0;?y?<?inputBmp.Height;?y++)15?????????????{16?????????????????for?(var?x?=?0;?x?<?inputBmp.Width;?x++)17?????????????????{18?????????????????????var?color?=?inputBmp.GetPixel(x,?y);19?????????????????????if?(toRGB)20?????????????????????{21?????????????????????????input[0,?0,?y,?x]?=?color.R;22?????????????????????????input[0,?1,?y,?x]?=?color.G;23?????????????????????????input[0,?2,?y,?x]?=?color.B;24?????????????????????}25?????????????????????else26?????????????????????{27?????????????????????????//將RGB轉成YCbCr,此處僅保留Y值用于超分辨率放大28?????????????????????????var?ycbcr?=?RGBToYCbCr(color);29?????????????????????????input[0,?0,?y,?x]?=?ycbcr.Y;30?????????????????????}31?????????????????}32?????????????}33?????????}34?????????///?<summary>35?????????///?將tensor轉換成對應的bitmap36?????????///?</summary>37?????????///?<param?name="output"></param>38?????????///?<returns></returns>39?????????public?static?Bitmap?TensorToBitmap(Tensor<float>?output,?int?width,?int?height,?bool?toRGB?=?true,?Tensor<float>?inputCbCr?=?null)40?????????{41?????????????//創建一個新的bitmap用于填充遷移后的像素42?????????????var?newBmp?=?new?Bitmap(width,?height);43?????????????for?(var?y?=?0;?y?<?newBmp.Height;?y++)44?????????????{45?????????????????for?(var?x?=?0;?x?<?newBmp.Width;?x++)46?????????????????{47?????????????????????if?(toRGB)48?????????????????????{49?????????????????????????//由于神經風格遷移可能存在異常值,所以我們需要將遷移后的RGB值確保只在0-255這個區間內,否則會報錯50?????????????????????????var?color?=?Color.FromArgb((byte)Math.Clamp(output[0,?0,?y,?x],?0,?255),?(byte)Math.Clamp(output[0,?1,?y,?x],?0,?255),?(byte)Math.Clamp(output[0,?2,?y,?x],?0,?255));51?????????????????????????newBmp.SetPixel(x,?y,?color);52?????????????????????}53?????????????????????else54?????????????????????{55?????????????????????????//分別將模型推理得出的Y值以及我們通過雙三次插值得到的Cr、Cb值轉換為對應的RGB色56?????????????????????????var?color?=?YCbCrToRGB(output[0,?0,?y,?x],?inputCbCr[0,?y,?x],?inputCbCr[1,?y,?x]);57?????????????????????????newBmp.SetPixel(x,?y,?color);58?????????????????????}59?????????????????}60?????????????}61?????????????return?newBmp;62?????????}63?????????///?<summary>64?????????///?RGB轉YCbCr65?????????///?</summary>66?????????public?static?(float?Y,?float?Cb,?float?Cr)?RGBToYCbCr(Color?color)67?????????{68?????????????float?fr?=?(float)color.R?/?255;69?????????????float?fg?=?(float)color.G?/?255;70?????????????float?fb?=?(float)color.B?/?255;71?????????????return?((float)(0.2989?*?fr?+?0.5866?*?fg?+?0.1145?*?fb),?(float)(-0.1687?*?fr?-?0.3313?*?fg?+?0.5000?*?fb),?(float)(0.5000?*?fr?-?0.4184?*?fg?-?0.0816?*?fb));72?????????}73?????????///?<summary>74?????????///?YCbCr轉RGB75?????????///?</summary>76?????????public?static?Color?YCbCrToRGB(float?Y,?float?Cb,?float?Cr)77?????????{78?????????????return?Color.FromArgb((byte)Math.Clamp(Math.Max(0.0f,?Math.Min(1.0f,?(float)(Y?+?0.0000?*?Cb?+?1.4022?*?Cr)))?*?255,?0,?255),79?????????????????(byte)Math.Clamp(Math.Max(0.0f,?Math.Min(1.0f,?(float)(Y?-?0.3456?*?Cb?-?0.7145?*?Cr)))?*?255,?0,?255),80?????????????????(byte)Math.Clamp(Math.Max(0.0f,?Math.Min(1.0f,?(float)(Y?+?1.7710?*?Cb?+?0.0000?*?Cr)))?*?255,?0,?255)81?????????????????);82?????????}83?????????///?<summary>84?????????///?雙三次插值提取CbCr值85?????????///?</summary>86?????????public?static?DenseTensor<float>?ResizeGetCbCr(Bitmap?original,?int?newWidth,?int?newHeight)87?????????{88?????????????var?cbcr?=?new?DenseTensor<float>(new[]?{?2,?newWidth,?newHeight?});89?????????????using?var?bitmap?=?new?Bitmap(newWidth,?newHeight);90?????????????using?var?g?=?Graphics.FromImage(bitmap);91?????????????g.InterpolationMode?=?InterpolationMode.HighQualityBicubic;92?????????????g.SmoothingMode?=?SmoothingMode.HighQuality;93?????????????g.DrawImage(original,?new?Rectangle(0,?0,?newWidth,?newHeight),94?????????????????new?Rectangle(0,?0,?original.Width,?original.Height),?GraphicsUnit.Pixel);95?????????????g.Dispose();96?????????????for?(var?y?=?0;?y?<?bitmap.Width;?y++)97?????????????{98?????????????????for?(var?x?=?0;?x?<?bitmap.Height;?x++)99?????????????????{
100?????????????????????var?color?=?bitmap.GetPixel(x,?y);
101?????????????????????var?ycbcr?=?RGBToYCbCr(color);
102?????????????????????cbcr[0,?y,?x]?=?ycbcr.Cb;
103?????????????????????cbcr[1,?y,?x]?=?ycbcr.Cr;
104?????????????????}
105?????????????}
106?????????????return?cbcr;
107?????????}
108?????}

總結

這一期整活基本到此就結束了,雖然只是調用了兩個小模型搞著玩,但是其實只要能搞到業界主流的開源預訓練模型,其實可以解決很多實際的商業場景,比如我們最近在使用美團開源的yolov6模型做一些圖像對象檢測來落地就是一個很好的例子這里就不再展開。另外微軟也承諾ML.NET的RoadMap會包含對預訓練模型的遷移學習能力,這樣我們可以通過通用的預訓練模型根據我們自己的定制化場景只需要提供小規模數據集即可完成特定場景的遷移學習來提高模型對特定場景問題的解決能力。今天就到這里吧,下次再見。

作者:a1010

原文:https://www.cnblogs.com/gmmy/p/16433499.html

END

aa70f08449953bb08a8272f026b48d94.gif

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/285355.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/285355.shtml
英文地址,請注明出處:http://en.pswp.cn/news/285355.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C語言試題125之一個 5 位數,判斷它是不是回文數。即 12321 是回文數,個位與萬位相同,十位與千位相同

?作者簡介:大家好我是碼莎拉蒂,CSDN博客專家?????? ??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款模擬面試、刷題神器?? 點擊跳轉進入網站 1、題目 題目:一個 5 位數,判斷它是不是回文數。即 12321 是回文數,個位與萬位相同,十位與千位相…

不會C# Winform用戶界面設計?是因為沒有掌握這些控件(建議收藏)

文章目錄 6.1 常用控件6.1.1 單選按鈕(RadioButton)6.1.2 復選框(CheckBox)6.1.3 框架1. 面板控件Panel2. 分組框GroupBox3. TabControl控件6.1.4 應用實例6. 2 列表框和組合框6.2.1 列表框(ListBox)6.2.2 組合框(ComboBox )6.2.3 應用實例6.3 用戶交互界面6.3.1 滾動條和進度條…

痞子衡嵌入式:ARM Cortex-M內核那些事(2)- 第一款微控制器

大家好&#xff0c;我是痞子衡&#xff0c;是正經搞技術的痞子。今天痞子衡給大家介紹的是第一款Cortex-M微控制器。 1.天生榮耀&#xff1a;ARM Cortex-M處理器由來 ARM公司自2004年推出ARMv7內核架構時&#xff0c;摒棄了以往"ARM數字"這種處理器命名方法&#xff…

SQL Server在更改計算機名后的設置

把原來的賬號刪除 再添加現有的賬號 添加權限 搞定了上面的賬號配置&#xff0c;接下來就是設置服務器名稱 參考&#xff1a;http://www.cnblogs.com/EasonJim/p/6114249.html 后話&#xff1a;當初為了設置這個問題&#xff0c;選擇了重裝SQL Server&#xff0c;但是也是無用的…

Jupyter Notebook 入門指南

簡介 Jupyter Notebook&#xff08;此前被稱為 IPython notebook&#xff09;是一個交互式筆記本&#xff0c;支持運行 40 多種編程語言。 Jupyter Notebook 的本質是一個 Web 應用程序&#xff0c;便于創建和共享文學化程序文檔&#xff0c;支持實時代碼&#xff0c;數學方程…

C語言試題127之 100 之內的素數

?作者簡介:大家好我是碼莎拉蒂,CSDN博客專家?????? ??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款模擬面試、刷題神器?? 點擊跳轉進入網站 1、題目 題目:求 100 之內的素數 2 、溫馨提示 想獲取更多C語言題目請猛搓這里==========》200個C語…

學弟學妹們,C#為啥學不好?是因為你沒真正理解面向對象的思想!

文章目錄 7.1面向對象的基本概念7.1.1 什么是面向對象編程7.1.2 面向對象編程的特點7.2 類7.2.1 類的概念7.2.2 類的聲明7.2.3 類的成員7.2.4 類成員訪問修飾符7.3 對象7.4 構造函數和析構函數7.4.1 構造函數7.4.2 析構函數7.5 方法7.5.1 方法的聲明7.5.2 方法的參數7.5.3 靜態…

MongoDB中的分組

一.MongoDB中的Count函數、Distinct函數以及分組 準備工作&#xff0c;插入一個班級的文檔 > for(var i0;i<10;i){ ... db.Classes.insert({ClassName:"Class"i,_id:i}); ... } WriteResult({ "nInserted" : 1 }) > db.Classes.find() { "_i…

在 .NET 6 項目中使用 Startup.cs

對于 .NET 6 項目&#xff0c;現在已經找不到 Startup.cs 文件。默認情況下&#xff0c;此文件已經被刪除&#xff0c;并且 Program.cs 是配置依賴注入服務和 Middleware 的新位置。但是&#xff0c;有些人可能更喜歡使用 Startup.cs , 并且我也是&#xff0c;可能已經習慣了&a…

編寫iptables腳本實現IP地址、端口過濾

實驗案例&#xff1a;公司使用一臺運行RHEL5系統的服務器作為網關&#xff0c;分別連接三個網絡&#xff0c;其中LAN1為普通員工電腦所在的局域網&#xff0c;LAN2為DNS緩存服務器所在的局域網。eth0通過10M光纖接入Internet。為了有效的管理網絡環境及增強內部網絡的安全性&am…

讓VS Code 支持 Jupyter Notebook

一、Jupyter Notebook Jupyter Notebook是基于網頁的用于交互計算的應用程序。其可被應用于全過程計算&#xff1a;開發、文檔編寫、運行代碼和展示結果。——Jupyter Notebook官方介紹。 Jupyter Notebook是以網頁的形式打開&#xff0c;可以在網頁頁面中直接編寫代碼和運行代…

C語言試題128之對 10 個數進行排序

?作者簡介:大家好我是碼莎拉蒂,CSDN博客專家?????? ??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款模擬面試、刷題神器?? 點擊跳轉進入網站 1、題目 題目:對 10 個數進行排序 分析:可以利用選擇法,即從后 9 個比較過程中,選擇一個最小的與…

JavaScript設計模式

JavaScript的設計模式&#xff1a; 1、單例模式 單例模式&#xff08;Singleton&#xff09;&#xff0c;整個運行期間只會被初始化一次。該模式簡單易懂&#xff0c;運用也很廣泛。可以用它來聚合公共的方法&#xff0c;形成一個工具類&#xff0c;對外提供api。 var single …

Windows 11 23H2 25145 推送!全新隱私設置和 OneDrive 體驗

面向 Dev 頻道的 Windows 預覽體驗成員&#xff0c;微軟現已推送 Windows 11 預覽版 Build 25145。主要變化1.微軟宣布為 Windows 11 設置引入全新 OneDrive 體驗&#xff0c;您可以在設置中查看 OneDrive 云存儲服務的訂閱詳情&#xff0c;包括付費方式、容量大小和定期付費等…

你不知道的無人機知識(建議收藏)

文章目錄 無人機簡介無人機的分類按用途分類按飛行平臺構型分類按大小分類按飛行性能分類你絕對沒有見過的無人機真三維模型 無人機簡介 無人駕駛飛機簡稱“無人機”,英文縮寫為“UAV”,是利用無線電遙控設備和自備的程序控制裝置操縱的不載人飛機,或者由車載計算機完全地或…

用jekyll制作高大上的網站(二)——實際應用

最近公司要制作個文檔庫&#xff0c;直接就可以將jekyll應用到實際中。 模版使用了Jekyll Clean&#xff0c;這么模版相對內部簡單一點&#xff0c;學習成本不會很大&#xff0c;而復雜的Minimal Mistakes就當作參考。 模版使用的CSS是Bootstrap v3.2.0版本的。為了省時點&…

Python腳本模擬登錄網頁之CSDN篇

1. 通過Firefox配合插件Tamper Date獲取登錄時客戶端向服務器端提交的數據, 并且發現lt和execution這兩個字段每次登錄時都不一樣. POSTDATAusernameyour_id&passwordyour_pwd&ltLT-239317-XyzqoketZ2AhHE5BSIKLwW7gNyb4sP&executione2s1&_eventIdsubmit 2. 于…

C語言試題129之求一個 3乘3 矩陣對角線元素之和

?作者簡介:大家好我是碼莎拉蒂,CSDN博客專家?????? ??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款模擬面試、刷題神器?? 點擊跳轉進入網站 1、題目 題目:求一個 3乘3 矩陣對角線元素之和 分析:利用雙重 for 循環控制輸入二維數組,再將 a[…

ssh key生成

Mac電腦用終端生成SSH key 訪問自己的Github 字數684 閱讀427 評論4 喜歡15前言&#xff1a;最近有不少剛剛使用github管理代碼的開發者或者新手碼農在網上 問我如何關聯自己的github&#xff0c;今天就寫篇文章僅供參考。一、首先你要檢測自己電腦是否存在 SSH key 在終端輸出…

[轉]小白都能看懂的softmax詳解

1.softmax初探 在機器學習尤其是深度學習中&#xff0c;softmax是個非常常用而且比較重要的函數&#xff0c;尤其在多分類的場景中使用廣泛。他把一些輸入映射為0-1之間的實數&#xff0c;并且歸一化保證和為1&#xff0c;因此多分類的概率之和也剛好為1。 首先我們簡單來看看s…