#javaで手の認識
javaでopencv叩いて手の平を認識してみた.
ちなみに,描画系がめんどくさいので,processingをライブラリとして使用している.
ソースコードはgithubにあげて置いた.
HsvTest.java
package imageTranslater;
import java.util.ArrayList;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfInt4;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.highgui.VideoCapture;
import org.opencv.imgproc.Imgproc;
import processing.core.PApplet;
public class HsvTest extends PApplet {
public static void main(String args[]) {
PApplet.main(new String[] { "imageTranslater.HsvTest" });
}
VideoCapture cap;
ImageTranslater it;//画像変換ライブラリ
public void setup() {
size(1280, 720);
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);//openCVのロード
frameRate(60);
it = new ImageTranslater(this);
cap = new VideoCapture();
cap.open(0);
}
public void draw() {
Mat bgr = new Mat();//元画像
cap.read(bgr);
Mat hsv = new Mat();//HSV変換画像
Imgproc.cvtColor(bgr, hsv, Imgproc.COLOR_BGR2HSV);
Imgproc.medianBlur(hsv, hsv, 3);
Mat skin = skinDetect(hsv);
Mat skinDist = distTransform(skin);
Mat bin = new Mat();
Imgproc.threshold(skinDist, bin, 0, 255, Imgproc.THRESH_BINARY
| Imgproc.THRESH_OTSU);
//肌色の輪郭抽出
ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat(skinDist.cols(), skinDist.rows(),
CvType.CV_32SC1);
Imgproc.findContours(skinDist, contours, hierarchy, Imgproc.RETR_LIST,
Imgproc.CHAIN_APPROX_NONE);
//一番大きい肌色の領域を手として認識する
int index = -1;
double area = 0;
for (int i = 0, n = contours.size(); i < n; i++) {
double tmp = Imgproc.contourArea(contours.get(i));
if (area < tmp) {
area = tmp;
index = i;
}
}
if (index != -1) {
//手の概形を抽出
MatOfInt hull = new MatOfInt();
MatOfPoint contour = contours.get(index);
Imgproc.convexHull(contour, hull);
Point data[] = contour.toArray();
int v = -1;
for (int i : hull.toArray()) {
if (v == -1) {
v = i;
} else {
Imgproc.line(bgr, data[i], data[v], new Scalar(0, 255, 0));
v = i;
}
}
convexityDefects(bgr, contour,hull);
Imgproc.drawContours(bgr, contours, index, new Scalar(255, 0, 0));
}
image(it.Mat2PI(bgr), 0, 0, width, height);
fill(0);
text(frameRate + "fps", 0, 10);
}
//手の概形を得る
void convexityDefects(Mat img,MatOfPoint contour,MatOfInt hull) {
Point data[] = contour.toArray();
MatOfInt4 convexityDefects = new MatOfInt4();
Imgproc.convexityDefects(contour, hull, convexityDefects);
int cd[] = convexityDefects.toArray();
if(cd==null)return;
for (int i = 0; i < cd.length; i += 4) {
Imgproc.line(img, data[cd[i + 1]], data[cd[i + 2]], new Scalar(255, 255, 255));
Imgproc.line(img, data[cd[i]], data[cd[i + 2]], new Scalar(255, 255, 255));
}
}
//特定閾値内の色を肌色とする
Mat skinDetect(Mat mat) {
Mat mat1 = new Mat();
Core.inRange(mat, new Scalar(0, 58, 89), new Scalar(25, 173, 229), mat1);
return mat1;
}
//距離変換
Mat distTransform(Mat mat) {
Mat mat1 = new Mat(mat.cols(), mat.rows(), CvType.CV_8UC1);
Mat mat2 = new Mat();
Imgproc.distanceTransform(mat, mat2, Imgproc.CV_DIST_L2, 3);
Core.convertScaleAbs(mat2, mat1);
Core.normalize(mat1, mat1, 0.0, 255.0, Core.NORM_MINMAX);
return mat1;
}
}
ちなみにopencvの画像オブジェクトとjavaの画像オブジェクト,processingの画像オブジェクトを相互に変換する必要があるので,それらを違うjavaファイルにまとめて書いておく.
ImageTranslater.java
package imageTranslater;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import processing.core.PApplet;
import processing.core.PImage;
/**
* javaとprocessingとopencvのための画像オブジェクト変換用クラス
* ビルド・パスにopencv-xxx.jar(openCV)とcore.jar(Processing)を追加しておいてください.
*
* @version 1.1
*/
public class ImageTranslater {
/**
* インスタンス化した時にPAppletを保存しておく.
*/
PApplet pa;
/**
* インスタンス化すると短い名前で使用可能です.
*/
public ImageTranslater(PApplet pa) {
this.pa = pa;
}
/**
* PImage型をBufferedImage型(TYPE_INT_RGBまたはTYPE_INT_ARGB)に変換します
*
* @param img1 変換したいPImage型
* @return 変換したBufferedImage型
*/
public static BufferedImage PImageToBufferedImageRGB(PImage img1) {
int icm = (img1.format == PImage.ARGB) ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB;
boolean ifHSB = (img1.format == PImage.HSB);
BufferedImage img2 = new BufferedImage(img1.width, img1.height, icm);
img1.loadPixels();
for (int i = 0; i < img1.width; i++) {
for (int j = 0; j < img1.height; j++) {
int color = img1.pixels[i + j * img1.width];
if (ifHSB) {
color = Color.HSBtoRGB((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF);
}
img2.setRGB(i, j, color);
}
}
return img2;
}
/**
* BufferedImage型をPImage型(RGB)に変換します
* @param pa PApplet
* @param img1 変換したいBufferedImage型
* @return 変換したPImage型
*/
public static PImage BufferedImageToPImageRGB(PApplet pa, BufferedImage img1) {
PImage img2 = pa.createImage(img1.getWidth(), img1.getHeight(), pa.RGB);
for (int i = 0; i < img2.width; i++) {
for (int j = 0; j < img2.height; j++) {
img2.pixels[i + j * img2.width] = img1.getRGB(i, j);
}
}
img2.updatePixels();
return img2;
}
/**
* Mat型をBufferedImage型(TYPE_BYTE_GRAYまたはTYPE_3BYTE_BGR)に変換します
* @param mat 変換したいMat型
* @return 変換したBufferedImage
*/
public static BufferedImage MatToBufferedImageBGR(Mat mat) {
int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
byte data[] = new byte[dataSize];
mat.get(0, 0, data);
int type;
if (mat.channels() == 1) {
type = BufferedImage.TYPE_BYTE_GRAY;
} else {
type = BufferedImage.TYPE_3BYTE_BGR;
for (int i = 0; i < dataSize; i += 3) {
byte blue = data[i + 0];
data[i + 0] = data[i + 2];
data[i + 2] = blue;
}
}
BufferedImage img = new BufferedImage(mat.cols(), mat.rows(), type);
img.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data);
return img;
}
/**
* Mat型をPImage型(RGB)に変換します
* @param pa PApplet
* @param mat 変換したいMat型
* @return 変換したPImage型
*/
public static PImage MatToPImageRGB(PApplet pa, Mat mat) {
PImage img = pa.createImage(mat.cols(), mat.rows(), pa.RGB);
if (mat.channels() == 1) {
for(int i=0;i<mat.cols();i++){
for(int j=0;j<mat.rows();j++){
int c=(int)mat.get(j,i)[0];
img.pixels[i+j*img.width]=(c<<16)|(c<<8)|c;
}
}
}else{
for(int i=0;i<mat.cols();i++){
for(int j=0;j<mat.rows();j++){
img.pixels[i+j*img.width]=(int)mat.get(j,i)[0]|((int)mat.get(j,i)[1]<<8)|((int)mat.get(j,i)[2]<<16);
}
}
}
img.updatePixels();
return img;
}
/**
* PImage型をMat型(CV_8UC3)に変換します
* @param img 変換したいPImage型
* @return 変換したMat型
*/
public static Mat PImageToMat(PImage img) {
Mat mat = new Mat(img.height, img.width, CvType.CV_8UC3);
int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
byte data[] = new byte[dataSize];
img.loadPixels();
int index = 0;
for (int i = 0; i < dataSize; i += 3) {
int color = img.pixels[index++];
data[i + 2] = false ? 0 : (byte) ((color >> 16) & 0xFF);
data[i + 1] = false ? 0 : (byte) ((color >> 8) & 0xFF);
data[i] = (byte) (color & 0xFF);
}
mat.put(0, 0, data);
return mat;
}
/**
* PImage型をMat型(CV_8UC3)に変換します
* @param img 変換したいPImage型
* @return 変換したMat型
*/
public static Mat PImageToMatMask(PImage img) {
Mat mat = new Mat(img.height, img.width, CvType.CV_8U);
int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
byte data[] = new byte[dataSize];
img.loadPixels();
int index = 0;
for (int i = 0; i < dataSize; i ++) {
if((img.pixels[index++]&0x00FFFFFF)!=0);{
data[i] = 1;
}
}
mat.put(0, 0, data);
return mat;
}
/**
* BufferedImage型(TYPE_3BYTE_RGB)をMat型(CV_8UC3)に変換します
* @param image 変換したいBufferedImage型
* @return 変換したMat型
*/
public static Mat BufferedImageToMat(BufferedImage image) {
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.out.println("bufferedimage:" + data.length);
Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, data);
return mat;
}
public BufferedImage PI2BI(PImage img1) {
return PImageToBufferedImageRGB(img1);
}
public PImage BI2PI(BufferedImage img1) {
return BufferedImageToPImageRGB(this.pa, img1);
}
public BufferedImage Mat2BI(Mat mat) {
return MatToBufferedImageBGR(mat);
}
public PImage Mat2PI(Mat mat) {
return MatToPImageRGB(this.pa, mat);
}
public Mat PI2Mat(PImage img) {
Mat mat = new Mat(img.height, img.width, CvType.CV_8UC3);
int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
byte data[] = new byte[dataSize];
img.loadPixels();
int index = 0;
for (int i = 0; i < dataSize; i += 3) {
int color = img.pixels[index++];
data[i + 2] = false ? 0 : (byte) ((color >> 16) & 0xFF);
data[i + 1] = false ? 0 : (byte) ((color >> 8) & 0xFF);
data[i] = (byte) (color & 0xFF);
}
mat.put(0, 0, data);
return mat;
}
public Mat BI2Mat(BufferedImage image) {
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.out.println("bufferedimage:" + data.length);
Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, data);
return mat;
}
}