日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

js操作像素实现图片编辑

發布時間:2024/1/8 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js操作像素实现图片编辑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基礎概念

眾所周知,圖片是由一個個像素點組成.每一個像素點包含四個值,決定了渲染出來的狀態.這四個值為rgba(red, green, blue, alpha).

前三個值是紅綠藍,值的大小范圍從0到255,或者從0%到100%之間.

第四個值alpha,規定了色彩的透明度,它的范圍為0到1之間.其中0代表完全透明,1代表完全可見.

紅綠藍是色彩中的三元色,通過設置這三種顏色所占的比重,可以變幻出其他所有顏色.

比如某個標簽的文字想設置為紅色,就可以通過css設置rgba值(代碼如下).

span {color: rgba(255, 0, 0, 1); }

既然每個像素點可以通過rgba的值來表達,那么一張圖片所包含的所有像素點都可以轉換成數據.如果修改某部分像素點的rgba值,那該圖片渲染出來的效果就會發生變化,這樣便實現了圖片的編輯.

那怎么把圖片轉化成由像素點組成的數據呢?

實現

圖片轉換數據

一段簡單的html結構如下,頁面上放置一個原始圖片和一個canvas畫布,寬高都為300;

<body><p class="image"><img src="./img/rect.png" width="300" height="300" /></p><canvas id="myCanvas" width="300" height="300"></canvas> <body>

首先編寫一個getImageData函數將原始圖片轉化成數據(代碼如下).

圖片轉換成像素數據按以下兩步操作.

  • 調用ctx.drawImage將圖片繪制到畫布上
  • 調用ctx.getImageData獲取像素數據
const dom = document.getElementById("myCanvas"); // canvas畫布getImageData(dom,"./img/rect.png").then((data)=>{console.log(data); // 打印輸出像素數據})function getImageData(dom,url){const ctx = dom.getContext("2d"); // 設置在畫布上繪圖的環境const image = new Image();image.src= url;//獲取畫布寬高const w = dom.width;const h = dom.height;return new Promise((resolve)=>{image.onload = function(){ctx.drawImage(image, 0, 0 ,w,h); // 將圖片繪制到畫布上const imgData = ctx.getImageData(0,0,w,h); // 獲取畫布上的圖像像素resolve(imgData.data) // 獲取到的數據為一維數組,包含圖像的RGBA四個通道數據ctx.clearRect(0,0,w,h);} }) }

最終的打印出來的數據結果(data)如下:

data = [255, 255, 255, 255, 255, 61, 61, 255, 255, 0, 0, 255, 255,...]

data是一維數組,數組的前四個值[255, 255, 255, 255]為圖片第一個像素點的rgba值(ctx.getImageData返回的透明度大小范圍是從0 - 255的),[255, 61, 61, 255]
是圖片第二個像素點的rgba值,后面依次類推.如此便成功的將圖片轉化成了數據.

數據格式化

雖然圖片成功轉化成了數據,但這樣的數據結構很難操作,我們期待能夠將數據結構的表現形式與圖片展示效果保持一致.

假如存在四個都是黑色的像素點(如下圖),總寬高都為2,值為[0, 0, 0, 255,0, 0, 0, 255,0, 0, 0, 255,0, 0, 0, 255].

通過某個函數轉換,數據就變成了下列格式.

[[[0, 0, 0, 255],[[0, 0, 0, 255]]], // 第一行[[0, 0, 0, 255],[[0, 0, 0, 255]]] // 第二行 ]

上列數據格式和圖片的展示結構保持了一致,可以很清晰的看出當前圖形有多少行,每一行又有多少個像素點,
以及每一個像素點的rgba值.

綜合上面描述,可以編寫函數normalize(代碼如下)實現數據格式的轉換.

const dom = document.getElementById("myCanvas"); // canvas畫布getImageData(dom,"./img/rect.png").then((data)=>{console.log(normalize(data,dom.width,dom.height)); // 打印格式化后的像素數據 })function normalize(data,width,height){const list = [];const result = [];const len = Math.ceil(data.length/4);// 將每一個像素點的rgba四個值組合在一起for(i = 0;i<len;i++){const start = i*4;list.push([data[start],data[start+1],data[start+2],data[start+3]]);}//根據圖形的寬和高將數據進行分類for(hh = 0;hh < height;hh++){const tmp = [];for(ww = 0; ww < width;ww++){tmp.push(list[hh*width + ww]);}result.push(tmp);}return result; }

換膚需求

通過normalize函數的轉換,一維數組的圖片數據轉換成了矩陣形式.有了矩陣,我們就可以更加方便的實現編輯圖片的需求.

首選我們簡單實現一個圖片換膚的需求,將圖片中的黑色全部變成黃色(最終效果圖如下).

上方的原始圖片包含紅藍綠黑四種顏色,下方是換膚后生成的新圖片.

實現代碼如下,peeling函數負責變換圖片的顏色.

觀察代碼,由于黑色的rgb值是(0,0,0).那么只需要判斷出像素點是黑色,就重置其rgb值為(255,255,0)便能將圖片中所有的黑色換成黃色.

const dom = document.getElementById("myCanvas"); // canvas畫布getImageData(dom,"./img/rect.png").then((data)=>{data = peeling(data,dom.width,dom.height); // 換膚drawImage(dom,data); // 繪制圖像 })function peeling(data,w,h){data = normalize(data,w,h); // 轉化成多維數組// 將黑色變成黃色 (0,0,0) -> (255,255,0) for(let i = 0;i<data.length;i++){for(let j = 0;j<data[i].length;j++){//排除透明度的比較if(data[i][j].slice(0,3).join("") == "000"){data[i][j] = [255,255,0,data[i][j][3]];}}}return restoreData(data); // 轉化成一維數組 }

矩陣的數據操作完了,還需要調用restoreData函數將多維數組再轉回一維數組傳給瀏覽器渲染.

function restoreData(data){const result = [];for(let i = 0;i<data.length;i++){for(let j = 0;j<data[i].length;j++){result.push(data[i][j][0],data[i][j][1],data[i][j][2],data[i][j][3]);}}return result;}

渲染圖片

數據處理完畢后,還需將處理完的數據data傳遞給drawImage函數渲染成新圖片(代碼如下).

渲染圖像主要調用以下兩個api.

  • ctx.createImageData.創建新的空白ImageData對象,通過.data.set重新賦值.
  • ctx.putImageData.將像素數據繪制到畫布上.
const dom = document.getElementById("myCanvas"); // canvas畫布getImageData(dom,"./img/rect.png").then((data)=>{data = peeling(data,dom.width,dom.height); // 換膚drawImage(dom,data); // 繪制圖像 })function drawImage(dom,data){const ctx = dom.getContext("2d");const matrix_obj = ctx.createImageData(dom.width,dom.height);matrix_obj.data.set(data);ctx.putImageData(matrix_obj,0,0); }

至此新圖片便成功渲染了出來,效果圖如下.

回顧上述操作,編輯圖像主要分解成以下三步.

  • 將原始圖片轉化成矩陣數據(多維數組)
  • 依據需求操作矩陣
  • 將修改后的矩陣數據渲染成新圖片

上述第二步操作是圖像編輯的核心,很多復雜的變換效果可以通過編寫矩陣算法實現.

為了加深理解,利用上述知識點實現一個圖片旋轉的需求.

圖片旋轉

假定存在最簡單的情況如下圖所示,其中左圖存在四個像素點.第一行有兩個像素點1和2(這里用序號代替rgba值).

第二行也有兩個像素點3和4.數據源轉換成矩陣data后的值為 [[[1],[2]],[[3],[4]]].

如何將左圖按順時針旋轉90度變成右圖?

通過觀察圖中位置關系,只需要將data中的數據做位置變換,讓data = [[[1],[2]],[[3],[4]]]變成data = [[[3],[1]],[[4],[2]]],就可以實現圖片變換.

四個像素點可以直接用索引交換數組的值,但一張圖片動輒幾十萬個像素,那該如何進行操作?

這種情況下通常需要編寫一個基礎算法來實現圖片的旋轉.

首先從下圖中尋找規律,圖中有左 - 中 - 右三種圖片狀態,為了從左圖的1-2-3-4變成右圖的3-1-4-2,可以通過以下兩步實現.

  • 尋找矩陣的高度的中心軸線,上下兩側按照軸線進行數據交換.比如左圖1 - 2和3 - 4之間可以畫一條軸線,上下兩側圍繞軸線交換數據,第一行變成了3 - 4,第二行變成了1 - 2.通過第一步操作變成了中圖的樣子.

  • 中圖的對角線3 - 2和右圖一致,剩下的將對角線兩側的數據對稱交換就可以變成右圖.比如將中圖的1和4進行值交換.操作完后便實現了圖片的旋轉.值得注意的是4的數組索引是[0][1],而1的索引是[1][0],剛好索引順序顛倒.

通過以上描述規律便可編寫下面函數實現圖片的旋轉.

const dom = document.getElementById("myCanvas"); // canvas畫布// getImageData 獲取像素數據 getImageData(dom,"./img/rect.png").then((data)=>{data = rotate90(data,dom.width,dom.height); // 順時針旋轉90度drawImage(dom,data); // 繪制圖像 })function rotate90(data,w,h){data = normalize(data,w,h); // 轉化成矩陣// 圍繞中間行上下顛倒const mid = h/2; // 找出中間行for(hh = 0;hh < mid;hh++){const symmetric_hh = h - hh -1; // 對稱行的索引for(ww = 0;ww<w;ww++){let tmp = data[hh][ww];data[hh][ww] = data[symmetric_hh][ww];data[symmetric_hh][ww] = tmp;}}// 根據對角線進行值交換for(hh = 0;hh < h;hh++){for(ww = hh+1;ww<w;ww++){let tmp = data[hh][ww];data[hh][ww] = data[ww][hh];data[ww][hh] = tmp;}}return restoreData(data); // 轉化成一維數組 }

由于我們定義的canvas寬高都為300,上面的旋轉算法只適用于正方形(長方形的圖片要另外編寫).

最終頁面展示的效果圖如下(上面是原始圖片,下面是畫布生成的新圖片).

總結

以上是生活随笔為你收集整理的js操作像素实现图片编辑的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。