85
86

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

画像処理100本ノックにJavaScriptで挑戦してみた 【画像処理100本ノックJS】

Last updated at Posted at 2019-05-26

test.gif

概要

画像処理100本ノックをJavaScriptで挑戦してみました。
「ブラウザ上で完結させたい」 & 「デモを共有できたら面白い」という動機ではじめました。
まだ100問完了していませんが、ここまで解いてみた所感を書きます。

とりあえず、、、「100問は辛いです。」

画像処理100本ノックについて

画像処理が初めての人のための問題集をつくったりました。(完成!!)

研究室の後輩用に作ったものです。

自然言語処理100本ノックがあるのに、画像処理のがなかったので作ってみました。

画像処理の基本のアルゴリズム理解につながると思います。

https://qiita.com/yoyoyo_/items/2ef53f47f87dcf5d1e14

GitHub Qiita

この「画像処理100本ノック」にはPythonとC++のコードが解答例として用意されています。

デモの例 ※ → デモ はこちらから(Gasyori100KnockJS)

いくつかのデモの例を紹介します。


201904071958_njvf00.png
大津の2値化


20190518004109.png
プーリング (MAX値)


 2019-05-26 19.56.11.png
メディアンフィルタ



アフィン変換 (スキュー)


 2019-05-26 19.51.21.png
テンプレートマッチング


 2019-05-26 19.57.46.png
バイリニア補間


 2019-05-26 20.03.21.png
JPEG圧縮 (Step.2)DCT+量子化


 2019-05-26 19.53.02.png
Harrisのコーナー検出

実装の紹介

GitHub にソースを置いてます。

canvasを使った画像の表示と操作

画像の読み込み

画像の表示ピクセル値の操作にはcanvasのAPIを利用しています。

任意の画像を読み込む際には次のように実装しています。

<canvas id="canvas"></canvas>
// canvas関連のオブジェクト
const canvas = document.getElementById("canvas")
const ctx = canvas.getContext("2d")

// 任意の画像読み込み
let image = new Image()
image.src = "path/to/image.png"

// 読み込み完了時のイベント
image.onload = () => {
    canvas.width = image.width
    canvas.height = image.height
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
    // canvas描画後、画像の処理を実行
}

ピクセル操作

html5-canvas-imageData-example.png

CREATE A PAINT BUCKET TOOL IN HTML5 AND JAVASCRIPT
http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/

より

getImageDataメソッドでImageDataオブジェクトを取得しています。
このオブジェクトにはr, g, b, a の順に画像情報が格納されています。
putImageDataを使って編集したImageDataオブジェクトをcanvasに描画しています。

これを用いることにより大概の画像の処理を行うことができます。

(canvasを用いず、Imageオブジェクトの画像情報に対して直接参照する方法があればいいんですけど... )


let src = ctx.getImageData(0, 0, canvas.width, canvas.height)
let dst = ctx.createImageData(canvas.width, canvas.height)
for (let i = 0; i < src.data.length; i += 4) {
    dst.data[i] = src.data[i]          // r
    dst.data[i + 1] = src.data[i + 1]  // g
    dst.data[i + 2] = src.data[i + 2]  // b
    dst.data[i + 3] = src.data[i + 3]  // a (透過度)
}

例えば、グレースケール画像であれば次のような処理になります。


const grayscale = (r, g, b) => 0.2126 * r + 0.7152 * g + 0.0722 * b
// 略
for (let i = 0; i < src.data.length; i += 4) {
    let gray = grayscale(src.data[i], src.data[i + 1], src.data[i + 2])
    dst.data[i] = gray[0]
    dst.data[i + 1] = gray[1]
    dst.data[i + 2] = gray[2]
    dst.data[i + 3] = src.data[i + 3]
}
ctx.putImageData(dst, 0, 0)

こんな風に表示されます。

201904071946_398cds.png

参考 : 画像をグレースケールに変換する JavaScript + canvas 【画像処理】

ヒストグラムの表示

 2019-05-26 1.46.55.png

このデモでは、ヒストグラムの表示にChart.jsを使っています。

実装については次のように行なっています。

ヒストグラム描画


import Chart from "chart.js"

export default class Histogram {
  /**
   * ヒストグラムを描画する
   * @param {Object} canvas 
   * @param {Object} data 
   */
  static renderHistogram(canvas, data) {
    let labels = new Array(data.length).fill('')
    new Chart(canvas, {
      type: 'bar',
      data:{
        labels,
        datasets: [
          {
            label: '画素値',
            data,
            backgroundColor: "rgba(80,80,80,0.5)"
          }
        ],
      },
      options: {
        title: {
          display: true,
          text: 'Histogram'
        },
        scales: {
          yAxes: [{
            ticks: {
              suggestedMin: 0,
            }
          }]
        },
        animation: {
          duration: 0
        }
      }
    })
  }
}
import Histogram from 'path/to/Histogram'

const grayscale = (r, g, b) => 0.2126 * r + 0.7152 * g + 0.0722 * b
// 略
let pixelValues = new Array(255).fill(0)
for (let i = 0; i < src.data.length; i += 4) {
    let gray = grayscale(src.data[i], src.data[i + 1], src.data[i + 2])
    gary = Math.floor(gray)
    pixelValues[gray]++
}
Histogram.renderHistogram(canvas, pixelValues)

参考 : 画像のヒストグラムを表示する Char.js JavaScript canvas

フレームワークにVueを使っています。
またSPAにも挑戦しました。

コンポーネントの制御が難しく、処理がバグっている箇所があると思います()

まとめ : JSで挑戦するメリット・デメリット

ブラウザ上で動かせるのがJSを使う最大のメリットだと思います。
加えて、チャート系のライブラリが豊富なので、matplotlibに比べ、グラフィカルな表現がしやすいのも良い点だと感じました。

一方で、行列演算に関してはJSではnumjsやmath.jsといったものはありますが、
Numpyほど簡潔に行列の処理を書くことはできません。

(※今回のデモではアフィン変換などの行列演算を多用する箇所で math.js を使いました。)

また、フーリエ変換のデモでは、実装に複素数を利用しますが、
Pythonは「j」が利用できるのに対し、JSの場合は実部と虚部に分けるような処理に実装する必要がありました。

改めてPython、Numpyの偉大さには感謝したいと思います。


画像処理100本ノックJS
https://s-yoshiki.github.io/Gasyori100knockJS/#/


画像処理100本ノックJS - GitHub
https://github.com/s-yoshiki/Gasyori100knockJS


JavaScriptで画像処理100本ノックに挑戦してみた
https://tech-blog.s-yoshiki.com/entry/110

85
86
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
85
86

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?