使用JavaScript實現(xiàn)二值化圖像
黑白二值圖像的像素只有黑色和白色兩種顏色。將圖像轉(zhuǎn)換為黑白有多種用途:
- 減少文件大?。号c24位彩色圖像相比,1位黑白圖像所需的存儲數(shù)據(jù)通常更少。
- 圖像處理:許多圖像處理算法需要先將圖像轉(zhuǎn)換為二值圖。
- 用于顯示和打?。阂恍┹斎?輸出設備,如激光打印機、傳真機和顯示器,只能處理二值圖。
- 美學:二值圖像的像素明顯,可以算作一種像素藝術。
將圖像轉(zhuǎn)換為黑白的過程稱為閾值處理(thresholding),通常執(zhí)行以下操作:
- 將圖像轉(zhuǎn)換為灰度
- 如果像素的灰度值小于閾值,則將該像素替換為黑色;如果大于閾值,則將該像素替換為白色
轉(zhuǎn)換為黑白的示例圖像:
在本文中,我們將討論使用JavaScript將圖像轉(zhuǎn)換為黑白二值圖的兩種方法。
- Canvas
- Dynamic Web TWAIN ,一個文檔掃描SDK
編寫一個HTML5頁面以轉(zhuǎn)換圖像為黑白
使用以下模板創(chuàng)建一個新的HTML5頁面,然后讓我們?yōu)槠涮砑訄D像顏色轉(zhuǎn)換功能。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Convert an Image to Black and White</title> <style> </style> </head> <body> <div class="home"> <h2>Convert an Image to Black and White</h2> <button id="convertButton">Convert</button> </div> <script> </script> </body> </html>
加載圖片
添加用于選擇文件的input
元素,并使用按鈕觸發(fā)它。圖片將被加載到兩個img元素中,一個用于顯示轉(zhuǎn)換的結(jié)果,另一個用于存儲原始圖像。
HTML:
<button id="loadFileButton">Load a File</button> <input style="display:none;" type="file" id="file" onchange="loadImageFromFile();" accept=".jpg,.jpeg,.png,.bmp" /> <div class="imageContainer"> <img id="image"/> <img id="imageHidden"/> </div> <style> .imageContainer { max-width: 50%; } #image { max-width: 100%; } #imageHidden { display: none; } </style>
JavaScript:
function loadImageFromFile(){ let fileInput = document.getElementById("file"); let files = fileInput.files; if (files.length == 0) { return; } let file = files[0]; fileReader = new FileReader(); fileReader.onload = function(e){ document.getElementById("image").src = e.target.result; document.getElementById("imageHidden").src = e.target.result; }; fileReader.onerror = function () { console.warn('oops, something went wrong.'); }; fileReader.readAsDataURL(file); }
使用Canvas將圖像轉(zhuǎn)換為黑白
HTML5提供了一個canvas
標簽,我們可以用它操作圖像數(shù)據(jù)。我們可以使用它將圖像轉(zhuǎn)換為黑白。
向頁面添加一個隱藏的canvas
元素。
<canvas id="canvasHidden"></canvas> <style> #canvasHidden { display: none; } </style>
添加用于指定旋轉(zhuǎn)閾值的input
元素。
<div class="thresholdControls" style="display:inline"> <label> Threshold (0-255): <input id="threshold" type="number" min="0" max="255" value="127"> </label> </div>
將canvas的大小設置為圖像的大小。
const image = document.getElementById("image"); const canvas = document.getElementById("canvasHidden"); canvas.width = image.naturalWidth; canvas.height = image.naturalHeight;
獲取canvas的context以執(zhí)行操作。
const context = canvas.getContext("2d");
將圖像繪制到canvas上。
context.drawImage(image, 0, 0);
獲取圖像的ImageData
:
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
ImageData
以RGBA順序?qū)⑾袼刂荡鎯υ?code>Uint8ClampedArray中,取值范圍是0到255。像素從左上角到右下角逐行排列。
創(chuàng)建一個函數(shù)以根據(jù)RGB值計算灰度值。
//https://github.com/image-js/image-js/blob/9ab86a86f6c13a9a7d14c62566c1396c3c6f54f4/src/image/transform/greyAlgorithms.js function RGBToGrayScale(red,green,blue){ //return red * 0.2126 + green * 0.7152 + blue * 0.0722; return (red * 6966 + green * 23436 + blue * 2366) >> 15; }
計算灰度值的方法有很多。這里,我們使用Rec. 709亮度系數(shù)
進行轉(zhuǎn)換。使用基于整數(shù)的乘法計算和移位優(yōu)化了計算速度。
創(chuàng)建一個函數(shù)以根據(jù)閾值確定像素值應該為黑色還是白色。
//return true if the value should be black. return false if the value should be white function threshold(grayscale){ const thresholdValue = parseInt(document.getElementById("threshold").value); if (grayscale < thresholdValue) { return true; }else{ return false; } }
遍歷所有像素,將RGB值設置為我們計算出的黑白值,黑色是0,白色是255。
const pixels = imageData.data; //[r,g,b,a,...] for (var i = 0; i < pixels.length; i += 4) { const red = pixels[i]; const green = pixels[i + 1]; const blue = pixels[i + 2]; const grayscale = RGBToGrayScale(red, green, blue) if (threshold(grayscale)) { pixels[i] = 0; pixels[i + 1] = 0; pixels[i + 2] = 0; }else{ pixels[i] = 255; pixels[i + 1] = 255; pixels[i + 2] = 255; } }
放回ImageData
。
context.putImageData(imageData, 0, 0);
顯示轉(zhuǎn)換后的圖像。
image.src = canvas.toDataURL();
使用OTSU方法確定閾值
我們可以使用OTSU方法自動確定分離背景和前景的最佳閾值。
在頁面中包含以下JS文件。
//https://github.com/cawfree/otsu/ const histo = (data, bins) => data.reduce((arr, e) => { arr[bins.indexOf(e)] += 1; return arr; }, [...Array(bins.length)].fill(0)); const width = (hist, s, e) => { let v = 0; for (let i = s; i < e; i += 1) { v += hist[i]; } return v; }; const bins = data => Array.from(new Set(data)).sort((e0, e1) => e0 - e1); const weight = (hist, s, e, total) => { let v = 0; for (let i = s; i < e; i += 1) { v += hist[i]; } return v / total; }; const mean = (hist, bins, s, e, width) => { let v = 0; for (let i = s; i < e; i += 1) { v += hist[i] * bins[i]; } return v * width; }; const variance = (hist, bins, s, e, mean, width) => { let v = 0; for (let i = s; i < e; i += 1) { const d = bins[i] - mean; v += d * d * hist[i]; } return v * width; }; const cross = (wb, vb, wf, vf) => wb * vb + wf * vf; const otsu = (data) => { const b = bins(data); const h = histo(data, b); const { length: total } = data; const vars = [...Array(b.length)].map((_, i) => { const s0 = 0; const e0 = i; const s1 = i; const e1 = h.length; const w0 = 1 / width(h, s0, e0); const w1 = 1 / width(h, s1, e1); const wb = weight(h, s0, e0, total); const vb = variance(h, b, s0, e0, mean(h, b, s0, e0, w0), w0); const wf = weight(h, s1, e1, total); const vf = variance(h, b, s1, e1, mean(h, b, s1, e1, w1), w1); const x = cross(wb, vb, wf, vf); return !isNaN(x) ? x : Number.POSITIVE_INFINITY; }); return b[vars.indexOf(Math.min(...vars))]; };
使用OTSU計算閾值。我們需要將灰度直方圖傳遞給otsu
函數(shù)。
function calculateThresholdWithOTSU(){ const image = document.getElementById("imageHidden"); const canvas = document.getElementById("canvasHidden"); const width = image.naturalWidth; const height = image.naturalHeight; const context = canvas.getContext('2d'); canvas.width = width; canvas.height = height; context.drawImage(image, 0, 0); const imageData = context.getImageData(0, 0, canvas.width, canvas.height); const pixels = imageData.data; //[r,g,b,a,...] const grayscaleValues = []; for (var i = 0; i < pixels.length; i += 4) { const red = pixels[i]; const green = pixels[i + 1]; const blue = pixels[i + 2]; const grayscale = RGBToGrayScale(red, green, blue) grayscaleValues.push(grayscale); } document.getElementById("threshold").value = otsu(grayscaleValues); }
使用Dynamic Web TWAIN將圖像轉(zhuǎn)換為黑白
Dynamic Web TWAIN是一個文檔掃描SDK,可以在瀏覽器中掃描文檔。它提供了各種圖像處理方法。我們可以使用其ConvertToBW方法轉(zhuǎn)換圖像為黑白。
使用它的優(yōu)點是,它可以用于批量處理大量圖像,因為處理是使用本地進程完成的。由于使用了libpng等本地圖像庫根據(jù)顏色信息輸出文件,輸出文件的尺寸也會更優(yōu)。
以下是使用它的步驟:
在頁面中引入Dynamic Web TWAIN。
<script src="https://unpkg.com/dwt@18.4.2/dist/dynamsoft.webtwain.min.js"></script>
初始化一個Web TWAIN的實例并使用它來轉(zhuǎn)換圖像。可以在此處申請其許可證。
let DWObject; Dynamsoft.DWT.AutoLoad = false; Dynamsoft.DWT.ProductKey = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="; //one-day trial Dynamsoft.DWT.ResourcesPath = "https://unpkg.com/dwt@18.4.2/dist"; async function convertWithDWT(){ if (!DWObject) { await initDWT(); } DWObject.RemoveAllImages(); let response = await fetch(document.getElementById("imageHidden").src); let buffer = await response.arrayBuffer(); DWObject.LoadImageFromBinary(buffer, function(){ const convert = async () => { const thresholdValue = parseInt(document.getElementById("threshold").value); await DWObject.RunCommandAsync({command:"convertToBW",parameter:{index:0,threshold:thresholdValue}}); document.getElementById("image").src = DWObject.GetImageURL(0); } convert(); }, function(errorCode, errorString){ console.log(errorString); }) } function initDWT(){ return new Promise((resolve, reject) => { const title = document.querySelector("h2").innerText; document.querySelector("h2").innerText = "Loading Dynamic Web TWAIN..."; Dynamsoft.DWT.CreateDWTObjectEx( { WebTwainId: 'dwtcontrol' }, function(obj) { DWObject = obj; document.querySelector("h2").innerText = title; resolve(); }, function(err) { console.log(err); document.querySelector("h2").innerText = "Failed to load Dynamic Web TWAIN"; reject(err); } ); }) }
源代碼
可以在以下倉庫中找到所有代碼和在線演示:
github.com/tony-xlh/Color-Conversion-JavaScript
到此這篇關于使用JavaScript實現(xiàn)二值化圖像的文章就介紹到這了,更多相關JavaScript二值化圖像內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JavaScript面試中常考的字符串操作方法大全(包含ES6)
對于JavaScript字符串操作方法,你真的全部掌握了嗎?來看看這篇面試中??嫉淖址僮鞔笕?,包含最新的ES6字符串操作方法,值得收藏哦2020-05-05javascript Promise簡單學習使用方法小結(jié)
下面小編就為大家?guī)硪黄猨avascript Promise簡單學習使用方法小結(jié)。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-05-05js類定義函數(shù)時用prototype與不用的區(qū)別示例介紹
沒有使用prototype的方法相當于類的靜態(tài)方法,相反,使用prototype的方法相當于類的實例方法,不許new后才能使用2014-06-06Js+Dhtml:WEB程序員簡易開發(fā)工具包(預先體驗版)
Js+Dhtml:WEB程序員簡易開發(fā)工具包(預先體驗版)...2006-11-11