画像に映っているボールの色の平均値を取得したくなりました。ボールの場所が決まっているなら、JavaScriptとcanvasで色の平均を出すことができます。
色の平均値が欲しくなる場面
Webアプリでボールの色を覚えさえるプログラムを作っています。
当初は、ボールの中心の色をピックアップしていました。しかし、意図しない色が取得される場合があります。
ボールのツヤ、描かれているロゴ、縫い目などの色を誤認するようです。特にツヤは照明環境に左右され、どうにもできない場合もありました。
ピンポイントでの色取得は難しいため、ボール全体の色の平均値を取得することにします。
JavaScriptとcanvasで色の平均値取得
画像からボールを切り抜いて、1pxずつ色を調べます。
R,G,Bの値をそれぞれ合算していき、最後にpx数で割れば色の平均が出せます。
実際にテストしたコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>canvasで色の平均を取得</title> <style> /*表示確認用CSS*/ main { display: flex; } main > div { position: relative; width: 320px; margin-right: 20px; background-color: #EEE; } h2 { position: absolute; width: 320px; margin-top: 330px; text-align: center; } img { vertical-align: bottom; } #canvas, #color { position: absolute; top: 93px; left: 93px; } #color { width: 134px; height: 134px; line-height: 134px; border-radius: 50%; color: #FFF; text-align: center; } </style> </head> <body> <h1>canvasで色の平均を取得</h1> <main> <div> <h2>元画像</h2> <img id="img" src="img/ball.jpg" alt="" width="320" height="320"> </div> <div> <h2>canvas転写</h2> <canvas id="canvas" width="134" height="134"></canvas> </div> <div> <h2>色の平均</h2> <div id="color"></div> </div> </main> <script> window.onload = () =>{ const img = document.getElementById("img"); const canvas = document.getElementById("canvas"); const ctx = canvas.getContext('2d'); const color = document.getElementById("color"); /* canvasのサイズと位置取得 */ const canvasW = canvas.width; const canvasH = canvas.height; const canvasX = canvas.offsetTop; const canvasY = canvas.offsetLeft; /* 円で切り抜き */ ctx.beginPath(); ctx.arc(canvasW / 2, canvasH / 2, Math.round(canvasW / 2), 0, Math.PI*2, false); ctx.clip(); /* 元画像の指定部分をcanvasに転写 */ ctx.drawImage(img, //元画像 canvasX, canvasY, canvasW, canvasH, //元画像の切り抜き範囲 0, 0, canvasW, canvasH //canvas転写範囲(全体) ); /* 色の平均取得 */ const getAverageColor = () => { const data = ctx.getImageData(0, 0, canvasW, canvasH).data; let averageColor = {r:0, g:0, b:0}; let n = 0; /* canvasの左上から[r,g,b,a]の配列を調査 */ for(let i = 0, len = data.length; i < len; i += 4) { if(data[i + 3] === 255){//不透明色のみ取得 averageColor.r += data[i]; averageColor.g += data[i + 1]; averageColor.b += data[i + 2]; n++; } } /* RGBの平均計算 */ averageColor.r = Math.round(averageColor.r / n); averageColor.g = Math.round(averageColor.g / n); averageColor.b = Math.round(averageColor.b / n); return averageColor; }; /* 色の平均書き出し */ let average = getAverageColor(); color.style.backgroundColor = `rgb(${average.r},${average.g},${average.b})`; color.innerText = `rgb(${average.r},${average.g},${average.b})`; }; </script> </body> </html> |
利用したimg/ball.jpgも合わせて掲載します。テストコードを実行する場合にご利用ください。
ライブラリなしで軽量に計算できる
JavaScriptで画像解析をしようとすると、OpenCV.jsのような重たいライブラリを読み込まないといけないかと思っていました。
しかし、JavaScriptとcanvasだけで、すべてのピクセルを色の配列に変換して取得できます。
これにより、簡単な画像処理ならライブラリなしで軽量に処理できることが分かりました。