JavaでOpenCVを使う必要があったので、JavaのラッパーライブラリであるJavaCVを利用してみました。
その際に、URL上の画像を読み込んで加工するのに少々ハマったので、その対応メモです。
やりたいこと
URL上の画像パスから画像をMat型で読み込んで、OpenCVで加工したいのです。
ハマったポイント
URLから画像を読み込む機能はOpenCVにはなさそうだったので、JavaのImageIO
クラスを使ってURLから画像を読み込んで、それをOpenCVのMatに変換してやろうと思いました。
当初はImageIO
クラスで読み込んだ画像データはint配列として取り出せるので、それをバイト配列に変換してからOpenCVのMat
で読み込めばいけるだろうと思ったんですが、こんな感じでなんだか色味がおかしくなってしまいました。
ちなみに元画像は↓のOpenCVのサンプル画像を利用させてもらってます。
https://raw.githubusercontent.com/opencv/opencv/4.x/samples/data/fruits.jpg
どうせBGRとして読み込むべきところをRGBとして読み込んじゃってるとかそんなんだろうと思ってcvtColorで変換かけてみましたが、赤っぽくなるだけで元画像と同じ色味にはなりませんでした。
原因
原因は、ImageIO
クラスから取り出したint配列はARGBの順で並んでいるため、その順のままMat
に変換してしまうとおかしな色になってしまうことにあります。
なので、この問題を解決するためには、ARGBの並び順をMat
で読みこめるBGRAの順に並び変えないといけないです。
そういった変換にはcvtColor
を使いたいところですが、残念ながらOpenCVにはARGBをBGRAなどに変換する処理は実装されていません。
そのため、ARGBからBGRAに変換する部分は自前で実装する必要があります。
実装
上記のARGBからBGRAに変換する部分を自前実装して、URLから画像を読み込んでMat型に格納するサンプルコードがこちらになります。
public class Sample {
public static void main(String[] args) throws IOException {
// URLから画像を取得
URL url = new URL("https://raw.githubusercontent.com/opencv/opencv/4.x/samples/data/fruits.jpg");
BufferedImage bufferedImage = ImageIO.read(url);
// 各画素のARGB色情報を抽出
// intは4byteなので、各byteがそれぞれARGBの各要素に対応している
int[] argbArray = bufferedImage.getRGB(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), null, 0, bufferedImage.getWidth());
// int配列をbyte配列に変換する
// その際、Mat型で読み込めるようにARGBの並びをBGRAに変換する
byte[] bgraArray = new byte[argbArray.length * 4];
for (int i = 0; i < argbArray.length; i++) {
bgraArray[i * 4 + 0] = (byte) ((argbArray[i] >> 0) & 0xFF); // B
bgraArray[i * 4 + 1] = (byte) ((argbArray[i] >> 8) & 0xFF); // G
bgraArray[i * 4 + 2] = (byte) ((argbArray[i] >> 16) & 0xFF); // R
bgraArray[i * 4 + 3] = (byte) ((argbArray[i] >> 24) & 0xFF); // A
}
// Mat型として読み込み
Mat image = new Mat(bufferedImage.getHeight(), bufferedImage.getWidth(), CV_8UC4, new BytePointer(bgraArray));
imwrite("result.jpg", image);
}
}
上記コードを実行すると、無事元画像と同じものがresult.jpg
として保存されます。
参考
コメント