Gafas de visión crepuscular. Android Camera2 API de Maker Part 5 Flash



Viviendo en la era de los avances y logros tecnológicos, observando cómo los cohetes Mask y Bezos se precipitan hacia el cielo, nosotros, las personas comunes con educación técnica superior, a menudo no notamos la posibilidad de hacer un avance no allí, lejos en el espacio, sino aquí al lado de nosotros, literalmente No levantarse del sofá de la mesa.

Juzgue usted mismo qué descubrimiento puede llevar a leer un artículo regular sobre teléfonos inteligentes modernos. No daré la fuente, para no compartir ingresos futuros.
, , . « » Google Pixel. IT RAW, HDR-, «», . Pixel 4 «Night Sight» . : , . , .

Otra cosa es que caminar de noche y restregar la pantalla de un teléfono móvil es algo incómodo, incluso en modo nocturno. Y luego mis ojos cayeron accidentalmente en un auricular VR para un teléfono inteligente, acostado en un estante. ¡Ha ocurrido un gran avance! Solo queda, usándolo y el conocimiento acumulado en cuatro publicaciones sobre la API de Android Camera2, para dirigir la imagen de la "Visión nocturna" directamente al ojo. Al mismo tiempo, las manos serán libres para atrapar a un gato negro en una habitación oscura. Sin luz, por supuesto, no funcionará, fotones, al menos un poco, pero es necesario. Pero al menos debemos alcanzar (o incluso superar) el nivel de los mirones de Kotan en la oscuridad.

Entonces, para aprender a ver en la oscuridad, necesitamos:

1: un casco de realidad virtual para un teléfono inteligente, (el más barato posible)



2: un teléfono inteligente con soporte para guglofich moderno para una cámara (bueno, definitivamente no será el más barato)



3: conocimiento de los conceptos básicos de Android Camera2 API (ya lo tenemos)

parte uno
parte dos
parte tres
parte cuatro

Abrimos un nuevo proyecto en Android Studio y comenzamos a esculpir el código.

Lo primero que debe hacer es ensamblar la superficie de realidad virtual que brillará en los auriculares.

Diseño
<?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>

La salida debería tener algo como esto:



como puede ver, las texturas resultaron ser rectangulares, mientras que en cualquier juguete de realidad virtual para un teléfono inteligente, los desarrolladores de alguna manera logran hacerlas redondas. Pero no nos detendremos en esto. Más experiencia aún demostrará que lo hará.

Para esto, de hecho, escribimos una Actividad modesta, en la que todo ya nos es familiar de artículos anteriores.

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();
    }
}

Sí, y no te olvides de

Manifiesto
<?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>


Ahora colocamos el teléfono inteligente en los auriculares VR y caminamos por la casa, disfrutando de la visión del cyborg con una resolución de 1280 x 1024 para cada ojo. Las sensaciones son, por supuesto, extrañas, con una pérdida de profundidad de visión, pero aún así son geniales. Lo único es que se ve un poco oscuro, pero esto se debe a que el panel frontal de auriculares translúcidos interfiere. Por lo tanto, es necesario hacer un agujero delante de la cámara del teléfono inteligente. Pero, de nuevo, en los modelos de VR más presupuestarios, tal panel puede no existir en absoluto, y es probable que no tenga que contaminarse con el trabajo manual.

Todo lo que queda ahora es convencer a la API de la cámara de Google de que tenemos una oscuridad total, y sería bueno usar el modo de Visión Nocturna, y con todo esto RAW, HDR-apilamiento y reconocimiento de escena por redes neuronales .

Para hacer esto, solo escribe en la sesión:

mPreviewBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
                                            CaptureRequest.CONTROL_SCENE_MODE_NIGHT);


y desenroscar la exposición y fotosensibilidad al máximo

 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);


¡Estoy ciego!



Esto es lo que el gato ve cuando lo expulsan de la habitación, donde evita que las personas tengan relaciones sexuales en la sala de estar.

Pero, por supuesto, se trata de enumeración y parámetros (y hay muchos de ellos en la API, y aquí hay solo un par) que necesita ajustar más tarde por experiencia.

Ahora solo podemos esperar a la noche. No sin luna, por supuesto, con una densa capa de nubes en algún lugar de la taiga, sino una noche normal con fotones voladores al azar. Y esto es lo que sucederá ...

Aunque parece que con un disparo normal, casi nada es visible.



Pero las cámaras modernas hacen maravillas y todavía encuentran un gato negro ...



Ahora puedes caminar de noche, porque durante el día no puedes por la cuarentena. En teoría, también es imposible por la noche, pero quién te verá, acechando en la oscuridad de la noche con auriculares VR en sus cabezas ...

All Articles