package org.hipi.opencv;
import org.hipi.util.ByteUtils;
import org.apache.hadoop.io.Writable;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_core.Mat;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
/**
* Implementation of Hadoop {@link Writable} interface which encapsulates {@link Mat} objects.
*/
public class OpenCVMatWritable implements Writable {
private Mat mat = null;
public OpenCVMatWritable() {
mat = new Mat();
assert mat != null;
mat.dims(2);
assert (mat.dims() == 2); // handle only 1- or 2-D arrays (sanity check)
}
public OpenCVMatWritable(Mat mat) {
setMat(mat);
}
public void setMat(Mat mat) throws IllegalArgumentException {
if (mat == null) {
throw new IllegalArgumentException("Must provide valid non-null Mat object.");
}
int dims = mat.dims();
if (!(dims == 1 || dims == 2)) {
throw new IllegalArgumentException("Currently supports only 1D or 2D arrays. "
+ "Input mat dims: " + dims);
}
Mat matCopy = new Mat(mat.rows(), mat.cols(), mat.type());
mat.copyTo(matCopy);
this.mat = matCopy;
}
public Mat getMat() {
Mat matCopy = new Mat(mat.rows(), mat.cols(), mat.type());
mat.copyTo(matCopy);
return matCopy;
}
public void write(DataOutput out) throws IOException {
assert mat != null;
int dims = mat.dims();
assert (dims == 1 || dims == 2); // handle only 1- or 2-D arrays
int type = mat.type();
out.writeInt(type);
out.writeInt(mat.rows());
out.writeInt(mat.cols());
int elms = (int)(mat.total() * mat.channels());
if (elms > 0) {
int depth = opencv_core.CV_MAT_DEPTH(type);
switch (depth) {
case opencv_core.CV_8U:
case opencv_core.CV_8S:
byte [] data = new byte[elms];
((ByteBuffer)mat.createBuffer()).get(data);
out.write(data);
break;
case opencv_core.CV_16U:
case opencv_core.CV_16S:
short [] shortData = new short[elms];
((ShortBuffer)mat.createBuffer()).get(shortData);
out.write(ByteUtils.shortArrayToByteArray(shortData));
break;
case opencv_core.CV_32S:
int [] intData = new int[elms];
((IntBuffer)mat.createBuffer()).get(intData);
out.write(ByteUtils.intArrayToByteArray(intData));
break;
case opencv_core.CV_32F:
float [] floatData = new float[elms];
((FloatBuffer)mat.createBuffer()).get(floatData);
out.write(ByteUtils.floatArrayToByteArray(floatData));
break;
case opencv_core.CV_64F:
double [] doubleData = new double[elms];
((DoubleBuffer)mat.createBuffer()).get(doubleData);
out.write(ByteUtils.doubleArrayToByteArray(doubleData));
break;
default:
throw new IOException("Unsupported matrix depth [" + depth + "].");
}
}
}
public void readFields(DataInput in) throws IOException {
int type = in.readInt();
int depth = opencv_core.CV_MAT_DEPTH(type);
int rows = in.readInt();
int cols = in.readInt();
mat = new Mat(rows, cols, type);
int elms = (int)(mat.total() * mat.channels());
switch (depth) {
case opencv_core.CV_8U:
case opencv_core.CV_8S:
byte[] data = new byte[elms];
in.readFully(data);
((ByteBuffer)mat.createBuffer()).put(data);
break;
case opencv_core.CV_16U:
case opencv_core.CV_16S:
byte[] shortDataAsBytes = new byte[elms * 2]; // 2 bytes per short
in.readFully(shortDataAsBytes);
((ShortBuffer)mat.createBuffer()).put(ByteUtils.byteArrayToShortArray(shortDataAsBytes));
break;
case opencv_core.CV_32S:
byte[] intDataAsBytes = new byte[elms * 4]; // 4 bytes per int
in.readFully(intDataAsBytes);
((IntBuffer)mat.createBuffer()).put(ByteUtils.byteArrayToIntArray(intDataAsBytes));
break;
case opencv_core.CV_32F:
byte[] floatDataAsBytes = new byte[elms * 4]; // 4 bytes per float
in.readFully(floatDataAsBytes);
((FloatBuffer)mat.createBuffer()).put(ByteUtils.byteArrayToFloatArray(floatDataAsBytes));
break;
case opencv_core.CV_64F:
byte[] doubleDataAsBytes = new byte[elms * 8]; // 8 bytes per double
in.readFully(doubleDataAsBytes);
((DoubleBuffer)mat.createBuffer()).put(ByteUtils.byteArrayToDoubleArray(doubleDataAsBytes));
break;
default:
throw new IOException("Unsupported matrix depth [" + depth + "].");
}
}
}