JS.10 OpenCVで遊ぶ

2019-06-13(Thu) | tags: app

作ったもの

画像にいくつかの処理を(一度に一つだけ)加えることのできるアプリケーションです。線画化が意外といい感じに仕上がるので、試してみてください。

Source Output

処理方法

  • px

    px

動機

色々と覚えてきたので、最後に

あたりを使って静的サイトで機械学習のWebアプリケーションを動かしJavaScript の勉強は一旦終了にしようと思いました。そこで、まずは OpenCV.js から使ってみようと思い、上記のアプリケーションを作りました。

あと、地味に毎回 Python やシェルでコード書いたり、 PowerPoint 使って画像のリサイズするのが面倒だったので意外と重宝しています笑

コード

html

<table>
  <thead>
    <tr>
      <th align="center">Source</th>
      <th align="center">Output</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <img id="src-image" src="https://iwasakishuto.github.io/images/contents-icon/Home.png" />
        <img id="hidden-image" src="https://iwasakishuto.github.io/images/contents-icon/Home.png" style="display: none;" />
      </td>
      <td>
        <canvas id="dest-canvas"></canvas>
        <canvas id="hidden-canvas" style="display: none;"></canvas>
      </td>
    </tr>
    <tr>
      <td align="center">
        <!-- 入力ファイル選択 -->
        <input type="file" id="input-file" />
      </td>
      <td align="center">
        <!-- ダウンロード -->
        <input id="download-btn" type="button" value="ダウンロード">
      </td>
  </tbody>
</table>

<!-- 処理方法を選択 -->
<form name="Processing Method" action="#">
  <h2>処理方法</h2>
  <ul>
    <li><input type="radio" name="method" id="gray-scale-btn" onClick="changeDisabled()"><label for="gray-scale-btn">グレースケール化</label></li>
    <li><input type="radio" name="method" id="linedraw-btn" onClick="changeDisabled()"><label for="linedraw-btn">線画化</label></li>
    <li><input type="radio" name="method" id="resize-btn" onClick="changeDisabled()"><label for="resize-btn">リサイズ化</label>
    <label for="gray-scale-btn">横幅</label><p style="display:inline;"><input type="number" id="resize-width" name="resizeSize" step="10" min="10" max="1000" value="400">px</p>
    <label for="gray-scale-btn">縦幅</label><p style="display:inline;"><input type="number" id="resize-height" name="resizeSize" step="10" min="10" max="1000" value="400">px</p></li>
  </ul>
  <input id="execute-btn" type="button" value="実行">
</form>

<script src="https://docs.opencv.org/3.4/opencv.js" type="text/javascript"></script>

JavaScript

const srcImg = document.getElementById('src-image');
const hiddenImg = document.getElementById('hidden-image');
const fileInput = document.getElementById('input-file');
const canvas = document.getElementById('dest-canvas');
const hiddenCanvas = document.getElementById('hidden-canvas');
const grayScaleBtn = document.getElementById('gray-scale-btn');
const lineDrawBtn = document.getElementById('linedraw-btn');
const downloadBtn = document.getElementById('download-btn');
const resizeBtn = document.getElementById('resize-btn');
const executeBtn = document.getElementById('execute-btn');
const resizeSize = document.getElementsByName('resizeSize');

// OpenCVメソッドを全て引き受ける
function EventHandler(func, ...args){
  const src = cv.imread(srcImg);
  const dst = func(src, ...args);
  cv.imshow('dest-canvas', dst);
  src.delete();
  dst.delete();

  const hiddenSrc = cv.imread(hiddenImg);
  const hiddenDst = func(hiddenSrc, ...args);
  cv.imshow('hidden-canvas', hiddenDst);
  hiddenSrc.delete();
  hiddenDst.delete();
}

function convertImageToGray(img) {
  let dst = new cv.Mat();
  cv.cvtColor(img, dst, cv.COLOR_RGBA2GRAY, 0);
  return dst;
}

function convertImageToLineDrawing(img) {
    const kernel = cv.getStructuringElement(cv.MORPH_RECT,new cv.Size(5,5));

    const imgGray = new cv.Mat();
    cv.cvtColor(img, imgGray, cv.COLOR_RGBA2GRAY);

    const imgDilated = new cv.Mat();
    cv.dilate(imgGray, imgDilated, kernel, new cv.Point(-1, 1), 1);

    const imgDiff = new cv.Mat();
    cv.absdiff(imgDilated, imgGray, imgDiff);

    const contour = new cv.Mat();
    cv.bitwise_not(imgDiff, contour);
    return contour;
}

function convertImageRisze(img, width, height) {
  let dst = new cv.Mat();
  let dsize = new cv.Size(width, height);
  cv.resize(img, dst, dsize, 0, 0, cv.INTER_AREA);
  return dst;
}

// Execute Button
executeBtn.addEventListener('click', function(){
  var radios = document.getElementsByName("method");
  if (radios[0].checked) EventHandler(convertImageToGray);
  if (radios[1].checked) EventHandler(convertImageToLineDrawing);
  if (radios[2].checked) EventHandler(
    convertImageRisze,
    parseInt(document.getElementById("resize-width").value),
    parseInt(document.getElementById("resize-height").value)
  );
});

// Input Button
fileInput.addEventListener('change', function(e){
  srcImg.src = URL.createObjectURL(e.target.files[0]);
  hiddenImg.src = URL.createObjectURL(e.target.files[0]);
  const src = cv.imread(hiddenImg);
}, false);

// Make URL for Download
function dataUriToBlob(dataUri) {
  const b64 = atob(dataUri.split(',')[1]);
  const u8 = Uint8Array.from(b64.split(''), e => e.charCodeAt());
  return new Blob([u8], {type: 'image/png'});
}

// Download Button
downloadBtn.addEventListener('click', function(e){
  let data = hiddenCanvas.toDataURL();
  let url = URL.createObjectURL(dataUriToBlob(data));
  let link = document.createElement("a");
  link.href = url;
  link.download = "processed.png";
  link.click();
})

function changeDisabled() {
  if (document.getElementsByName("method")[2].checked ) {
    resizeSize[0].disabled = false;
    resizeSize[1].disabled = false;
  } else {
    resizeSize[0].disabled = true;
    resizeSize[1].disabled = true;
  }
}

window.onload = changeDisabled();

css

ul {
  list-style: none;
}
label {
  display: inline-flex;
  margin-bottom: 0;
}
other contents
social