May 19, 2021

Processingで『最新の』OpenCV使いたい

OpenCV for Processingじゃない生OpenCVを叩こうとした話

モチベーション

Jeff氏のNon-Circular Packingの絵がかっこよかった.

https://ippsketch.com/posts/non-circular-packing/

アルゴリズム改変して遊びたかったけど配列走査する画像処理はさすがにやりたくなかったのでOpenCVをProcessingに導入しようとした.

OpenCV for Processingのリポジトリは↓なのだが, 2017年から更新が止まっていて,対応してるOpenCVのバージョンも2.4.5止まりなので新しいOpenCV使うには生のOpenCV Java APIを叩くしかなかった.

https://github.com/atduskgreg/opencv-processing

Processingで外部jarファイルを利用する方法

最新のOpenCVは以下のページからダウンロード可能:

https://opencv.org/releases/

Windows環境でOpenCV Java APIを使うのに必要なのは以下の2ファイル (4xxのところはバージョンが入る):

build/java/opencv-4xx.jar
build/java/x64/opencv_java4xx.dll

OpenCVのjarとdllに適当にパス通してから以下のimport文を書いて実行

import org.opencv.core.*;

すると以下のようなエラーが出る.

Libraries must be installed in a folder named 'libraries' inside the sketchbook folder (see the Preferences window).

エラーの指示通りにlibrariesにjarファイルとdllを置いてもエラーになる. どうやらエラー文が間違ってるらしく,正しくはcodeに置かないといけない.なんでやねん.

http://nlaboratory.blogspot.com/2009/12/processing.html

OpenCVで画像を読み込んでPImageに変換して出力するプログラム

画像は以下のスケッチから拝借した:

https://openprocessing.org/sketch/1190911

import org.opencv.core.*;

Mat img;
PImage pimg;
PGraphics pg;

static{ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }

void setup() {
  size(800, 800);

  // 画像をBGRAで読み込む
  // (Imgcodecs.IMREAD_UNCHANGEDをつけないとBGRになるので注意)
  img = Imgcodecs.imread(dataPath("banana_peeled.png"), Imgcodecs.IMREAD_UNCHANGED);
  pimg = matToPImage(img);
}

void draw() {
  background(100);
  translate(width / 2.0, height / 2.0);
  imageMode(CENTER);

  rotate(frameCount / 60.0 * TWO_PI);
  image(pimg, 0, 0);
}

PImage matToPImage(Mat mat) {
  PImage img = new PImage(mat.cols(), mat.rows(), ARGB);

  byte[] buff = new byte[mat.cols() * mat.rows() * 4];
  mat.get(0, 0, buff);

  img.loadPixels();
  for (int k = 0; k < buff.length; k+=4) {
    // ピクセルの順序をBGRAからARGBに並べ替える
    int b = (buff[k+2] & 0xff) << 16;
    int g = (buff[k+1] & 0xff) << 8;
    int r = (buff[k] & 0xff) << 0;
    int a = (buff[k+3] & 0xff) << 24;

    // Matは要素数(width * height * 4)の配列に8bit整数としてBGRAの色情報を入れてる
    // PImageは要素数(width * height)の配列に32bit整数としてARGBの色の情報を入れてる
    img.pixels[k / 4] = a | r | g | b;
  }
  img.updatePixels();

  return img;
}

void keyPressed() {
  if (key == ESC) {
    exit();
  }
}

img

最後に

Mat -> PImage変換が重いのでこのままだとあんまり役に立たないかも.もっと高速にできそうな方法は見つけてるのでそのうち試すかもしれない.

© eqs 2021