Ubuntu 18に純LibTorchでYOLOv 4のピットを配置する道(付源コード)


目次
坑一:公式サイトからダウンロードしたLibTorchライブラリはtorchvisionを持たない
ピット2:PythonのPILライブラリとopencvライブラリの画像処理の違いに注意すべき
ピット3:LibTorchのtensorに対する種々の変換操作度はPythonに比べて窒息する
ピット四:LibTorchにおけるtensor回転配列(ベクトル)
ピット5:YOLOv 4モデルの出力はtupleでforward後に直接toTensor()を使用することはできません.
3週間前、愚かな気持ちで困難なYOLOv 4の配置の道を始めたが、どれだけ困難だったのだろうか.基础がない、支援がない、最初はPyTorch、LibTorchで接触したことがなくて、甚だしきに至ってはC++も学びながらしなければならなくて、唯一闻くことができるのは小度と谷兄貴です.のあるhubでYOLOv 4に関する配置コードを探していたが、純LibTorchで作ったものは見つからなかった(私が見つけられなかったのかもしれない)、上にはLibTorch+DarkNetで書いたものが多く、DarkNetを使いたくないので、いっそ自分で純LibTorchの後処理コードを消してしまおう.昨日のテストの後、やっと効果が現れたので、この配置中に出会った穴を一つ一つ記録しました.
まず、操作環境を宣言します.
CPU版:Ubuntu 18、PyTorch 1.5.0CPU、LibTorch1.5.0CPU、CMake、VSCode、OpenCV 3.4.10、torchvision-0.6.0
GPU版:Ubuntu 18、PyTorch 1.7.1GPU、LibTorch1.7.1GPU、CMake、VSCode、CUDA10.2、torchvision0.8.2、OpenCV3.4.10
最終効果:LibTorchの目標出力データはPyTorchの出力と一致する
コードはGitHubにアップロードしました.リンク:
CPU版:https://github.com/wsx000/YOLOv4-LibTorch
GPU版:https://github.com/wsx000/YOLOv4-LibTorch-GPU
Ubuntu 18でのOpenCVのコンパイルインストールに関するチュートリアルはこちらをご覧ください.
坑一:公式サイトからダウンロードしたLibTorchライブラリはtorchvisionを持たない
torchvisionの関数、例えばnms_を使いたいならcpu()は、torchvisionを自分でダウンロードしてコンパイルする必要があります.具体的な方法はこのブログを見ることができます.
ピット2:PythonのPILライブラリとopencvライブラリの画像処理の違いに注意すべき
libtorchで作成した後にコードを処理した後、コード自体が不注意によるエラーの結果を除いて、PyTorchから飛び出した結果とは違うことに気づきました!複数の検証を経て、PyTorchでPILライブラリを使用して画像を処理するフォーマットがopencvライブラリと異なり、具体的な違いは以下の通りであることが分かった.
1、画像対サイズの記憶形式
PILライブラリを使用して直接画像のサイズを読み取って出力するのは(w,h)です.numpy配列に変換すると(h,w,c)になりますが、画像幅の高さを異なる方法で取得する場合は特に注意してください.
Opencvの画像の記憶フォーマットは(h,w,c)
例を見ると、使用される画像の幅は500、高さは333です.
from PIL import Image
import numpy as np
import cv2
pil_img = Image.open("/home/wsx/code/YOLOv4/y4-libtorch-V1-20201120/62.jpg")  # PIL   
cv_img = cv2.imread("/home/wsx/code/YOLOv4/y4-libtorch-V1-20201120/62.jpg")  # opencv  
print("pil_img   :", pil_img.size)   #   PIL       size
print("np-pil_img:", np.shape(pil_img))  #  PIL         numpy     shape
print("cv_img    :", cv_img.shape)   # opencv      shape

#=================    =================#
pil_img   : (500, 333)
np-pil_img: (333, 500, 3)
cv_img    : (333, 500, 3)

2、画像カラーチャンネルの記憶形式上
Opencvのピクチャに対する記憶フォーマットはBGRであり、PILライブラリはRGBであるため、PyTorchでPILライブラリを用いて画像を処理すると、LibTorchでopencvで処理した画像がRGBフォーマットに変換されて最終的な出力結果が一致する.また,異なるライブラリの同種差分メソッドで計算した結果も全く同じではない.
YOLOで歪みのない調整画像の関数を例に、2つの方法の違いを説明します.
# PIL        
def letterbox_image(image, size):
    iw, ih = image.size
    w, h = size
    scale = min(w/iw, h/ih)
    nw = int(iw*scale)
    nh = int(ih*scale)
    image = image.resize((nw,nh), Image.BICUBIC)
    new_image = Image.new('RGB', size, (128,128,128))
    new_image.paste(image, ((w-nw)//2, (h-nh)//2))
    return new_image

# opencv        
def letterbox_image(image, size):
    new_image = np.ones((608,608,3),np.uint8)
    new_image[:] = 128
    h, w = image.shape[0], image.shape[1]
    scale = min(608./w, 608./h)
    nw, nh = int(scale*w), int(scale*h)
    image = cv2.resize(image,(nw,nh))
    offsetw, offseth = int((608-nw)/2), int((608-nh)/2)
    new_image[offseth:nh+offseth, offsetw:nw+offsetw] = image
    # cv2.imshow('hhh', new_image)
    # cv2.waitKey()
    new_image = cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB)
    return new_image

ピット3:LibTorchのtensorに対する種々の変換操作度はPythonに比べて窒息する
YOLOv 4モデルの出力を後処理すると様々なマトリクス変換操作に遭遇するのは避けられませんが、PyTorchでできるLibTorchはすべて可能ですが、実現方式が少し複雑になっただけで、導入過程で使用したすべてのLibTorchマトリクスの操作方法はこのブログとこのブログを参考にすることができます.マトリクスの操作に注意してプログラミングするときは必ず注意してから注意してください!!!さもないと捜査が大変だ.血と涙の教訓よ(T...T)
ピット四:LibTorchにおけるtensor回転配列(ベクトル)
最後にいつもtensorを配列に変換して操作しなければならなくて、ネット上で探して、どのように変換するかを書くことはめったにないようで、下に私の変換の方法を置いて、少し複雑かもしれませんが、もっと良い方法があれば教えてください.
私はn*6のtensorステアリング量です.
    // tensor      (n, 6)  bboxes      tensor,boxes       
    vector> boxes(bboxes.sizes()[0], vector(6));

    for (int i = 0; i < bboxes.sizes()[0]; i++)
    {
        for (int j = 0; j < 6; j++)
        {
            boxes[i][j] = bboxes.index({at::tensor(i).toType(at::kLong),at::tensor(j).toType(at::kLong)}).item().toFloat();
            cout << boxes[i][j] << endl;
        }
        
    }

ピット5:YOLOv 4モデルの出力はtupleでforward後に直接toTensor()を使用することはできません.
このピットは実は私が出会った最初のピットで、当時forwardの後ろで直接toTensor()の方法を使ってずっと間違っていて、モデルの変換の問題だと思っていましたが、それから2日間愚かな顔をして問題を発見しました(T...T)、正しい操作方法は以下の通りです.
    //     
    auto outputs = module.forward(input).toTuple();

    //           
    vector<:tensor> out(3);
    out[0] = outputs->elements()[0].toTensor();
    out[1] = outputs->elements()[1].toTensor();
    out[2] = outputs->elements()[2].toTensor();

他にもたくさんの小さな問題があって、解決方法は一つ一つ記録していません.初心者としては不足点があります.問題があれば、伝言を歓迎します.