c#實現SharedMatting摳圖算法


內容簡介

       將Alpha Matting摳圖算法由c++ 版本移植至c#環境。 主要采用OpenCV的C#版本Emgu取代c++支撐的OpenCV。

 

參考資料

http://www.inf.ufrgs.br/~eslgastal/SharedMatting/

這個網頁是算法的論文介紹頁,可以從該網頁下載到論文原文以及測試用圖以及linux下的Demo。

https://github.com/np-csu/AlphaMatting
我從該網頁下載了Alpha Matting算法的c++源碼。

https://www.cnblogs.com/Imageshop/p/3550185.html
這是我在查詢Alpha Matting算法資料時看見的比較友善的算法介紹。作者優化了C++版的算法。

 

我的實現效果

 

實驗環境

系統: Windows 8.1 專業版

工具:Visual Studio 2017

Emgu:emgucv-windesktop 3.2.0.2682

C#測試工程: WPF工程項目

 

避免采坑

  1. 最好不要用最新版Vs2019

  我剛開始采用VS2019,安裝OpenCv后測試運行C++,各種不通。后續發現vs2019新建的項目自動配置為VC16 環境。 而下載的OpenCv明確指定了需要VC14或VC15. 如我下載的OpenCV: opencv-3.4.5-vc14_vc15.exe. 從名稱即可看出。廢了我許多不必要的嘗試時間。

         2、版本差異

  剛開始下載OpenCV 最新的4.1.1 版本,報了很多類型錯誤。 建議使用3.* 的版本。  4.1.1版本我注釋掉了部分OpenCv的代碼,算法可以繼續無差運行,但總感覺不完美,所以我替換成了3.* 的版本。

 

關鍵信息

C++類型

我處理成的對應c#類型

cv::Point

System.Drawing.Point

vector<cv::Point>&

List<System.Drawing.Point>

vector<vector<cv::Point>>&

List<List<System.Drawing.Point>>

char*

string

struct labelPoint

public class labelPoint

Tuple

public class TupleInfo

Ftuple

public class FtupleInfo

int**

Int[,]

uchar*

Byte[]

iterator

更改為For循環

Scalar

Emgu: MCvScalar

 public class labelPoint
{
        public int x;
        public int y;
        public int label;
};

public class TupleInfo
{
        public double FR;
        public double FG;
        public double FB;

        public double BR;
        public double BG;
        public double BB;

        public double sigmaf;
        public double sigmab;
        public int flag;
};

public class FtupleInfo
{
        public double FR;
        public double FG;
        public double FB;

        public double BR;
        public double BG;
        public double BB;

        public double alphar;
        public double confidence;
};

public void loadImage(string sFile)
{
        pImg = CvInvoke.Imread(sFile);
        if (pImg.GetData() == null || pImg.GetData().Length == 0)
        {
              Console.WriteLine("load pImg failed!");
              return;
        }

        height = pImg.Rows;
        width = pImg.Cols;
        step = pImg.Step / (pImg.ElementSize / pImg.NumberOfChannels);
        channels = pImg.NumberOfChannels;
        data = pImg.GetData();
        unknownIndex = new int[height, width];
        tri = new int[height, width];
        alpha = new int[height, width];
}

有了這些對應信息,你就可以嘗試自己移植了。

 

深化嘗試

       從我的調試結果來看,可以實現摳圖,如果你也同時在C++環境下運行了算法,你會發現C#環境下的算法運行時間遠超C++。然后我就考慮將摳圖算法在C++環境下打包成dll供C#調用。

       由於不熟悉c#與C++的交互,我踩了很多坑,實現的也並不算完美,不過總之調通了。

我將提前准備好的原圖以及Trimap圖的路徑傳給C++的dll,期望返回處理過后的Alpha數組。

c#端:

   首先添加我生成的C++ Dll並聲明引用。

 [DllImport("ImgIntelligHelper.dll", CharSet = CharSet.Unicode)]
 public extern static IntPtr GetMatteMap([MarshalAs(UnmanagedType.LPStr)] string sInput,
 [MarshalAs(UnmanagedType.LPStr)] string sOutput);

然后對dll中的函數進行調用,返回透明度矩陣的內存地址,然后賦值到我創建的數組中。

// sInput - 原圖路徑; sTrimap: Trimap圖路徑
System.Drawing.Bitmap oBitmap = new Bitmap(sInput);
int nlength = oBitmap.Width * oBitmap.Height;
IntPtr intptr = GetMatteMap(sInput, sTrimap);
int[] arrAlpha = new int[nlength];
Marshal.Copy(intptr, arrAlpha, 0, nlength);

C++端:

       新增了一個方法,將矩陣轉換為int數組。

void AlphaMatting::GetAlphaMap()
{
    int h = matte.rows;
    int w = matte.cols;
    Map = new int[h*w];
    for (int i = 0; i < h; ++i)
    {
        for (int j = 0; j < w; ++j)
        {
            Map[i * w + j] = alpha[i][j];
        }
    };
}

// 接收圖片並處理
int* GetMatteMap(char* sInput, char* sTrimap)
{
    AlphaMatting alphaMatHelper;
    alphaMatHelper.loadImage(sInput);
    alphaMatHelper.loadTrimap(sTrimap);
    alphaMatHelper.solveAlpha();
    alphaMatHelper.GetAlphaMap();
    return alphaMatHelper.Map;
}

最后用原圖以及dll返回的Alpha數組實現摳圖。 這是可行的方式,整個流程進行下來效率相對於純C#版會有較大的改進。但是相對於純C++版本來說,消耗還是過高。

然后我又嘗試將C++版算法改成控制台應用程序。在C#中采用啟動進程的方式,傳入原圖、Trimap圖、輸出圖路徑值,然后以不顯示應用程序窗口的方式在后台靜默執行。 進程結束后見到了C++應用程序處理過后的結果。

C#中調用C++生成的控制台應用exe。 

private void DoConvert(string sIndex)
{
    string sBasePath = AppDomain.CurrentDomain.BaseDirectory;
    string sExeFile = sBasePath + @"\AlphaMattingPlugin.exe";
    string sInput = sBasePath + @"\Datas\input" + sIndex+ ".jpg";
    string sTrimap = sBasePath + @"\Datas\trimap" + sIndex + ".jpg";
    string sOutput = sBasePath + @"\Datas\AlphaMattingPluginSample" + sIndex + ".png";

     Process process = new Process();
     process.StartInfo.FileName = sExeFile;
     // 調用C++版本的控制台Exe,傳入原圖、Trimap圖、摳圖結果輸出文件路徑
     process.StartInfo.Arguments = " " + sInput + " " + sTrimap + " " + sOutput;
     process.StartInfo.CreateNoWindow = false;
     process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
     process.Start();
     process.WaitForExit();
       
     if (File.Exists(sOutput))
         this.ShowImage(this.ImgResult, sOutput);
}

private void ShowImage(Image img, string sFile)
{
     byte[] bytes = System.IO.File.ReadAllBytes(sFile);
     BitmapImage bitImg = new BitmapImage();
     bitImg.BeginInit();
     bitImg.StreamSource = new System.IO.MemoryStream(bytes);
     bitImg.EndInit();
     bitImg.Freeze();
     img.Source = bitImg;
}

這樣處理后比純C++環境多耗時0.5s左右,這個結論對於我是能接受的。

采用這種方式,摳圖算法執行時間消耗我進行了測試,如下圖

原來需要10-20s的現在僅用1-3s就能實現。如下圖在C#環境下WPF工程調用C++版exe的調試截圖:

結論

        Alpha Matting摳圖算法可以移植至C#平台,但是最佳實踐還是用C++去處理,采用C#調用C++的方式會大大節省耗時。

        圖片越大耗時會越高,目前我尚未嘗試4K圖。

        原本還想將Global Matting 及其他幾種摳圖算法也想法移植到C#平台,但是經過上文中一些列測試,發現還是保留原版本更為合理,用C#直接去調用Dll 或 包裝的exe應用即可,而且效率更高。

        源碼下載:微信掃描下方二維碼文章末尾獲取鏈接。

                                 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM