顯示具有 HTML5 標籤的文章。 顯示所有文章
顯示具有 HTML5 標籤的文章。 顯示所有文章

2014年11月12日 星期三

HTML5 Canvas的影像處理

  還記得在前幾篇知識文件中所介紹過的Canvas繪圖功能嗎?透過HTML5的Canvas繪圖功能,我們可以即時的產生圖形、並做出各種各種動態的圖形效果,而這些效果在以往都是需要透過第三方軟體協助才能夠作到。
  在這篇文章中,我們來試看看Canvas的另一個重要功能:影像處理,透過Canvas對於影像的支援能力,使得網頁的圖形處理不再需要依賴Server端來執行,我們在Client端利用javascript就能直接取用使用者的相片、Web Camera影像、網頁上的圖片等等,這不但減少了網路的傳輸與伺服器的負擔之外,網頁開發者也可以直接使用HTML加上javascript這樣的方式直接在用戶端就完成影像的處理,而不需要再另外依賴或學習撰寫伺服端的處理程式,最重要的一個好處是,我們不再需要利用第三方軟體,造成使用者在調用client端元件時會發生的種種繁雜認證與確認動作。

一)Canvas影像處理的基本步驟:

  在開始利用Canvas來處理影像之前,我們必須A) 先建立一個影像物件並開始B) 讀入影像的資料,當影像C) 讀入完畢後,便會觸發一個onload的事件,此時,我們便可在該事件的函式中,將讀入的影像資料繪製到Canvas物件中,開始進行相關的處理工作。
    這個基本步驟可寫成Javascript如下:





  接下來我們以凌陽創新網站上的圖片為例,使用Canvas提供的drawImage()方法針對該影像進行最基本的直接繪製、尺寸修改、影像截取等三種處理來作示範。

A)直接複製:














B)執行結果:

我們會發現網頁上有一張一模一樣的圖片(不是直接用連結的哦)。

C)尺寸修改:

要將圖片的尺寸縮小或放大,只要將ctx.drawImage(image, 0, 0); 這行再加入兩個參數,改為 ctx.drawImage(image, 0, 0, 寬度, 高度);就可以了,例如我們把圖片縮小為200x50如下:

D)截取部份影像:

    截取影像上的某個區域也是使用drawImage()這個方法,但是要額外給予影像的開始截取座標位置(sx, sy)以及截取的寬度(sw)和高度(sh),如下例:   
    ctx.drawImage(image, 0 ,0 ,350, 200, 0, 0, 350, 200);
我們截取了從(0,0)位置、寬長為350x200區塊的圖片(剛好為鍵盤的部份),並繪製在0,0的區域,繪製的圖形尺寸為350x200,執行結果如下:

二)Canvas像素處理:

  在Canvas所繪製的圖形或影像是作為一個整體的點陣圖(Bitmap)方式儲存的,我們我們可以造訪各個像素的資訊,換句話說,我們可以使用Javascript處理Canvas上的像素資訊,這是Canvas的一個很重要的特色。
  我們使用getImageData()以及putImageData()兩種方法針對drawImage()所繪製的影像來進行像素的處理,而這些像素資訊是用一維陣列的方式存放在CanvasPixelArray當中;在開始說明如何使用getImageData()及putImageData()方法之前,我們必須先瞭解實際像素和這些存放在CanvasPixelArray陣列中的像素資訊之間的關係。
  如左圖,影像上的每一個像素都會擁有四個數值,它們分別代表RGB三種顏色以及Alpha(透明度),這些數值的組合形成了最終每個像素的色彩及透明度;我們以下方圖形的橘色方框區塊為例,它的長寬分別是119px, 69px, 所以這個區塊總共有8211個像素,但是由於每個像素有四種數值,因此放到一維的CanvasPixelArray之後,總共會有32844個數值,所以,如果我們要找出某個像素的資訊,就要先確定該像素資訊是在陣列中的第幾筆到第幾筆。
    因此,當我們要得到一張圖片上(x,y)這個像素的影像資訊時,我們很容易就可找出這個公式:(注意,x,y都是從0,0開始)
Red =  (x + y * 影像寬度) * 4
Green =  (x + y * 影像寬度) * 4 + 1
Blue =  (x + y * 影像寬度) * 4 + 2
Alpha =  (x + y * 影像寬度) * 4 + 3
    知道影像的實際像素與存放資訊陣列之間的關係後,下面我們用一些實例來作一些練習。

A)變更R、G、B或Alpha值:

    請將下方程式碼放置於HTML的<body>標籤內,試著執行看看:














執行結果應該如下,您會看到圖形變充滿了藍色調:
這是因為我們把Red與Green兩個色彩的值變小,Blue的值奱大的結果,您可以試看看,隨意更改這些數值,看看會出現什麼結果!

B)將彩色圖片轉為黑白:

    如果我們將每一個像素的RGB三種數值皆設定為此其RGB值的平均,那麼,你會發現整張相片都變成黑白了,因為相同比重的三種RGB顏色混合後便會變成黑色,這是最快速的彩色轉黑白的方法,當然,要把黑白相片轉得更完美,應該還要考慮到加重光影的比例,但這部份與本文無關就先略過不談。
  我們修改上面範例中迴圈內的部份程式如下:





        執行結果,真的變成黑色的圖形了!

三)Canvas與外部設備的搭配:

看到這裏,你有沒有想到,既然Canvas可以抓取某個圖形來進行重繪及處理,那麼,web camera以及網頁上播放中的影片豈不是也可以如法炮製嗎?的確,透過HTML5的Canvas,我們可以對播放中的影片加入動態的效果或字幕,可以將web camera的即時畫面重繪到Canvas進行處理及判斷,作出類似人臉偵測、移動偵測等等功能,甚至於也可以進行現在流行的Gesture Recognition,換句話說,透過Canvas搭配HTML 5的影像支援,可以作出很多令人驚奇的新功能。

2014年10月14日 星期二

HTML5 Canvas繪圖

    Canvas是HTML5的新元素之一,它所提供的API可讓我們動態產生顯示圖形、圖表、影像和動畫等等,以往我們要在網頁上製造出這類的效果,只能依賴Flash或Silverlight的外掛程式,但是自從HTML5出現後,我們就可以直接使用Javascript及標籤語法來繪製圖形或產生動畫效果了。
    HTML5繪製圖形的方式有兩種技術:Canvas與SVG,兩者最大的差異為Canvas是點陣圖而Canvas是向量圖形式。   

Canvas
SVG
圖形格式
點陣圖
向量圖
放大支援
放大到一定程度後會模糊
無論放大多少倍清晰度不變
處理效能
大型及複雜圖檔會導致處理速度降低及記憶體使用增加
大型及複雜圖檔對於系統效能影響不顯著
精細繪圖
可繪製較精密細緻複雜的圖形
無法針對像素操作,因此無法製作過於複雜精密的圖形
相對的Adobe工具
Illustrator
Photoshop
  Canvas並不困難,只要有基本的HTML及Javascript概念就可以很快上手,下面我們開始來示範如何使用HTML5 Canvas來進行圖形繪製,如果您有興趣的話,可以在自己的電腦安裝LAMP軟體,並且用文字編輯軟體來編寫範例中的HTML和Javascript碼來試試。
本文介紹的是Canvas基本繪圖及文字顯示,前面所使用的範例來自於http://www.html5canvastutorials.com/,這個網站對於Canvas有非常清楚的用法介紹,另外,最後一個稍複雜的圖形繪製範例則來自於http://www.williammalone.com/articles/html5-canvas-example/,這個範例使用到的技巧都是前方所介紹過的,相當適合作為練習使用;其實Canvas不僅提供繪圖功能,它還能處理影像圖片以及製作動畫,得力於Canvas,讓HTML5能夠開發出各種驚人的動態效果與遊戲,而且支援各類的手持設備,這部份留待以後再來專文介紹。  
  1. Canvas的基本用法

  要使用Canvas時,我們需先在HTML中宣告一個<canvas>標籤,然後透過Javascript針對此canvas物件進行繪製,其步驟如下:
    • 在HTML中宣告一個Canvas物件
    • 取得HTML中宣告的Canvas 我們可以想像為取得一個畫板
    • 取得Canvas的Context物件 我們可以想像為取得畫筆
    • 透過Context物件提供的方法及屬性進行繪圖 開始作畫
  • 範例:繪製矩形

   我們將繪製如下的兩個矩形

繪製矩形方式:
步驟:1.建立Canvas html 標籤 → 2.使用javascript取得Canvas物件 → 3.使用javascript取得Canvas的2D context物件 → 4.使用fillStyle屬性設定顏色 5.使用fillRect指令繪製出矩形
程式碼:(請對照上述1 ~ 5 的步驟)
 





說明:
        在上圖中,我們繪製了一紅一藍兩個矩形,其中藍色的透明度為0.5。
      1. var ctx = canvas.getContext("2d");
取得繪製2d圖形的畫筆
      1. ctx.fillStyle = "#fa1005";
設定填充的顏色 (亦可使用其它顏色的格式,如rgb(255,0,0)或直接用 red)
      1. ctx.fillRect(50,50,200,200);
畫出矩形,對角線為x,y座標軸(50,50)到(200,200)的紅色矩形,請參考下方的示意圖。
      1. ctx.fillStyle = "rgba(0,0,255,0.5)";
藍色矩形繪製方式同上,但設定填充的顏色的指令不同 (rgba(0,0,255,0.5)的第四個參數0.5指的是不透明度,1代表完全不透明)
  1. 繪製路徑:以三角形為例

所謂的路徑就是通常我們所說的「一筆繪圖」,首尾封閉的一種圖形,在下面的範例中我們以三角形為例,示範如何利用Canvas路徑繪製圖形,其它任何可以一筆畫完的封閉圖形也是如下的作法。
繪製三角形方式:
步驟:1.建立Canvas html 標籤 → 2.使用javascript取得Canvas物件 → 3.使用javascript取得Canvas的2D context物件 → 4.使用strokeStyle設定線條顏色 5.使用moveTo()將啟始點移到(x1,y1) 6.呼叫lineTo ()方法繪製直線到(x2,y2) → 7.呼叫lineTo ()方法繪製直線到(x3,y3) →8.呼叫lineTo ()方法繪製直線到(x1,y1) → 9. 呼叫stroke()將線條繪出
程式碼:(請對照上述1 ~ 7 的步驟)











執行結果:
繪製說明:
1. 啟始點移到(100,100)
2. 畫線到(300,100)
3. 再畫到(200,200)
4. 再畫到啟始點(100,100)
  1. 繪製弧形與圓形

上面介紹了繪製路徑的方法moveTo()與lineTo(),下面我們來看看繪製弧形和圓形的方法arc(),其語法如下:
        context.arc(x ,y ,半徑 ,開始角度 ,結束角度 ,是否逆時針方向)
    弧形和圓形的語法都是使用arc,兩者的差異在在於繪製圓形時只要將開始角度設為0,結束角度設為2л(或360)就可以畫出圓形。
    另外要注意的是,在arc()中的角度指的是「弧度(radian)」而不是「度(degree)」,所以我們在傳入此參數時要先利用這個轉換公式來處理:弧度=角度*Math.PI / 180
畫一個從100度順時針到230度的弧形:
   










    執行結果:
startAngle和endAngle分別改為0和360,就可以畫出圓形:
  1. 繪製拋物線

我們可以利用quadraticCurveTo()繪製二元拋物線,這個方法需要傳入四個參數:
    context.quadraticCurveTo( cpx, coy, x, y)
步驟:1. beginPath()宣佈開始繪製 2. 使用moveTo()移到啟始點 3. 使用quadraticCurveTo()繪製拋物線 3. 可透過lineWidth()及strokeStyle分別定義線寬及顏色 4. 最後使用stroke()將圖形畫出來










那如果我們要畫的是三元拋物線(貝茲曲線)呢,也很簡單,它只比起二元拋物線要多傳入一個控制點:
context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
繪製三元拋物線的步驟與二元拋物線相同,僅在於使用的函數及傳入參數有所差異:
步驟:1. beginPath()宣佈開始繪製 2. 使用moveTo()移到啟始點 3. 使用bezierCurveTo ()繪製拋物線 3. 可透過lineWidth()及strokeStyle分別定義線寬及顏色 4. 最後使用stroke()將圖形畫出來










  1. 繪製文字

Canvas除了可以繪製圖形外,亦可以繪製文字,它提供了兩種繪製文字的方式:實心及空心文字
實心文字:context.fillText( text ,x ,y ,maxWidth )
    空心文字:context.strokeText( text ,x ,y ,)
    text → 要顯示的文字
    x, y → 顯示的座標位置
    maxWidth → 此值為可選擇性輸入,若有輸入此值則文字寬度將會限制在指定的數值之內
基本上,我們只要使用上述的fillText()或strokeText()便可以將文字顯示在Canvas上了,但通常會再搭配下列常用的參數指令:
    設定字形 → context.font = 'italic 40pt Calibri';
        設定顏色 → context.fillStyle = 'blue';
        水平對齊方式 → context.textAlign = 'center';
        垂直對齊方式 → context.textBaseline = 'middle';
        使用strokeText()來顯示空心字時:
設定線條寬度:context.lineWidth = 3;
設定線條顏色:context.strokeStyle = 'blue';















步驟:1. 設定相關字形參數 2. 使用fillText()或strokeText()繪出字串
  1. 綜合練習

接下來,我們用Canvas來繪出下面這個圖形作為本單元的結束。
Step 7
A)先產生一張165x145大小的畫布:
        <canvas id="canvasId" width="165" height="145"></canvas>
B)取得Canvas 2D的context(可看成畫筆)
        var context = document.getElementById("canvasId").getContext("2d");
    C)產生一個三角形並著色
var width = 125;  //三角形的寬度
var height = 105; //三角形的高度
var padding = 20;  //三角形周圍的空白間隔

// 開始畫路徑
context.beginPath();
context.moveTo(padding + width/2, padding);        // 三角形中間的點
context.lineTo(padding + width, height + padding); // Bottom Right
context.lineTo(padding, height + padding);         // Bottom Left
context.closePath();

// 塗上顏色
context.fillStyle = "#ffc821";
context.fill();
   
        此時我們會得到如下的圖形:
Step 1
您可以看看下方的座標說明會更清楚:
    D)加上漸層效果
        我們將左側的程式碼更改為右側的內容;設定線性漸層的指令為:
createLinearGradient(x1,x1,y2,y2) 這四個參數表示我們要由(x1,y1)至(x2,y2)產生漸層效果。
addColorStop(n,Color) 指定在比例為n的位置顯示顏色為Color的漸層,其中n為介於0~1的數值。







    此時顯示的效果如下:
           
                Step 2
        E)加入陰影效果
我們要替此三角形加上陰影效果,讓它變得更立體,請加入下方的程式碼在塗上顏色程式碼區段之前
// 加入陰影效果
context.shadowBlur = 10;  // 模糊程度,此數值愈大愈模糊
context.shadowColor = "black"; // 陰影顏色
   
// 塗上顏色

            此時顯示的效果如下:
    Step 3
        E)加入第二個漸層效果
我們在原先的漸層上再加入一個漸層色可讓圖形顯得比較生動不至於太呆板;一樣也是加在塗上顏色程式碼區段之前:
// 水平的漸層,但塗色位置與原先的不同
gradient = context.createLinearGradient(0,padding,0,padding+height);
gradient.addColorStop(0.5, "transparent");
gradient.addColorStop(0.5, "#dcaa09");
gradient.addColorStop(1, "#faf100");

// 塗上顏色

            此時顯示的效果如下:
Step 4
        F)替三角形加上黑框
在下方的程式碼中,我們比較陌生的是lineJoin,這個屬性可以設定兩條線接點的形式,在此我們設定為圓邊。
// 加上黑框
context.lineWidth = 5;
context.lineJoin = "round";   
context.strokeStyle = "#333";
context.stroke();
   
        此時圖片會顯示如下:
                    Step 5
        G)替黑框再加上黃色外框
  我們接下來將黃色外框加在黑框外,會感覺黑框好像是畫在板子裏,會更有擬真的效果;其作法與黑框完全相同。
context.lineWidth = 20;
context.lineJoin = "round";   
context.strokeStyle = gradient;
context.stroke();

        此時圖片會顯示如下:















         H)加入文字:驚嘆號
最後,我們在中間區域用fillText指令來加上驚嘆號就完成了。

context.textAlign = "center";
context.textBaseline = "middle";
context.font = "bold 60px 'Times New Roman', Times, serif";
context.fillStyle = gradient;
try{
   context.fillText("!", canvasWidth/2, padding + height/1.5);
}catch(ex){}


         I)成果