In einer Zeit technologischer Durchbrüche und Errungenschaften, in der wir sehen, wie die Raketen Mask und Bezos in den Himmel rasen, bemerken wir, normale Menschen mit einer höheren technischen Ausbildung, oft nicht die Möglichkeit, einen Durchbruch nicht dort, weit im Weltraum, sondern buchstäblich hier neben uns zu erzielen nicht vom Sofa vom Tisch aufstehen .Überzeugen Sie sich selbst, welche Entdeckung dazu führen kann, dass Sie regelmäßig einen Artikel über moderne Smartphones lesen. Ich werde die Quelle nicht angeben, um zukünftige Einnahmen nicht zu teilen., , . « » Google Pixel. IT RAW, HDR-, «», . Pixel 4 «Night Sight» . : , . , .
Eine andere Sache ist, dass das Gehen in der Nacht und das Schrubben auf dem Bildschirm eines Mobiltelefons selbst im Nachtmodus irgendwie unangenehm ist. Und dann fiel mein Blick versehentlich auf ein VR-Headset für ein Smartphone, das auf einem Regal lag. Der Durchbruch ist wahr geworden! Es bleibt nur, es und das in vier Beiträgen über die Android Camera2-API gesammelte Wissen zu verwenden, um das Bild vom "Nachtsichtgerät" direkt ins Auge zu lenken. Gleichzeitig haben die Hände die Freiheit, eine schwarze Katze in einem dunklen Raum zu fangen. Ohne Licht wird es natürlich nicht funktionieren, Photonen, zumindest ein wenig, aber es ist notwendig. Aber zumindest müssen wir das Niveau von Kotans Peeper im Dunkeln erreichen (oder sogar übertreffen).Um zu lernen, wie man im Dunkeln sieht, benötigen wir:1: ein Virtual-Reality-Headset für ein Smartphone (das billigste, das möglich ist)
2: ein Smartphone mit Unterstützung für modernes Guglofich für eine Kamera (es wird definitiv nicht das billigste sein)
3: Kenntnisse der Grundlagen der Android Camera2-API (wir haben es bereits)Teil einsTeil zweiTeil dreiTeil vierÖffnen Sie ein neues Projekt in Android Studio und beginnen Sie mit der Modellierung des Codes.Als erstes müssen Sie die tatsächliche VR-Oberfläche zusammenbauen, die im Headset leuchtet.Layout<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#03061B"
tools:context=".MainActivity">
<TextureView
android:id="@+id/textureView"
android:layout_width="240dp"
android:layout_height="320dp"
android:layout_marginTop="28dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextureView
android:id="@+id/textureView3"
android:layout_width="240dp"
android:layout_height="320dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textureView" />
<LinearLayout
android:layout_width="165dp"
android:layout_height="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textureView3"
app:layout_constraintVertical_bias="0.838">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:backgroundTint="#3F51B5"
android:text=""
android:textColor="#1A87DD" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="37dp"
android:backgroundTint="#3F51B5"
android:text=""
android:textColor="#2196F3" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Die Ausgabe sollte ungefähr so aussehen:
Wie Sie sehen können, erwiesen sich die Texturen als rechteckig, während Entwickler es in jedem VR-Spielzeug für ein Smartphone irgendwie schaffen, sie rund zu machen. Aber darauf werden wir nicht näher eingehen. Weitere Erfahrungen werden noch zeigen, dass es geht.Dafür schreiben wir tatsächlich eine bescheidene Aktivität, in der uns alles bereits aus früheren Artikeln bekannt ist.package com.example.twovideosurfaces;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.StrictMode;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
public static final String LOG_TAG = "myLogs";
public static Surface surface1 = null;
public static Surface surface2 = null;
CameraService[] myCameras = null;
private CameraManager mCameraManager = null;
private final int CAMERA1 = 0;
private Button mOn = null;
private Button mOff = null;
public static TextureView mImageViewUp = null;
public static TextureView mImageViewDown = null;
private HandlerThread mBackgroundThread;
private Handler mBackgroundHandler = null;
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("CameraBackground");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
private void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_main);
Log.d(LOG_TAG, " ");
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
||
(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
) {
requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
mOn = findViewById(R.id.button1);
mOff = findViewById(R.id.button3);
mImageViewUp = findViewById(R.id.textureView);
mImageViewDown = findViewById(R.id.textureView3);
mOn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (myCameras[CAMERA1] != null) {
if (!myCameras[CAMERA1].isOpen()) myCameras[CAMERA1].openCamera();
}
}
});
mOff.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
myCameras = new CameraService[mCameraManager.getCameraIdList().length];
for (String cameraID : mCameraManager.getCameraIdList()) {
Log.i(LOG_TAG, "cameraID: " + cameraID);
int id = Integer.parseInt(cameraID);
myCameras[id] = new CameraService(mCameraManager, cameraID);
}
} catch (CameraAccessException e) {
Log.e(LOG_TAG, e.getMessage());
e.printStackTrace();
}
}
public class CameraService {
private String mCameraID;
private CameraDevice mCameraDevice = null;
private CameraCaptureSession mSession;
private CaptureRequest.Builder mPreviewBuilder;
public CameraService(CameraManager cameraManager, String cameraID) {
mCameraManager = cameraManager;
mCameraID = cameraID;
}
private CameraDevice.StateCallback mCameraCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
Log.i(LOG_TAG, "Open camera with id:" + mCameraDevice.getId());
startCameraPreviewSession();
}
@Override
public void onDisconnected(CameraDevice camera) {
mCameraDevice.close();
Log.i(LOG_TAG, "disconnect camera with id:" + mCameraDevice.getId());
mCameraDevice = null;
}
@Override
public void onError(CameraDevice camera, int error) {
Log.i(LOG_TAG, "error! camera id:" + camera.getId() + " error:" + error);
}
};
private void startCameraPreviewSession() {
SurfaceTexture texture = mImageViewUp.getSurfaceTexture();
texture.setDefaultBufferSize(1280, 1024);
surface1 = new Surface(texture);
SurfaceTexture texture2 = mImageViewDown.getSurfaceTexture();
surface2 = new Surface(texture2);
texture2.setDefaultBufferSize(1280, 1024);
try {
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
mPreviewBuilder.addTarget(surface1);
mPreviewBuilder.addTarget(surface2);
mCameraDevice.createCaptureSession(Arrays.asList(surface1,surface2),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
mSession = session;
try {
mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
public boolean isOpen() {
if (mCameraDevice == null) {
return false;
} else {
return true;
}
}
public void openCamera() {
try {
if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
mCameraManager.openCamera(mCameraID, mCameraCallback, mBackgroundHandler);
}
} catch (CameraAccessException e) {
Log.i(LOG_TAG, e.getMessage());
}
}
public void closeCamera() {
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
}
}
@Override
public void onPause() {
if (myCameras[CAMERA1].isOpen()) {
myCameras[CAMERA1].closeCamera();
}
stopBackgroundThread();
super.onPause();
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
}
}
Ja und nicht vergessenManifest<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.twovideosurfaces">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.NoActionBar"
>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Jetzt schieben wir das Smartphone in das VR-Headset und gehen um das Haus herum. Wir genießen die Vision des Cyborgs mit einer Auflösung von 1280 x 1024 für jedes Auge. Die Empfindungen sind natürlich seltsam, mit einem Verlust der Sichttiefe, aber immer noch cool. Das einzige ist, dass es ein bisschen dunkel aussieht, aber das liegt daran, dass das vordere durchscheinende Headset-Panel stört. Daher muss vor der Kamera des Smartphones ein Loch gebohrt werden. Andererseits gibt es bei den preisgünstigsten VR-Modellen möglicherweise überhaupt kein solches Panel, und es ist wahrscheinlich, dass Sie sich nicht mit Handarbeit beschmutzen müssen.Jetzt müssen wir nur noch die Google-Kamera-API davon überzeugen, dass wir völlig dunkel sind. Es wäre schön, den Nachtsichtmodus und damit all diese RAW-, HDR-Stapel- und Szenenerkennung durch neuronale Netze zu verwenden .Schreiben Sie dazu einfach in die Sitzung:mPreviewBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
CaptureRequest.CONTROL_SCENE_MODE_NIGHT);
und schrauben Sie die Belichtung und Lichtempfindlichkeit maximal ab mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_OFF);
mPreviewBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME,Long.valueOf("100000000"));
mPreviewBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, 30000);
Oh, ich bin blind!
Dies stellt sich heraus, wenn die Katze aus dem Schlafzimmer geworfen wird, wo er verhindert, dass Menschen im Wohnzimmer Sex haben.Aber natürlich sind dies Aufzählung und Parameter (und es gibt viele davon in der API, und hier sind nur ein paar), die Sie später empirisch optimieren müssen.Jetzt können wir nur noch auf die Nacht warten. Natürlich nicht mondlos, mit dichter Wolkendecke irgendwo in der Taiga, sondern eine gewöhnliche Nacht mit zufällig fliegenden Photonen. Und hier ist, was passieren wird ...Obwohl es so aussieht, als ob bei normalen Aufnahmen fast nichts sichtbar ist.Aber moderne Kameras wirken Wunder und finden immer noch eine schwarze Katze ...Jetzt können Sie nachts laufen, weil Sie tagsüber wegen Quarantäne nicht gehen können. Theoretisch ist es auch nachts unmöglich, aber wer wird dich sehen, wenn du mit VR-Headsets auf dem Kopf in der Dunkelheit der Nacht herumpirschst ...