Mit Kamera2-API mit ImageReader
Ich versuche zu erfassen, Bilddaten mit der Kamera 2 API auf einem Galaxy S4. ImageReader ist als die Oberfläche provider. Das Bild-format verwendet wurde, versuchte mit beiden ImageFormat.YV12-und ImageFormat.YUV_420_888 und liefert die gleichen Ergebnisse.
Setup scheint gut, und ich bekomme ein Bild aus dem ImageReader mit ImageReader. Das Bild hat 3 Ebenen. Die Puffer sind die erwarteten Größen, Breite*Höhe für Y-Ebene und (Breite*Höhe)/4 für die beiden anderen Ebenen.
Das Problem ist, dass ich nicht immer die Daten richtig in zweierlei Hinsicht. Das erste Problem ist, dass die Y-Ebene Daten in Spiegel-Bild. Dies kann behandelt werden, obwohl es seltsam ist, so bin ich neugierig, ob dies zu erwarten ist.
Das schlimmere Problem ist, dass die beiden anderen Ebenen scheinen nicht zu liefern Daten nicht korrekt auf allen. Zum Beispiel mit einem Bild mit der Größe 640x480, die Ergebnisse in U-und V-puffergrößen von 76800 bytes, werden nur die ersten 320 bytes, die Puffer sind nicht-null-Werte. Diese Zahl schwankt und scheint nicht zu gehen, das Verhältnis zwischen verschiedenen Bildgrößen, aber scheint konsequent zu sein, zwischen den Bildern für jede Größe.
Frage ich mich, ob es gibt etwas, dass ich vermisst werde in der Verwendung dieser API. Code ist unten.
public class OnboardCamera {
private final String TAG = "OnboardCamera";
int mWidth = 1280;
int mHeight = 720;
int mYSize = mWidth*mHeight;
int mUVSize = mYSize/4;
int mFrameSize = mYSize+(mUVSize*2);
//handler for the camera
private HandlerThread mCameraHandlerThread;
private Handler mCameraHandler;
//the size of the ImageReader determines the output from the camera.
private ImageReader mImageReader = ImageReader.newInstance(mWidth, mHeight, ImageFormat.YV12, 30);
private Surface mCameraRecieverSurface = mImageReader.getSurface();
{
mImageReader.setOnImageAvailableListener(mImageAvailListener, mCameraHandler);
}
private byte[] tempYbuffer = new byte[mYSize];
private byte[] tempUbuffer = new byte[mUVSize];
private byte[] tempVbuffer = new byte[mUVSize];
ImageReader.OnImageAvailableListener mImageAvailListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
//when a buffer is available from the camera
//get the image
Image image = reader.acquireNextImage();
Image.Plane[] planes = image.getPlanes();
//copy it into a byte[]
byte[] outFrame = new byte[mFrameSize];
int outFrameNextIndex = 0;
ByteBuffer sourceBuffer = planes[0].getBuffer();
sourceBuffer.get(tempYbuffer, 0, tempYbuffer.length);
ByteBuffer vByteBuf = planes[1].getBuffer();
vByteBuf.get(tempVbuffer);
ByteBuffer yByteBuf = planes[2].getBuffer();
yByteBuf.get(tempUbuffer);
//free the Image
image.close();
}
};
OnboardCamera() {
mCameraHandlerThread = new HandlerThread("mCameraHandlerThread");
mCameraHandlerThread.start();
mCameraHandler = new Handler(mCameraHandlerThread.getLooper());
}
@Override
public boolean startProducing() {
CameraManager cm = (CameraManager) Ten8Application.getAppContext().getSystemService(Context.CAMERA_SERVICE);
try {
String[] cameraList = cm.getCameraIdList();
for (String cd: cameraList) {
//get camera characteristics
CameraCharacteristics mCameraCharacteristics = cm.getCameraCharacteristics(cd);
//check if the camera is in the back - if not, continue to next
if (mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING) != CameraCharacteristics.LENS_FACING_BACK) {
continue;
}
//get StreamConfigurationMap - supported image formats
StreamConfigurationMap scm = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
android.util.Size[] sizes = scm.getOutputSizes(ImageFormat.YV12);
cm.openCamera(cd, mDeviceStateCallback, mCameraHandler);
}
} catch (CameraAccessException e) {
e.printStackTrace();
Log.e(TAG, "CameraAccessException detected", e);
}
return false;
}
private final CameraDevice.StateCallback mDeviceStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
//make list of surfaces to give to camera
List<Surface> surfaceList = new ArrayList<>();
surfaceList.add(mCameraRecieverSurface);
try {
camera.createCaptureSession(surfaceList, mCaptureSessionStateCallback, mCameraHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "createCaptureSession threw CameraAccessException.", e);
}
}
@Override
public void onDisconnected(CameraDevice camera) {
}
@Override
public void onError(CameraDevice camera, int error) {
}
};
private final CameraCaptureSession.StateCallback mCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
CaptureRequest.Builder requestBuilder = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
requestBuilder.addTarget(mCameraRecieverSurface);
//set to null - image data will be produced but will not receive metadata
session.setRepeatingRequest(requestBuilder.build(), null, mCameraHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "createCaptureSession threw CameraAccessException.", e);
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
};
}
InformationsquelleAutor Nicholas Spencer | 2015-07-10
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich hatte das gleiche Problem, das problem, das ich glaube, war in der Android-API-21. Ich habe ein Upgrade auf API-23 und den gleichen code geklappt. Getestet auch auf API-22 und es auch funktioniert.
InformationsquelleAutor Lyubomir Dinchev
Sind Sie Aufmerksamkeit auf das Bild.Flugzeug-Zeilen - und pixelStride Parameter?
Aufgrund von hardware-Speicher-mapping Einschränkungen, die Reihe Schritt ist oft größer als die Breite des Bildes und der Beginn der Zeile y des Bildes an der position (yrowStride) statt (yBreite) in das ByteArray für ein bestimmtes Flugzeug.
Wenn das der Fall ist, ist es nicht verwunderlich, dass nach den ersten 320 bytes für ein Bild der Größe 640x480 (1 Zeile aus einer Unterstichprobe (subsampled) chroma-Daten), die U-oder V-Ebene 0 für einige Zeit - sollte es (rowStride - Breite) bytes von Nullen oder Müll, und dann die nächste Zeile der pixel-Daten wird gestartet.
Beachten Sie, dass, wenn pixelStride ist nicht 1, dann haben Sie auch überspringen bytes zwischen den pixel-Werten; dies wird oft verwendet, wenn die zugrunde liegenden YCbCr-Puffer ist eigentlich semi-planar nicht planar.
InformationsquelleAutor Eddy Talvala