1. 程式人生 > >影象分割之圖割工具箱GCO3.0的使用(二)

影象分割之圖割工具箱GCO3.0的使用(二)

<div id="article_content" class="article_content clearfix csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post">                                 <div class="article-copyright">                     版權宣告:本文為博主原創文章,未經博主允許不得轉載。                    https://blog.csdn.net/on2way/article/details/46742765                </div>                                             <div class="markdown_views">                             <!-- flowchart 箭頭圖示 勿刪 -->                             <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path></svg>                             <h2 id="一之前"><a name="t0"></a>一):之前</h2>

<p>之前在部落格 <br>  <strong><em><a href="http://blog.csdn.net/on2way/article/details/43276155" rel="nofollow" target="_blank">matlab實現圖割演算法中的最大流最小割Max-flow/min-cut問題</a></em></strong> <br> 中我們講到了關於圖中求取最小割最大流的一個軟體包,並簡單介紹了它在影象分割中的應用,但是並沒有深入給出具體例項,這裡我們再在最小割最大流原理基礎上介紹同一個研究所出的另一種整合的軟體包gco-v3.0,這個軟體包則是直接可以對影象進行分割操作的,達到可以觀測的效果。 <br> 關於軟體包gco-v3.0的下載地址,同樣在網址為:<a href="http://vision.csd.uwo.ca/code/" rel="nofollow" target="_blank">http://vision.csd.uwo.ca/code/</a>在這裡找到對應的內容即可;</p>

<h2 id="二準備"><a name="t1"></a>二):準備</h2>

<p>好了下載好的工具箱解壓,裡面會有一些c編寫的程式碼以及另一個matlab版資料夾,這裡說一下,同樣是使用matlab來進行後面的所有操作(在使用之前需要編譯一下成matlab可用的檔案,程式裡面有)。那麼資料夾裡面的檔案正常的話就如下:<img src="https://img-blog.csdn.net/20150703154536891" alt="這裡寫圖片描述" title=""> <br> 乍一看,好多函式呀,不要著急,如果不瞭解要一個個看確實費勁,但是每個仔細看了以後就會發現其實每個都有用途的,後面也會對每個函式進行講解說明的,以加快初學者學習的進度。</p>

<h2 id="三理解"><a name="t2"></a>三):理解</h2>

<p>好了現在我們來回味一下關於基礎的圖割演算法需要哪些部分,連基本概念都不知道的建議去看一下上一篇文章: <br>  <strong><em><a href="http://blog.csdn.net/on2way/article/details/43276155" rel="nofollow" target="_blank">matlab實現圖割演算法中的最大流最小割Max-flow/min-cut問題</a></em></strong> <br>  看完後起碼知道在進行圖割演算法操作的時候,一些必備的項,矩陣等等都知道,比如關於一個待分割圖的資料項Datacost(點與源匯點(類點)之間的權值),平滑項Smoothcost(點與點之間的權值),標籤Labeling(所有點各屬於哪一個類),等等。借用上篇文章的圖,比如下列的兩分割問題: <br> <img src="https://img-blog.csdn.net/20150703154807556" alt="這裡寫圖片描述" title=""> <br> 當然,正常的一個影象可能不是兩分割問題,可能存在多分割問題,這個時候類的點數(S,T)可能就不是兩個了,可能就是多個,把S,T都統一為類L的話,那麼一個圖就可以分割為如下: <br> <img src="https://img-blog.csdn.net/20150703154832530" alt="這裡寫圖片描述" title=""> <br> 大概先簡化為這樣吧,畫的比較醜,圖中有分的類L(L1,L2…),點數N(n1,n2,…),以及所有的邊的權值(兩類不同的邊的權值吧),datacost應該好理解,就是每一個點都會與所有的類有一個權值,這個值越大就說明它越屬於這一類,smoothcost就是點與點之間的權值,這裡畫的是一維的情況,表示的是二維的關係,什麼意思呢,想想一幅圖是m*n的圖是什麼樣子的,就是m*n的畫素點吧,把每個畫素點看成這裡的一個點,假設現在是3*3的圖吧,畫素值都在0-255之間,那麼你在matlab下看到的影象就是這樣的: <br> <img src="https://img-blog.csdn.net/20150703154906986" alt="這裡寫圖片描述" title=""> <br> 這裡我們只認為每個點與它附近的4連通區域有關(也有8連通區域),並且他們之間的權值(也就是smoothcost)簡單的話就把他們之間的畫素差值當做cost,如上圖所示。這樣看來這是個二維的點陣,那麼如何應用到上述的圖呢?這裡我們就要在認識上轉化為一維的了,把這些點從1-9依次排開,然後我們看到點1與2和4相連,點2和1,3,5相連,點3和2,6相連等等,而連線的權值就是相應的畫素差絕對值,那麼對於沒有相連的,比如點1與3,5,6,7,8,9這些點怎麼辦了,我們也可以認為他們相連,只不過連線的權值為0而已;這樣是不是就出來了smoothcost矩陣了,而且大小為[m*n,m*n],也可以看到,這個矩陣是一個稀疏矩陣,也就是有(大概)80%的點為0,這要是都寫出來是非常佔記憶體,所以在後面處理的時候會看到,這個矩陣是需要用稀疏矩陣來生成的,matlab是有對應函式去生成的,比如下面這個:</p>

<pre class="prettyprint" name="code"><code class="hljs haml has-numbering"><span class="hljs-tag">%<span class="hljs-title">matlab</span></span>函式生成稀疏矩陣(這麼做的速度最快) neighb = spconvert(all);</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li></ul></pre>

<p>圖形化這個二維矩陣到一維就是這樣的: <br> <img src="https://img-blog.csdn.net/20150703155000490" alt="這裡寫圖片描述" title=""> <br> 對於邊權值為0的邊就沒有畫出來,這樣一個影象就可以用一維表示出來,就可以用上述圖割方法了。 <br> 當然還有很多問題,比如說有多少個類L,每個點到各個類之間的權值為多少,這些都會在求解問題的時候遇到。</p>

<h2 id="四再理解"><a name="t3"></a>四):再理解</h2>

<p>學習圖割分割演算法之前,看看幾篇專業研究這方面的文獻是很有必要的,比如: <br> <a href="http://wenku.baidu.com/link?url=r0UJHR5stIvnfYiRa5cbDqfY-icr6OmqV3afUCCQkcou6xyrqlDwKCdllWgUUBtdcJBoZcs9DeRSTqLUJ-Eih6Xi6MNmOP_I9EuAqr3pWVO" rel="nofollow" target="_blank">http://wenku.baidu.com/link?url=r0UJHR5stIvnfYiRa5cbDqfY-icr6OmqV3afUCCQkcou6xyrqlDwKCdllWgUUBtdcJBoZcs9DeRSTqLUJ-Eih6Xi6MNmOP_I9EuAqr3pWVO</a></p>

<p><a href="http://wenku.baidu.com/link?url=qMhM4Lbz24gupBNVOz0aSD7zmoqLKvzYRPwoxWUluvIXcqeIKzqrQ9SBCeInGX8oUFQdFzJpd9Vi0bfv4-2-OXGTP97teUQMlkVMvG73Pba" rel="nofollow" target="_blank">http://wenku.baidu.com/link?url=qMhM4Lbz24gupBNVOz0aSD7zmoqLKvzYRPwoxWUluvIXcqeIKzqrQ9SBCeInGX8oUFQdFzJpd9Vi0bfv4-2-OXGTP97teUQMlkVMvG73Pba</a> <br> 這兩篇文獻是非常經典的。圖割分割演算法在有了上述工具箱之後,只需要(主要)確定了上述說到的smoothcost和datacost就可以運行了。下面來談談工具箱中函式的具體用法:</p>

<h3 id="41建立目標體gcocreate"><a name="t4"></a>4.1)建立目標體GCO_Create</h3>

<p>函式用法:</p>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%---  建立一個優化目標體 Handle %</span>      <span class="hljs-constant">Handle</span> = <span class="hljs-constant">GCO_Create</span>(<span class="hljs-constant">NumSites</span>,<span class="hljs-constant">NumLabels</span>) %      <span class="hljs-constant">NumSites</span>:所有點數目; <span class="hljs-constant">NumLabels</span>:所有標籤數</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li></ul></pre>

<p>這個函式中,在確定了待分割影象後,NumSites就是所有點個數(m*n),NumLabels是要分類的類別數(也應該知道)。</p>

<h3 id="42設定分類標籤gcosetlabeling"><a name="t5"></a>4.2)設定分類標籤GCO_SetLabeling</h3>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%----為當前目標體設定標籤 %</span>    <span class="hljs-constant">GCO_SetLabeling</span>(<span class="hljs-constant">Handle</span>,<span class="hljs-constant">Labeling</span>),其中<span class="hljs-constant">Labeling</span>為<span class="hljs-number">1</span>*<span class="hljs-constant">Numsites</span>的向量; %    也就是為目標體中每個點設定一個標籤</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li></ul></pre>

<p>這個函式就是給所有點設定一個分類標籤,Labeling就是設定的標籤,可能有人會說,標籤不是我們最終要求解的嗎,為什麼自己可以設定,確實,我們的目的就是求解分類標籤,但是是更好的分類標籤,這個分類標籤可以是預分類的標籤,比如你可以在分類前使用kmeans預分割一下,把這個預分割標籤設定進去,kmeans預分割還有一個用處就是找到初始的各個類中心。</p>

<h3 id="43重頭戲之設定gcosetdatacost"><a name="t6"></a>4.3)重頭戲之設定GCO_SetDataCost</h3>

<p>datacost上述說到,所有點與每個類(中心)之間的權值大小,越小,說明該點越屬於這一類,反之越大越不屬於。函式說明:</p>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%------為每個點設定 datacost %</span> 用法:<span class="hljs-constant">GCO_SetDataCost</span>(<span class="hljs-constant">Handle</span>,<span class="hljs-constant">DataCost</span>) %  <span class="hljs-constant">DataCost</span>:大小為 <span class="hljs-constant">NumLabels</span>-by-<span class="hljs-constant">NumSites</span> :類標籤數m*總資料點數           n  接受矩陣為:n*m %  其中每個<span class="hljs-constant">DataCost</span>(k,i)表示類標籤k到點i的cost %  <span class="hljs-constant">SetDataCost</span>函式可以反覆呼叫;</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li></ul></pre>

<p>很顯然,在使用之前,你需要做的就是把DataCost求出來,然後直接設定進去,矩陣大小在上面也可以看到,注意總點數n是影象畫素點的總畫素點數。那麼怎麼去確定這個矩陣就是演算法的好壞了。</p>

<h3 id="44重頭戲之設定gcosetneighbors"><a name="t7"></a>4.4)重頭戲之設定GCO_SetNeighbors</h3>

<p>設定完點與類中心的權值,再來設定點與點之間的邊權值,認為每個點只與附近的四領域或者八領域內的幾個點有邊權值,而與其他所有點邊權值都為0,函式說明:</p>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%-----設定Neighbors之間的權值 %</span>為所有點sites兩兩之間設定一個權值,從而決定哪些點是相鄰的 %   使用:<span class="hljs-constant">GCO_SetNeighbors</span>(<span class="hljs-constant">Handle</span>,<span class="hljs-constant">Weights</span>) %        <span class="hljs-constant">Weights</span>是一個<span class="hljs-constant">NumSites</span>-by-<span class="hljs-constant">NumSites</span>矩陣 %        這裡:<span class="hljs-constant">Weights</span>(i,j) &gt; <span class="hljs-number">0</span>就表明點i與j是相鄰的 %        如果<span class="hljs-constant">Weights</span>是一個<span class="hljs-number">0</span>-<span class="hljs-number">1</span>矩陣,那麼smooth costs項就是一個空間不變數 %注意:只會訪問 <span class="hljs-constant">Weights</span>為上三角矩陣,因為矩陣圖邊與邊之間是一個無向圖</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li></ul></pre>

<p>可以看到函式需要一個矩陣Weights,這個矩陣一般情況下非常大,如果是100*100的影象,那麼這個矩陣就是10000*10000的,通常情況下我們是需要用稀疏矩陣來表示的,因為這個矩陣中80%以上元素為0。對於不為0的權值大小也是演算法需要構造的地方。</p>

<h3 id="45其他設定gcosetsmoothcost"><a name="t8"></a>4.5)其他設定GCO_SetSmoothCost</h3>

<p>這個函式實際上是輔助上面那個函式GCO_SetNeighbors的,函式說明:</p>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%-------設定SmoothCost項 %</span>    使用: <span class="hljs-constant">GCO_SetSmoothCost</span>(<span class="hljs-constant">Handle</span>,<span class="hljs-constant">SmoothCost</span>) %           其中<span class="hljs-constant">SmoothCost</span>為一個<span class="hljs-constant">NumLabels</span>-by-<span class="hljs-constant">NumLabels</span>矩陣 %  對任意一個<span class="hljs-constant">SmoothCost</span>(k,p),如果一個鄰域記憶體在類k與類p,表示類標籤k與類標籤p之間的在無權值情況下的cost; %  而鄰域內的兩個點i與j,他們之間的實際帶權值的cost是:<span class="hljs-constant">Weights</span>(i,j)*<span class="hljs-constant">SmoothCost</span>(k,p); %  空間變化上的權值是由<span class="hljs-constant">GCO_SetNeighbors</span>指定的; %  如果不呼叫<span class="hljs-constant">SetSmoothCost</span>函式的話,就是一個預設的<span class="hljs-constant">Potts</span>模型 %       <span class="hljs-constant">Potts</span>模型表示的是:如果兩個類k==p,那麼<span class="hljs-constant">SmoothCost</span>(k,p)=<span class="hljs-number">0</span>;認為是同類,否則值為<span class="hljs-number">1</span>;</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li></ul></pre>

<p>在這個工具箱中,程式所執行的實際點與點之間的權值大小其實是Weights(i,j)* <br> SmoothCost(k,p);正常情況下我們用Weights(i,j)就是了,所以在不需要SmoothCost的時候,我們採用預設的Potts模型。</p>

<h3 id="46工具箱的執行演算法gcoexpansion-gcoexpandonalphagcoswap"><a name="t9"></a>4.6)工具箱的執行演算法:GCO_Expansion、        GCO_ExpandOnAlpha、GCO_Swap</h3>

<p>在所有設定完成後選擇一種圖割演算法就可以得到新的分割結果,這幾種演算法在前面的外文文獻中都有詳細的介紹。</p>

<h4 id="461gcoexpansion函式說明">4.6.1:GCO_Expansion函式說明:</h4>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%-----對目標體進行alpha-expansion演算法操作:返回尋找的最小能量Energy %</span>  方式一: <span class="hljs-constant">GCO_Expansion</span>(<span class="hljs-constant">Handle</span>),執行a-膨脹最小化當前能量--直到收斂 %  方式二: <span class="hljs-constant">GCO_Expansion</span>(<span class="hljs-constant">Handle</span>,<span class="hljs-constant">MaxIter</span>)      %          加入一個迭代執行次數:<span class="hljs-constant">MaxIter</span>: 該演算法在迭代<span class="hljs-constant">MaxIter</span>次後還沒有找到最小能量也停止 % note: <span class="hljs-number">1</span>):當前目標體的標籤可以通過函式<span class="hljs-constant">GCO_GetLabeling</span>獲取; %        <span class="hljs-number">2</span>):膨脹演算法中,標籤膨脹的順序可以通過函式<span class="hljs-constant">GCO_SetLabelOrder</span>來規定; %        <span class="hljs-number">3</span>):如果<span class="hljs-constant">GCO_SetNeighbors</span>沒有呼叫過,也就是說目標體<span class="hljs-constant">Handle</span>中沒有smoothness項 %             這個時候,膨脹演算法將會使用內部的greedy演算法,不進行圖割;</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li></ul></pre>

<h4 id="462gcoexpandonalpha函式說明">4.6.2:GCO_ExpandOnAlpha函式說明:</h4>

<p>這個函式只是對其中的某一類標籤進行膨脹計算,</p>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%----單獨對目標體進行alpha-expansion 步驟 %</span>       <span class="hljs-constant">GCO_ExpandOnAlpha</span>(<span class="hljs-constant">Handle</span>,<span class="hljs-constant">Alpha</span>) %          <span class="hljs-constant">Alpha</span>:需要進行膨脹操作的類標籤 <span class="hljs-number">4.6</span>.<span class="hljs-number">3</span>:<span class="hljs-constant">GCO_Swap</span>函式說明: <span class="hljs-string">%%-----對目標體進行alpha-beta-swap演算法操作:返回尋找的最小能量Energy %</span>  方式一: <span class="hljs-constant">GCO_Expansion</span>(<span class="hljs-constant">Handle</span>),執行alpha-beta-swap最小化當前能量--直到收斂 %  方式二: <span class="hljs-constant">GCO_Expansion</span>(<span class="hljs-constant">Handle</span>,<span class="hljs-constant">MaxIter</span>)      %          加入一個迭代執行次數:<span class="hljs-constant">MaxIter</span>: 該演算法在迭代<span class="hljs-constant">MaxIter</span>次後還沒有找到最小能量也停止 % note: <span class="hljs-number">1</span>):當前目標體的標籤可以通過函式<span class="hljs-constant">GCO_GetLabeling</span>獲取; %        <span class="hljs-number">2</span>):膨脹演算法中,標籤膨脹的順序可以通過函式<span class="hljs-constant">GCO_SetLabelOrder</span>來規定; %        <span class="hljs-number">3</span>):無論是label costs還是sparse data costs都是在當前alpha-beta-swap演算法下實現的</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li></ul></pre>

<h3 id="47獲得運算結果的標籤gcogetlabeling"><a name="t10"></a>4.7)獲得運算結果的標籤GCO_GetLabeling</h3>

<p>該函式直接執行後可以得到當前程式對於分割結果的標籤,函式說明:</p>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%----獲取當前目標體的類標籤 %</span>    用法: %       <span class="hljs-number">1</span>):<span class="hljs-constant">Labeling</span> = <span class="hljs-constant">GCO_GetLabeling</span>(<span class="hljs-constant">Handle</span>)返回整個目標體的標籤,列向量標籤; %       <span class="hljs-number">2</span>):<span class="hljs-constant">Labeling</span> = <span class="hljs-constant">GCO_GetLabeling</span>(<span class="hljs-constant">Handle</span>,i)得到具體某個點的標籤; %       <span class="hljs-number">3</span>):<span class="hljs-constant">Labeling</span> = <span class="hljs-constant">GCO_GetLabeling</span>(<span class="hljs-constant">Handle</span>,i,count)得到點i到i後面count個點的標籤;</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li></ul></pre>

<h3 id="48計算運算結果的能量gcocomputeenergy"><a name="t11"></a>4.8)計算運算結果的能量GCO_ComputeEnergy</h3>

<p>圖割演算法是以能量函式來作為衡量結果好壞的,圖割的能量函式中主要包括3部分的能量:資料項的能量、平滑項的能量以及標籤的能量。這些能量其實就是切割的邊權值之和,前面介紹過。至於標籤的能量,是衡量不同類之間的一種能量,正常情況下,你不去設定它也不會有,詳細的可以參考上述文獻。關於這個函式的說明:</p>

<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering"><span class="hljs-string">%%---  計算能量函式: %</span>     兩種方式:一): <span class="hljs-constant">E</span> = <span class="hljs-constant">GCO_ComputeEnergy</span>(<span class="hljs-constant">Handle</span>)  %                    返回當前標籤下的總能量<span class="hljs-constant">E</span> %              二):[<span class="hljs-constant">E</span> <span class="hljs-constant">D</span> <span class="hljs-constant">S</span> <span class="hljs-constant">L</span>] = <span class="hljs-constant">GCO_ComputeEnergy</span>(<span class="hljs-constant">Handle</span>) %                   返回總能量<span class="hljs-constant">E</span>  各部分的能量 </code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li></ul></pre>

<h3 id="49多餘的小函式gcodelete"><a name="t12"></a>4.9)多餘的小函式GCO_Delete</h3>

<p>該函式為釋放記憶體的函式,在程式執行完後加在後面與利於系統釋放記憶體。</p>

<p>至此該工具箱裡面的主要函式都講解完畢,那些函式說明其實就是對應函式裡面的英文說明幫助翻譯過來的,還有些小函式沒有用到,也就沒有翻譯,也都有說明。在下一次將會把這個工具箱用在一個例項上實驗實驗。</p>            </div>                         <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-f77b6af6fd.css" rel="stylesheet">                 </div>