Android Camera2獲取的圖片為android.media.Image,opencv使用時需要轉成Mat煞檩。
Yuv420轉Mat
Android官方推薦使用yuv420圖片格式
import android.annotation.TargetApi;
import android.media.Image;
import android.os.Build;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
import java.nio.ByteBuffer;
@TargetApi(Build.VERSION_CODES.KITKAT)
public class Yuv420 {
// 復制uv通道數(shù)據(jù)
private static void copy(ByteBuffer data, ByteBuffer uv, int yl) {
int l = uv.remaining();
int quarter = yl / 4;
int half = yl / 2;
if (l == quarter) {
// 魅族M8,長度剛好是y的四分之一浙炼,直接寫入菌仁。
data.put(uv);
} else if (quarter < l && l <= half) {
// 華為榮耀,實際讀取到的長度是y的(1 / 2 - 1)
for (int i = 0; i < l; i++) {
byte b = uv.get();
if (i % 2 == 0) {
data.put(b);
}
}
} else if (l > half) {
// 未發(fā)現(xiàn)此種情況汽煮,先預留著
for (int i = 0; i < l; i++) {
byte b = uv.get();
if (i % 4 == 0) {
data.put(b);
}
}
}
}
public static Mat yuv420(Image image) {
// yuv420圖片有三個通道,按順序下來分別對應YUV
// 轉換需要把三個通道的數(shù)據(jù)按順序合并在一個數(shù)組里棚唆,
// 即全部Y逗物,隨后全部U,再隨后全部是V瑟俭,
// 再由此數(shù)組生成Yuv420的Mat翎卓,
// 之后可以利用opencv將其轉為其他格式
Image.Plane[] plans = image.getPlanes();
ByteBuffer y = plans[0].getBuffer();
ByteBuffer u = plans[1].getBuffer();
ByteBuffer v = plans[2].getBuffer();
// 此處需要把postition移到0才能讀取
y.position(0);
u.position(0);
v.position(0);
int yl = y.remaining();
// yuv420即4個Y對應1個U和一個V,即4:1:1的關系摆寄,長度剛好是Y的1.5倍
ByteBuffer data = ByteBuffer.allocateDirect(yl * 3 / 2);
// y通道直接全部插入
data.put(y);
copy(data, u, yl);
copy(data, v, yl);
// 生成Yuv420格式的Mat
int rows = image.getHeight();
int cols = image.getWidth();
return new Mat(rows * 3 / 2, cols, CvType.CV_8UC1, data);
}
public static Mat rgb(Image image) {
Mat yuvMat = yuv420(image);
int rows = image.getHeight();
int cols = image.getWidth();
// RGB三通道失暴,保存采用CV_8UC3
Mat rgbMat = new Mat(rows, cols, CvType.CV_8UC3);
// 通過cv::cvtColor將yuv420轉換為rgb格式
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2RGB_I420);
// Mat是jni本地對象,釋放對象是良好的習慣
yuvMat.release();
return rgbMat;
}
public static Mat gray(Image image) {
Mat yuvMat = yuv420(image);
int rows = image.getHeight();
int cols = image.getWidth();
// 灰色只有一個通道微饥,保存采用CV_8UC1
Mat grayMat = new Mat(rows, cols, CvType.CV_8UC1);
// 通過cv::cvtColor將yuv420轉換為灰色圖片
Imgproc.cvtColor(yuvMat, grayMat, Imgproc.COLOR_YUV2GRAY_I420);
yuvMat.release();
return grayMat;
}
}
JPEG轉Mat
實際測試Camera2也能輸出JPEG格式
import android.annotation.TargetApi;
import android.media.Image;
import android.os.Build;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import java.nio.ByteBuffer;
@TargetApi(Build.VERSION_CODES.KITKAT)
public class Jpeg {
private static Mat jpeg(Image image) {
Image.Plane plan = image.getPlanes()[0];
ByteBuffer data = plan.getBuffer();
data.position(0);
// 從通道讀取的圖片為JPEG逗扒,并不能直接使用,
// 將其保存在一維數(shù)組里
return new Mat(1, data.remaining(), CvType.CV_8U, data);
}
public static Mat rgb(Image image) {
Mat jpeg = jpeg(image);
// 通過cv::imdecode將其解析為彩色圖
Mat mat = Imgcodecs.imdecode(jpeg, Imgcodecs.CV_LOAD_IMAGE_COLOR);
jpeg.release();
return mat;
}
public static Mat gray(Image image) {
Mat jpeg = jpeg(image);
// 通過cv::imdecode將其解析為灰色圖
Mat mat = Imgcodecs.imdecode(jpeg, Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);
jpeg.release();
return mat;
}
}