Kacamata penglihatan suram. API Android Camera2 dari Maker Part 5 Flash



Hidup di era terobosan teknologi dan pencapaian, melihat bagaimana Topeng dan roket Bezos meluncur ke langit, kita, orang-orang biasa dengan pendidikan teknis yang lebih tinggi, sering tidak menyadari kemungkinan membuat terobosan tidak di sana, jauh di luar angkasa, tetapi di sini di sebelah kita, secara harfiah tidak bangun dari sofa dari meja.

Nilailah sendiri apa yang dapat menyebabkan penemuan membaca artikel reguler tentang smartphone modern. Saya tidak akan memberikan sumbernya, agar tidak membagikan penghasilan masa depan.
, , . « » Google Pixel. IT RAW, HDR-, «», . Pixel 4 «Night Sight» . : , . , .

Hal lain adalah berjalan di malam hari dan menggosok layar ponsel agak tidak nyaman, bahkan dalam mode malam. Dan kemudian mata saya secara tidak sengaja jatuh pada headset VR untuk smartphone, berbaring di rak. Terobosan telah menjadi kenyataan! Tetap saja, menggunakannya dan pengetahuan yang dikumpulkan selama empat posting tentang Android Camera2 API, untuk mengarahkan gambar dari "Night Sight" langsung ke mata. Pada saat yang sama, tangan akan bebas menangkap kucing hitam di ruangan yang gelap. Tanpa cahaya, tentu saja, itu tidak akan berfungsi, foton, setidaknya sedikit, tetapi itu perlu. Tetapi setidaknya kita harus mencapai (atau bahkan melampaui) tingkat rekan-rekan Kotan dalam gelap.

Jadi, untuk mempelajari cara melihat dalam gelap, kita perlu:

1: headset realitas virtual untuk smartphone, (yang termurah mungkin)



2: smartphone dengan dukungan untuk guglofich modern untuk kamera (well, pasti tidak akan termurah)



3: pengetahuan tentang dasar-dasar Android Camera2 API (kami sudah memilikinya)

bagian satu
bagian dua
bagian tiga
bagian empat

Kami membuka proyek baru di Android Studio dan mulai memahat kode.

Hal pertama yang harus dilakukan adalah merakit permukaan VR yang sebenarnya yang akan bersinar di headset.

Tata letak
<?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>

Outputnya harus seperti ini:



Seperti yang Anda lihat, teksturnya ternyata persegi panjang, sementara di mainan VR apa pun untuk ponsel cerdas, pengembang entah bagaimana berhasil membuatnya bulat. Tetapi kita tidak akan memikirkan hal ini. Pengalaman lebih lanjut masih akan menunjukkan bahwa itu akan berhasil.

Untuk ini, pada kenyataannya, kami menulis Kegiatan sederhana, di mana semuanya sudah akrab bagi kami dari artikel sebelumnya.

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

Ya, dan jangan lupakan

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


Sekarang kami mendorong smartphone ke headset VR dan berjalan di sekitar rumah, menikmati visi cyborg dengan resolusi 1280 x 1024 untuk setiap mata. Sensasinya, tentu saja, aneh, dengan hilangnya kedalaman penglihatan, tetapi tetap dingin. Satu-satunya hal yang terlihat agak gelap, tetapi ini karena panel headset tembus depan mengganggu. Karena itu, perlu dibuat lubang di depannya di depan kamera smartphone. Tetapi sekali lagi, pada model VR yang paling murah, panel semacam itu bahkan mungkin tidak ada sama sekali, dan kemungkinan Anda tidak perlu mencemari diri dengan tenaga manual.

Yang tersisa sekarang adalah meyakinkan API kamera Google bahwa kami memiliki kegelapan total, dan ada baiknya menggunakan mode Night Vision, dan dengan itu semua RAW, penumpukan HDR, dan pengenalan pemandangan oleh jaringan saraf .

Untuk melakukan ini, cukup tulis di sesi:

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


dan buka eksposur dan sensitivitas foto secara maksimal

 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, aku buta!



Inilah yang kucing lihat ketika dia dilempar keluar dari kamar tidur, tempat dia mencegah orang-orang berhubungan seks di ruang tamu.

Tapi tentu saja, ini adalah enumerasi dan parameter (dan ada banyak dari mereka di API, dan di sini hanya beberapa) Anda perlu mengubahnya nanti secara empiris.

Sekarang kita hanya bisa menunggu malam. Bukan bulan, tentu saja, dengan awan tebal di suatu tempat di taiga, tapi malam biasa dengan foton terbang acak. Dan inilah yang akan terjadi ...

Meskipun kelihatannya dengan pemotretan normal, hampir tidak ada yang terlihat.



Tapi kamera modern bekerja dengan sangat baik dan masih menemukan ...



Sekarang Anda bisa berjalan di malam hari, karena pada siang hari Anda tidak bisa karena karantina. Secara teori, itu juga mustahil di malam hari, tetapi siapa yang akan melihat Anda, berjalan di kegelapan malam dengan headset VR di kepala mereka ...

All Articles