使用HTML5技術實現Otsu算法(大津法)


本文主題

      情人節在網上看到國外JS牛人利用HTML5技術實現的一朵玫瑰花,深切的感受到HTML5技術的強大。本着學習的態度看了一下那朵玫瑰花的源代碼,其中用到的HTML5技術是canvas標簽,於是靈光一現,想試一下能不能進行圖像處理,結果成功了,再次介紹一下經驗。

      本文的思路是獲取一張帶有驗證碼的圖片,然后對其進行灰度化操作,完成后對其使用Otsu算法進行二值化操作,最后輸出二值化的圖片,其效果圖如下:

image

            圖1

最后友情提醒一下,HTML5技術在IE瀏覽器下面不支持或者是沒有全面支持,因此如要進行HTML5開發請使用火狐或者是谷歌等支持的瀏覽器。

顯示圖像

在頁面中引入canvas標簽,並設置其id屬性,在腳本中使用getElementById()來獲取標簽的句柄。

function drawImage(){
//獲取標簽的句柄
var canvas = document.getElementById('myCanvasElt');
//獲取繪圖的上下文
var ctx = canvas.getContext('2d');
//新建一個image,目的是為了讀取圖片
var img=new Image()
img.src="image/VerifyCode.jpg"
//將image中的圖片繪制到canvas中
ctx.drawImage(img,0,0);
}

以上代碼實現了圖1中原圖像的顯示。

灰值化圖像

對圖像進行灰值化的過程就是將一幅圖像的RGB三個屬性設為一致的過程,即去掉圖像的顏色信息,使用灰度信息表達圖像的內容。彩色轉灰度圖像有幾種算法:

1.加權平均法。Gray = R*0.299 + G*0.587 + B*0.114

2.平均值法。Gray = ( R + G + B )/3

3.最大值法。Gray  = Max(R,G,B)

其中R,G,B表示圖像三個分量的值,上述三種算法的好壞對比在這里不做解釋,詳細資料請參閱彩色圖像灰度化。本文采用第一種方法。

//彩色圖像灰度化
function ProcessToGrayImage(){
	var canvas = document.getElementById('myCanvasElt');
	var ctx = canvas.getContext('2d');	
	//取得圖像數據
        var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
	//這個循環是取得圖像的每一個點,在計算灰度后將灰度設置給原圖像
        for (var x = 0; x < canvasData.width; x++) {
	    for (var y = 0; y < canvasData.height; y++) {
	        // Index of the pixel in the array
	        var idx = (x + y * canvas.width) * 4;
	        // The RGB values
	        var r = canvasData.data[idx + 0];
	        var g = canvasData.data[idx + 1];
	        var b = canvasData.data[idx + 2];
	        //更新圖像數據
	        var gray = CalculateGrayValue(r , g , b);
	        canvasData.data[idx + 0] = gray;
	        canvasData.data[idx + 1] = gray;
	        canvasData.data[idx + 2] = gray;
	    }
	}
	ctx.putImageData(canvasData, 0, 0);
}
//計算圖像的灰度值,公式為:Gray = R*0.299 + G*0.587 + B*0.114
 function CalculateGrayValue(rValue,gValue,bValue){
 	   return parseInt(rValue * 0.299 + gValue * 0.587 + bValue * 0.114);
 	}	

Otsu算法

關於Otsu算法的具體理論在這里不再講解。這是一種二值化速度很快的圖像分割算法。后面會把該算法的理論詳細說明。使用javascript算法實現的過程如下:

//一維OTSU圖像處理算法
 function OTSUAlgorithm(){
   var m_pFstdHistogram = new Array();//表示灰度值的分布點概率
   var m_pFGrayAccu = new Array();//其中每一個值等於m_pFstdHistogram中從0到當前下標值的和
   var m_pFGrayAve = new Array();//其中每一值等於m_pFstdHistogram中從0到當前指定下標值*對應的下標之和
   var m_pAverage=0;//值為m_pFstdHistogram【256】中每一點的分布概率*當前下標之和
   var m_pHistogram = new Array();//灰度直方圖
   var i,j;
   var temp=0,fMax=0;//定義一個臨時變量和一個最大類間方差的值
   var nThresh = 0;//最優閥值
   //獲取灰度圖像的信息
   var imageInfo = GetGrayImageInfo();
   if(imageInfo == null){
     window.alert("圖像還沒有轉化為灰度圖像!");
     return;
   }
   //初始化各項參數
   for(i=0; i<256; i++){
     m_pFstdHistogram[i] = 0;
     m_pFGrayAccu[i] = 0;
     m_pFGrayAve[i] = 0;
     m_pHistogram[i] = 0;
   }
   //獲取圖像信息
   var canvasData = imageInfo[0];
   //獲取圖像的像素
   var pixels = canvasData.data;
   //下面統計圖像的灰度分布信息
   for(i=0; i<pixels.length; i+=4){
      //獲取r的像素值,因為灰度圖像,r=g=b,所以取第一個即可
      var r = pixels[i];
      m_pHistogram[r]++;
   }
   //下面計算每一個灰度點在圖像中出現的概率
   var size = canvasData.width * canvasData.height;
   for(i=0; i<256; i++){
      m_pFstdHistogram[i] = m_pHistogram[i] / size;
   }
   //下面開始計算m_pFGrayAccu和m_pFGrayAve和m_pAverage的值
   for(i=0; i<256; i++){
      for(j=0; j<=i; j++){
        //計算m_pFGaryAccu[256]
		m_pFGrayAccu[i] += m_pFstdHistogram[j];
		//計算m_pFGrayAve[256]
		m_pFGrayAve[i] += j * m_pFstdHistogram[j];
      }
      //計算平均值
	  m_pAverage += i * m_pFstdHistogram[i];
   }
   //下面開始就算OSTU的值,從0-255個值中分別計算ostu並尋找出最大值作為分割閥值
   for (i = 0 ; i < 256 ; i++){
		temp = (m_pAverage * m_pFGrayAccu[i] - m_pFGrayAve[i]) 
		     * (m_pAverage * m_pFGrayAccu[i] - m_pFGrayAve[i]) 
		     / (m_pFGrayAccu[i] * (1 - m_pFGrayAccu[i]));
		if (temp > fMax)
		{
			fMax = temp;
			nThresh = i;
		}
	}
   //下面執行二值化過程 
   for(i=0; i<canvasData.width; i++){
      for(j=0; j<canvasData.height; j++){
         //取得每一點的位置
         var ids = (i + j*canvasData.width)*4;
         //取得像素的R分量的值
         var r = canvasData.data[ids];
         //與閥值進行比較,如果小於閥值,那么將改點置為0,否則置為255
         var gray = r>nThresh?255:0;
         canvasData.data[ids+0] = gray;
         canvasData.data[ids+1] = gray;
         canvasData.data[ids+2] = gray;
      }
   }
   //顯示二值化圖像
   var newImage = document.getElementById('myCanvasThreshold').getContext('2d');
   newImage.putImageData(canvasData,0,0);
 }	
 
 //獲取圖像的灰度圖像的信息
 function GetGrayImageInfo(){
    var canvas = document.getElementById('myCanvasElt');
	var ctx = canvas.getContext('2d');
	var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
	if(canvasData.data.length==0){
	  return null;
	}
	return [canvasData,ctx];
 }
 //下面對灰度圖像進行處理,將目標信息分割出來
 function DividedTarget(){
   //讀取二值化圖像信息
   var imageInfo = document.getElementById('myCanvasThreshold');
   if(imageInfo == null){
     window.alert("沒有發現二值化圖像信息!");
     return;
   }
   //取得上下文
   var ctx = imageInfo.getContext('2d');
   //獲取圖像數據
   var canvasData = imageInfo.getImageData(0, 0, ctx.width, ctx.height);
   var newVanvasData = canvasData;
   //取得圖像的寬和高
   var width = canvasData.width;
   var height = canvasData.height;
   //算法開始
   var cursor = 2;
   for(var x=0; x<width; x++){
      for(var y=0; y<height; y++){
         //取得每一點的位置
         var ids = (x + y*canvasData.width)*4;
         //取得像素的R分量的值
         var r = canvasData.data[ids];
         //如果是目標點
         if(r==0){
            
         }
      }
   }
   
 }

      算法的實現中給出了詳細的注釋,如果有不清楚的地方可以留言或者發郵件咨詢,值得注意的是,由於js不允許跨域訪問的特性,要想成功運行上面的代碼,必須將它運行在一個服務器中,比如說Apache。

代碼詳單

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>gray.html</title>
	
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
    <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
<script type="text/javascript">
function ProcessToGrayImage(){
	var canvas = document.getElementById('myCanvasElt');
	var ctx = canvas.getContext('2d');
	var img=new Image()
	img.src="image/VerifyCode.jpg"
	ctx.drawImage(img,0,0);
	
	var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
	for (var x = 0; x < canvasData.width; x++) {
	    for (var y = 0; y < canvasData.height; y++) {
	        // Index of the pixel in the array
	        var idx = (x + y * canvas.width) * 4;
	        // The RGB values
	        var r = canvasData.data[idx + 0];
	        var g = canvasData.data[idx + 1];
	        var b = canvasData.data[idx + 2];
	        // Update the values of the pixel;
	        var gray = CalculateGrayValue(r , g , b);
	        canvasData.data[idx + 0] = gray;
	        canvasData.data[idx + 1] = gray;
	        canvasData.data[idx + 2] = gray;
	    }
	}
	ctx.putImageData(canvasData, 0, 0);
}
//計算圖像的灰度值,公式為:Gray = R*0.299 + G*0.587 + B*0.114
 function CalculateGrayValue(rValue,gValue,bValue){
 	   return parseInt(rValue * 0.299 + gValue * 0.587 + bValue * 0.114);
 	}	
 //一維OTSU圖像處理算法
 function OTSUAlgorithm(){
   var m_pFstdHistogram = new Array();//表示灰度值的分布點概率
   var m_pFGrayAccu = new Array();//其中每一個值等於m_pFstdHistogram中從0到當前下標值的和
   var m_pFGrayAve = new Array();//其中每一值等於m_pFstdHistogram中從0到當前指定下標值*對應的下標之和
   var m_pAverage=0;//值為m_pFstdHistogram【256】中每一點的分布概率*當前下標之和
   var m_pHistogram = new Array();//灰度直方圖
   var i,j;
   var temp=0,fMax=0;//定義一個臨時變量和一個最大類間方差的值
   var nThresh = 0;//最優閥值
   //獲取灰度圖像的信息
   var imageInfo = GetGrayImageInfo();
   if(imageInfo == null){
     window.alert("圖像還沒有轉化為灰度圖像!");
     return;
   }
   //初始化各項參數
   for(i=0; i<256; i++){
     m_pFstdHistogram[i] = 0;
     m_pFGrayAccu[i] = 0;
     m_pFGrayAve[i] = 0;
     m_pHistogram[i] = 0;
   }
   //獲取圖像信息
   var canvasData = imageInfo[0];
   //獲取圖像的像素
   var pixels = canvasData.data;
   //下面統計圖像的灰度分布信息
   for(i=0; i<pixels.length; i+=4){
      //獲取r的像素值,因為灰度圖像,r=g=b,所以取第一個即可
      var r = pixels[i];
      m_pHistogram[r]++;
   }
   //下面計算每一個灰度點在圖像中出現的概率
   var size = canvasData.width * canvasData.height;
   for(i=0; i<256; i++){
      m_pFstdHistogram[i] = m_pHistogram[i] / size;
   }
   //下面開始計算m_pFGrayAccu和m_pFGrayAve和m_pAverage的值
   for(i=0; i<256; i++){
      for(j=0; j<=i; j++){
        //計算m_pFGaryAccu[256]
		m_pFGrayAccu[i] += m_pFstdHistogram[j];
		//計算m_pFGrayAve[256]
		m_pFGrayAve[i] += j * m_pFstdHistogram[j];
      }
      //計算平均值
	  m_pAverage += i * m_pFstdHistogram[i];
   }
   //下面開始就算OSTU的值,從0-255個值中分別計算ostu並尋找出最大值作為分割閥值
   for (i = 0 ; i < 256 ; i++){
		temp = (m_pAverage * m_pFGrayAccu[i] - m_pFGrayAve[i]) 
		     * (m_pAverage * m_pFGrayAccu[i] - m_pFGrayAve[i]) 
		     / (m_pFGrayAccu[i] * (1 - m_pFGrayAccu[i]));
		if (temp > fMax)
		{
			fMax = temp;
			nThresh = i;
		}
	}
   //下面執行二值化過程 
   for(i=0; i<canvasData.width; i++){
      for(j=0; j<canvasData.height; j++){
         //取得每一點的位置
         var ids = (i + j*canvasData.width)*4;
         //取得像素的R分量的值
         var r = canvasData.data[ids];
         //與閥值進行比較,如果小於閥值,那么將改點置為0,否則置為255
         var gray = r>nThresh?255:0;
         canvasData.data[ids+0] = gray;
         canvasData.data[ids+1] = gray;
         canvasData.data[ids+2] = gray;
      }
   }
   //顯示二值化圖像
   var newImage = document.getElementById('myCanvasThreshold').getContext('2d');
   newImage.putImageData(canvasData,0,0);
 }	
 
 //獲取圖像的灰度圖像的信息
 function GetGrayImageInfo(){
    var canvas = document.getElementById('myCanvasElt');
	var ctx = canvas.getContext('2d');
	var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
	if(canvasData.data.length==0){
	  return null;
	}
	return [canvasData,ctx];
 }
 //下面對灰度圖像進行處理,將目標信息分割出來
 function DividedTarget(){
   //讀取二值化圖像信息
   var imageInfo = document.getElementById('myCanvasThreshold');
   if(imageInfo == null){
     window.alert("沒有發現二值化圖像信息!");
     return;
   }
   //取得上下文
   var ctx = imageInfo.getContext('2d');
   //獲取圖像數據
   var canvasData = imageInfo.getImageData(0, 0, ctx.width, ctx.height);
   var newVanvasData = canvasData;
   //取得圖像的寬和高
   var width = canvasData.width;
   var height = canvasData.height;
   //算法開始
   var cursor = 2;
   for(var x=0; x<width; x++){
      for(var y=0; y<height; y++){
         //取得每一點的位置
         var ids = (x + y*canvasData.width)*4;
         //取得像素的R分量的值
         var r = canvasData.data[ids];
         //如果是目標點
         if(r==0){
            
         }
      }
   }
   
 }
</script>
  </head>
  
  <body>
  原圖像:<br/>
  <img src="image/VerifyCode.jpg" /><br/>
  灰度圖像:<input type="button" value="處理" onclick="javascript:ProcessToGrayImage();" /><br/>
  <canvas id="myCanvasElt" width="200" height="100"></canvas><br/>
  二值化圖像:<input type="button" value="二值化" onclick="javascript:OTSUAlgorithm();" /><br/>
  <canvas id="myCanvasThreshold" width="200" height="100"></canvas><br/>
  </body>
</html>

總結說明

      HTML5的新特性,為前端開發人員提供了更加廣闊的施展空間,js對圖像處理方面的支持使得算法的實現更加方便、快捷。

      由於時間的不足,本來是想實現驗證碼的分割識別的。現在剩下的就只有目標提取方面了,在識別方面,我已經做好了大小寫字母和數字的16x16的二進制模板,如果哪位有興趣的話可以給我留言或發電子郵件,索取這個信息。

by Rush

2012年2月24日 10:26:19


免責聲明!

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



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