Opcje konfiguracji

Każdy przypadek użycia CameraX konfigurujesz osobno, aby kontrolować różne aspekty jego działania.

Na przykład w przypadku rejestrowania obrazów możesz określić docelowy format obrazu i tryb lampy błyskowej. Oto przykład:

Kotlin

val imageCapture = ImageCapture.Builder()
    .setFlashMode(...)
    .setTargetAspectRatio(...)
    .build()

Java

ImageCapture imageCapture =
    new ImageCapture.Builder()
        .setFlashMode(...)
        .setTargetAspectRatio(...)
        .build();

Oprócz opcji konfiguracji niektóre przypadki użycia udostępniają interfejsy API, aby dynamicznie zmieniać ustawienia po utworzeniu przypadku użycia. Informacje o konfiguracji związanej z poszczególnymi przypadkami użycia znajdziesz w artykułach Wdrażanie podglądu, Analiza obrazówZapisywanie obrazów.

CameraXConfig

W celu uproszczenia CameraX ma domyślne konfiguracje, takie jak wewnętrzne moduły wykonawcze i moduły obsługi, które są odpowiednie w większości scenariuszy użycia. Jeśli jednak Twoja aplikacja ma specjalne wymagania lub preferuje dostosowanie tych konfiguracji, możesz użyć interfejsu CameraXConfig.

Dzięki CameraXConfig aplikacja może:

Model wykorzystania

Poniżej znajdziesz opis procedury korzystania z CameraXConfig:

  1. Utwórz obiekt CameraXConfig z niestandardowymi konfiguracjami.
  2. Zaimplementuj tag CameraXConfig.Provider w Application, zwraca obiekt CameraXConfig w getCameraXConfig().
  3. Dodaj zajęcia Application do pliku AndroidManifest.xml w sposób opisany tutaj.

Na przykład ten przykładowy kod ogranicza rejestrowanie CameraX tylko do wiadomości o błędach:

Kotlin

class CameraApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
           .setMinimumLoggingLevel(Log.ERROR).build()
   }
}

Zachowaj lokalną kopię obiektu CameraXConfig, jeśli aplikacja tego wymaga i poznanie konfiguracji kamery X.

Ogranicznik aparatu

Podczas pierwszego wywołania funkcji ProcessCameraProvider.getInstance() CameraX wylicza właściwości kamer dostępnych na urządzenia. Ponieważ CameraX musi komunikować się ze sprzętowymi komponentami, ten proces może zająć sporo czasu w przypadku każdej kamery, zwłaszcza na urządzeniach niskiego poziomu. Jeśli aplikacja używa tylko określonych aparatów na urządzeniu, takich jak domyślny przedni aparat, w aparacie CameraX możesz ustawić ignorowanie innych kamer, co może zmniejszyć czas oczekiwania na uruchomienie kamer, z których korzysta Twoja aplikacja.

Jeśli CameraSelector zaliczono do CameraXConfig.Builder.setAvailableCamerasLimiter() odfiltrowuje kamerę, więc AparatX zachowuje się tak, jakby ona nie istniała. Na przykład ten kod ogranicza aplikację do korzystania tylko z domyślnego tylnego aparatu urządzenia:

Kotlin

class MainApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
              .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
              .build()
   }
}

Wątki

Wiele interfejsów API platformy, na których opiera się aplikacja CameraX, wymaga blokowania komunikację międzyprocesową (IPC) za pomocą sprzętu, który czasami może trwać setki, na odpowiedź w ciągu milisekund. Z tego powodu CameraX wywołuje te interfejsy API tylko z wątków w tle, aby wątek główny nie był zablokowany, a interfejs użytkownika działał płynnie. CameraX zarządza tymi wątkami w tle wewnętrznie, aby to działanie było przezroczyste. Niektóre aplikacje wymagają jednak ścisłej kontroli wątków. CameraXConfig zezwala aplikacji na ustawianie wątków w tle używane przez CameraXConfig.Builder.setCameraExecutor(). oraz CameraXConfig.Builder.setSchedulerHandler()

Wykonawca aparatu

Wykonawca kamery jest używany we wszystkich wewnętrznych wywołaniach interfejsu API platformy Camera oraz w przypadku wywołań zwrotnych z tych interfejsów API. CameraX przydziela i zarządza wewnętrznym Executor, aby wykonywać te zadania. Jeśli jednak Twoja aplikacja wymaga ściślej kontroli wątków, użyj CameraXConfig.Builder.setCameraExecutor().

Scheduler Handler

Obsługa harmonogramu służy do planowania zadań wewnętrznych w określonych odstępach czasu, na przykład do ponownego próby otwarcia kamery, gdy jest niedostępna. Ten handler nie wykonuje zadań, tylko przekazuje je do wykonawcy kamery. Jest także na starszych platformach API, które wymagają Handler na potrzeby wywołań zwrotnych. W takich przypadkach wywołania zwrotne są nadal wysyłane bezpośrednio do wykonawcy kamery. CameraX przydziela i zarządza wewnętrznym HandlerThread, aby wykonywać te zadania, ale możesz go zastąpić za pomocą CameraXConfig.Builder.setSchedulerHandler().

Logowanie

Rejestrowanie w CameraX pozwala aplikacjom filtrować komunikaty logcat, , aby unikać szczegółowych komunikatów w kodzie produkcyjnym. CameraX obsługuje 4 poziomy rejestrowania, od najbardziej obszernego do najbardziej rygorystycznego:

  • Log.DEBUG (domyślnie)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

Szczegółowe opisy tych poziomów logowania znajdziesz w dokumentacji dzienników Androida. Aby ustawić odpowiedni poziom rejestrowania dla aplikacji, użyj parametru CameraXConfig.Builder.setMinimumLoggingLevel(int).

Automatyczny wybór

CameraX automatycznie udostępnia funkcje właściwe dla urządzenia, która działa. Na przykład CameraX automatycznie określa najlepszą rozdzielczość, jeśli nie określisz jej samodzielnie lub jeśli wybrana rozdzielczość nie jest obsługiwana. Biblioteka zajmuje się wszystkimi tymi zadaniami, dzięki czemu nie musisz pisać kodu dla poszczególnych urządzeń.

Celem CameraX jest inicjowanie sesji aparatu. Oznacza to, że W zależności od możliwości urządzenia CameraX ogranicza rozdzielczość i formaty obrazu. Do kompromisu może dojść, ponieważ:

  • Urządzenie nie obsługuje żądanej rozdzielczości.
  • Urządzenie ma problemy ze zgodnością, np. starsze urządzenia, które wymagają określonych rozdzielczości, aby działać prawidłowo.
  • Na niektórych urządzeniach pewne formaty są dostępne tylko w określonych aspektach współczynników proporcji.
  • Urządzenie preferuje „najbliższy model 16” JPEG lub wideo kodowanie. Więcej informacji znajdziesz w sekcji SCALER_STREAM_CONFIGURATION_MAP.

Chociaż AparatX tworzy sesję i zarządza nią, zawsze sprawdzaj zwracały rozmiary obrazów na danych wyjściowych przypadku użycia w kodzie i odpowiednio je dostosuj.

Obrót

Domyślnie podczas tworzenia przykładu zastosowania obracanie kamery jest dopasowywane do domyślnego obracania wyświetlacza. W tym domyślnym przypadku CameraX generuje dane wyjściowe, aby aplikacja mogła dopasować obraz do tego, co ma się wyświetlać w podglądzie. Aby obsługiwać urządzenia z wieloma wyświetlaczami, możesz zmienić rotację na wartość niestandardową, przekazując bieżący kierunek wyświetlania podczas konfigurowania obiektów związanych z przypadkiem użycia lub dynamicznie po ich utworzeniu.

Aplikacja może ustawić rotację docelową za pomocą ustawień konfiguracji. Następnie może zaktualizować ustawienia rotacji za pomocą metod z interfejsów API do obsługi przypadków użycia (np. ImageAnalysis.setTargetRotation()), nawet gdy cykl życia jest w stanie uruchomionym. Możesz użyć tej metody, gdy aplikacja jest zablokowana w układzie pionowym (nie ma więc potrzeby rekonfiguracji po obróceniu), ale aplikacja do zdjęć lub analizy musi znać bieżący obrót urządzenia. Może na przykład być potrzebna świadomość obrotu, aby twarze były prawidłowo zorientowane pod kątem wykrywania twarzy lub aby zdjęcia były w orientacji poziomej bądź pionowej.

Dane dotyczące zarejestrowanych obrazów mogą być przechowywane bez informacji o ich obrocie. Dane EXIF zawiera informacje o obrocie, dzięki czemu aplikacje galerii mogą wyświetlać obraz ustaw prawidłową orientację po zapisaniu.

Aby wyświetlać dane podglądu w prawidłowej orientacji, możesz użyć danych wyjściowych metadanych z Preview.PreviewOutput() do tworzenia przekształceń.

Poniższy przykładowy kod pokazuje, jak ustawić obrót w zdarzeniu orientacji:

Kotlin

override fun onCreate() {
    val imageCapture = ImageCapture.Builder().build()

    val orientationEventListener = object : OrientationEventListener(this as Context) {
        override fun onOrientationChanged(orientation : Int) {
            // Monitors orientation values to determine the target rotation value
            val rotation : Int = when (orientation) {
                in 45..134 -> Surface.ROTATION_270
                in 135..224 -> Surface.ROTATION_180
                in 225..314 -> Surface.ROTATION_90
                else -> Surface.ROTATION_0
            }

            imageCapture.targetRotation = rotation
        }
    }
    orientationEventListener.enable()
}

Java

@Override
public void onCreate() {
    ImageCapture imageCapture = new ImageCapture.Builder().build();

    OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) {
       @Override
       public void onOrientationChanged(int orientation) {
           int rotation;

           // Monitors orientation values to determine the target rotation value
           if (orientation >= 45 && orientation < 135) {
               rotation = Surface.ROTATION_270;
           } else if (orientation >= 135 && orientation < 225) {
               rotation = Surface.ROTATION_180;
           } else if (orientation >= 225 && orientation < 315) {
               rotation = Surface.ROTATION_90;
           } else {
               rotation = Surface.ROTATION_0;
           }

           imageCapture.setTargetRotation(rotation);
       }
    };

    orientationEventListener.enable();
}

W zależności od ustawionej rotacji każdy przypadek użycia albo obraca dane obrazu bezpośrednio, albo udostępnia metadane rotacji odbiorcom nieobrócone dane obrazu.

  • Podgląd: dane wyjściowe metadanych są dostarczane w taki sposób, aby rotacja docelowej rozdzielczości była znana za pomocą Preview.getTargetRotation().
  • ImageAnalysis: dane wyjściowe metadanych są dostarczane w taki sposób, aby współrzędne bufora obrazu były znane w stosunku do współrzędnych wyświetlacza.
  • ImageCapture: metadane Exif obrazu, bufor lub bufor i metadane są zmieniane, aby uwzględnić ustawienie obrotu. Zmieniona wartość zależy od wdrożenia HAL.

Przytnij jako prostokąt

Domyślnie prostokąt przycięcia jest równy pełnemu prostokątowi bufora. Możesz go dostosować w ten sposób: ViewPort i UseCaseGroup Dzięki grupowaniu przypadków użycia i ustawieniu widoku CameraX zapewnia, że prostokąty przycinania wszystkich przypadków użycia w grupie wskazują ten sam obszar na czujniku aparatu.

Fragment kodu poniżej pokazuje, jak używać tych 2 klas:

Kotlin

val viewPort =  ViewPort.Builder(Rational(width, height), display.rotation).build()
val useCaseGroup = UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build()
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)

Java

ViewPort viewPort = new ViewPort.Builder(
         new Rational(width, height),
         getDisplay().getRotation()).build();
UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build();
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);

ViewPort określa pole bufora widoczne dla użytkowników. Następnie CameraX oblicza największy możliwy prostokąt przycinania na podstawie właściwości widocznego obszaru i dołączonych przypadków użycia. Aby uzyskać efekt WYSIWYG, możesz skonfigurować widoczny obszar na podstawie przypadku użycia podglądu. Prosty sposób na uzyskanie widocznego obszaru to użycie PreviewView.

Fragmenty kodu poniżej pokazują, jak uzyskać obiekt ViewPort:

Kotlin

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

Java

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

W powyższym przykładzie dane, które aplikacja otrzymuje z ImageAnalysisImageCapture, są zgodne z tym, co użytkownik końcowy widzi w PreviewView, o ile typ skali PreviewView jest ustawiony na domyślny, czyli FILL_CENTER. Po zastosowaniu prostokąta przycinania i obrotu do bufora wyjściowego obraz we wszystkich przypadkach użycia jest taki sam, ale może mieć różne rozdzielczości. Więcej o tym, jak zastosować informacje o przekształceniu, dowiesz się z artykułu Transform (transform) dane wyjściowe.

Wybór kamery

CameraX automatycznie wybiera najlepszy aparat dla danej aplikacji do wymagań i przypadków użycia. Jeśli chcesz użyć innego urządzenia niż wybrane przez nas, masz kilka opcji:

Poniższy przykładowy kod pokazuje, jak utworzyć CameraSelector do wpływać na wybór urządzenia:

Kotlin

fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? {
   val cam2Infos = provider.availableCameraInfos.map {
       Camera2CameraInfo.from(it)
   }.sortedByDescending {
       // HARDWARE_LEVEL is Int type, with the order of:
       // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL
       it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
   }

   return when {
       cam2Infos.isNotEmpty() -> {
           CameraSelector.Builder()
               .addCameraFilter {
                   it.filter { camInfo ->
                       // cam2Infos[0] is either EXTERNAL or best built-in camera
                       val thisCamId = Camera2CameraInfo.from(camInfo).cameraId
                       thisCamId == cam2Infos[0].cameraId
                   }
               }.build()
       }
       else -> null
    }
}

// create a CameraSelector for the USB camera (or highest level internal camera)
val selector = selectExternalOrBestCamera(processCameraProvider)
processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

Wybierz kilka kamer jednocześnie

Od wersji CameraX 1.3 możesz też wybierać kilka kamer jednocześnie. Możesz na przykład połączyć przedni i tylny aparat, aby robić zdjęcia lub nagrywać filmy z obu perspektyw jednocześnie.

Gdy korzystasz z funkcji jednoczesnego korzystania z aparatów, urządzenie może jednocześnie używać 2 aparatu z różnymi obiektywami lub 2 tylnych aparatów. Poniższy blok kodu pokazuje, jak ustawić dwie kamery, gdy wywołującego funkcję bindToLifecycle i dowiedzieć się, jak uzyskać obydwa obiekty aparatu ze zwróconego ConcurrentCamera obiekt.

Kotlin

// Build ConcurrentCameraConfig
val primary = ConcurrentCamera.SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val secondary = ConcurrentCamera.SingleCameraConfig(
    secondaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val concurrentCamera = cameraProvider.bindToLifecycle(
    listOf(primary, secondary)
)

val primaryCamera = concurrentCamera.cameras[0]
val secondaryCamera = concurrentCamera.cameras[1]

Java

// Build ConcurrentCameraConfig
SingleCameraConfig primary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

SingleCameraConfig secondary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

ConcurrentCamera concurrentCamera =  
    mCameraProvider.bindToLifecycle(Arrays.asList(primary, secondary));

Camera primaryCamera = concurrentCamera.getCameras().get(0);
Camera secondaryCamera = concurrentCamera.getCameras().get(1);

Rozdzielczość aparatu

Możesz pozwolić aplikacji CameraX na ustawienie rozdzielczości obrazu na podstawie kombinacji możliwości urządzenia, obsługiwanego przez nie poziomu sprzętowego, przypadku użycia i podanego formatu obrazu. Możesz też ustawić określoną rozdzielczość docelową lub określony format obrazu w przypadkach, które obsługują tę konfigurację.

Automatyczna rozdzielczość

CameraX może automatycznie określić najlepsze ustawienia rozdzielczości na podstawie przypadków użycia opisanych w zasadzie cameraProcessProvider.bindToLifecycle(). Kiedykolwiek należy określić wszystkie przypadki użycia wymagane do jednoczesnego działania w jednym w pojedynczym wywołaniu bindToLifecycle(). AparatX określa rozdzielczość na podstawie zbioru przypadków użycia po uwzględnieniu na poziomie sprzętowym i uwzględniając różnice w zależności od urządzenia (gdy urządzenie przekracza lub nie spełnia konfiguracji strumienia ). Dzięki temu aplikacja będzie działać na wielu różnych urządzeniach minimalizując liczbę ścieżek kodu przeznaczonych na konkretne urządzenia.

Domyślny format obrazu w przypadku rejestrowania i analizowania obrazów to 4:3.

W przypadku niektórych zastosowań można skonfigurować proporcje, aby aplikacja mogła określić pożądane proporcje na podstawie projektu interfejsu użytkownika. Dane wyjściowe AparatuX są generowane w celu: format obrazu musi być ściśle dopasowany do formatu obsługiwanego przez urządzenie. Jeśli ta rozdzielczość nie jest obsługiwana, ale ta, która spełnia większość warunków zaznaczono. Aplikacja określa więc sposób wyświetlania kamery na a AparatX określa najlepsze ustawienia rozdzielczości, na różnych urządzeniach.

Aplikacja może na przykład wykonywać dowolne z tych czynności:

  • W przypadku zastosowania określ docelową rozdzielczość 4:3 lub 16:9
  • Określ niestandardową rozdzielczość, której CameraX spróbuje znaleźć najbliższe dopasowanie.
  • Określ format przycinania dla ImageCapture

Aparat X automatycznie wybiera rozdzielczość wewnętrznej powierzchni aparatu Camera2. Rozdzielczości znajdziesz w poniższej tabeli:

Przypadek użycia Rozdzielczość powierzchni wewnętrznej Rozdzielczość danych wyjściowych
Podgląd Format obrazu: rozdzielczość, która najlepiej pasuje do ustawień docelowych. Rozdzielczość wewnętrznej powierzchni. Podane metadane umożliwiają przycięcie widoku danych, i obrócić, aby dostosować go do docelowego współczynnika proporcji.
Rozdzielczość domyślna: najwyższa rozdzielczość podglądu lub najwyższa. preferowanej przez urządzenie rozdzielczości, która jest zgodna z formatem obrazu podglądu.
Maksymalna rozdzielczość: rozmiar podglądu, który jest najbardziej zbliżony do rozdzielczości ekranu urządzenia lub 1080p (1920 x 1080), w zależności od tego, która wartość jest mniejsza.
Analiza obrazu Format obrazu: rozdzielczość, która najlepiej pasuje do ustawień docelowych. Rozdzielczość wewnętrznej powierzchni.
Rozdzielczość domyślna: domyślne ustawienie rozdzielczości docelowej to 640x480. Dostosowywanie docelowej rozdzielczości i odpowiednich formatów obrazu pozwala uzyskać najlepszą możliwą rozdzielczość.
Maksymalna rozdzielczość: maksymalna rozdzielczość wyjściowa aparatu w formacie YUV_420_888, pobierana z StreamConfigurationMap.getOutputSizes(). Domyślna rozdzielczość docelowa to 640 x 480, więc jeśli chcesz uzyskać rozdzielczość większą niż 640 x 480, musisz użyć setTargetResolution() oraz setTargetAspectRatio() aby uzyskać najbardziej zbliżoną rozdzielczość.
Przechwytywanie obrazu Współczynnik proporcji: współczynnik proporcji najlepiej pasujący do ustawienia. Rozdzielczość wewnętrznej powierzchni.
Rozdzielczość domyślna: najwyższa dostępna rozdzielczość preferowanej przez urządzenie rozdzielczości, która odpowiada formatowi obrazu w Zrób zdjęcie.
Maksymalna rozdzielczość: maksymalna rozdzielczość wyjściowa aparatu w format JPEG. Użyj StreamConfigurationMap.getOutputSizes() .

Określ rozdzielczość

Podczas tworzenia przypadków użycia możesz ustawić określone rozdzielczości setTargetResolution(Size resolution) zgodnie z tym kodem przykład:

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

Java

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new Size(1280, 720))
    .build();

Nie możesz ustawić jednocześnie docelowego formatu obrazu i rozdzielczości docelowej tych kwestii. Powoduje to zgłoszenie żądania IllegalArgumentException podczas tworzenia konfiguracji. obiektu.

Wyraź rozdzielczość Size we współrzędnej po obróceniu obsługiwanych rozmiarów zgodnie z rotacją docelową. Na przykład urządzenie z naturalną orientacją pionową w naturalnym docelowym obrocie, które wymaga obrazu w orientacji pionowej, może podać wymiary 480 x 640, a to samo urządzenie, które jest obracane o 90 stopni i kierowane na orientację poziomą, może podać wymiary 640 x 480.

Rozdzielczość docelowa próbuje ustawić minimalny limit rozdzielczości obrazu. Rzeczywista rozdzielczość obrazu to najbliższa dostępna rozdzielczość o rozmiarze, który nie jest mniejszy niż docelowy, zgodnie z implementacją aparatu.

Jeśli jednak nie ma rozdzielczości równej lub większej od docelowej, zostanie wybrana najbliższa dostępna rozdzielczość mniejsza od docelowej. Rozdzielczości o tym samym formacie obrazu co podany Size mają wyższy priorytet niż rozdzielczości o innych formatach obrazu.

Aparat X stosuje najlepszą odpowiednią rozdzielczość na podstawie liczby żądań. Jeśli głównym wymaganiem jest zachowanie współczynnika proporcji, określ tylko setTargetAspectRatio, a CameraX określi odpowiednią rozdzielczość na podstawie urządzenia. jeśli aplikacja potrzebuje określonej rozdzielczości, aby utworzyć obraz. (np. małe lub średnie obrazy oparte na możliwości przetwarzania na urządzeniu), użyj funkcji setTargetResolution(Size resolution).

Jeśli Twoja aplikacja wymaga określonej rozdzielczości, w tabeli w createCaptureSession() sprawdź, jakie maksymalne rozdzielczości są obsługiwane na poszczególnych poziomach sprzętowych. Do Sprawdź rozdzielczości obsługiwane przez bieżące urządzenie, patrz StreamConfigurationMap.getOutputSizes(int)

Jeśli aplikacja działa na Androidzie 10 lub nowszym, możesz użyć isSessionConfigurationSupported(), aby zweryfikować konkretny SessionConfiguration.

Sterowanie wyjściem z kamery

Oprócz tego, że w zależności od potrzeb można skonfigurować wyjścia z kamery do indywidualnego użycia, CameraX implementuje również poniższe interfejsy, aby operacje kamery typowe dla wszystkich powiązanych przypadków użycia:

  • CameraControl pozwala konfigurować typowe funkcje aparatu.
  • CameraInfo umożliwia wysyłanie zapytań dotyczących stanu tych typowych funkcji aparatu.

Następujące funkcje aparatu są obsługiwane przez funkcję CameraControl:

  • Zoom
  • Latarka
  • ostrość i wymiarowanie (dotknij, aby ustawić ostrość);
  • Kompensacja ekspozycji

Pobieranie wystąpień interfejsów CameraControl i CameraInfo

Pobieraj wystąpienia CameraControlCameraInfo za pomocą obiektu Camera zwracanego przez ProcessCameraProvider.bindToLifecycle(). Przykładowy kod:

Kotlin

val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
val cameraControl = camera.cameraControl
// For querying information and states.
val cameraInfo = camera.cameraInfo

Java

Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
CameraControl cameraControl = camera.getCameraControl()
// For querying information and states.
CameraInfo cameraInfo = camera.getCameraInfo()

Możesz na przykład przesłać powiększenie i inne operacje CameraControl po Dzwonię pod numer bindToLifecycle(). Po zatrzymaniu lub zniszczeniu działania używanego do powiązania instancja kamery, CameraControl nie może już wykonywać operacji i zwraca błąd ListenableFuture.

Zoom

Aplikacja CameraControl udostępnia 2 metody zmiany poziomu powiększenia:

  • setZoomRatio()ustawia powiększenie według współczynnika powiększenia.

    Współczynnik musi się mieścić w zakresie od CameraInfo.getZoomState().getValue().getMinZoomRatio() i CameraInfo.getZoomState().getValue().getMaxZoomRatio() W przeciwnym razie funkcja zwraca wartość ListenableFuture.

  • setLinearZoom() ustawia bieżące powiększenie na wartość powiększenia liniowego z zakresu od 0 do 1,0.

    Zaletą powiększenia liniowego jest to, że przekształca on pole widzenia ze zmianami powiększenia. Dzięki temu idealnie nadaje się do użycia Slider wyświetlenie.

CameraInfo.getZoomState() zwraca LiveData bieżącego stanu powiększenia. Wartość zmienia się po zainicjowaniu kamery lub ustawieniu poziomu powiększenia za pomocą setZoomRatio() lub setLinearZoom(). Wywołanie dowolnej z tych metod ustawia wsteczne wartości ZoomState.getZoomRatio() oraz ZoomState.getLinearZoom() Jest to przydatne, jeśli chcesz wyświetlić tekst współczynnika powiększenia obok suwaka. Wystarczy, że będziesz obserwować ZoomState LiveData, aby aktualizować obie wersje bez konieczności wykonywania konwersji.

ListenableFuture zwracany przez oba interfejsy API umożliwia aplikacjom otrzymywanie powiadomień po wykonaniu powtarzającego się żądania z określoną wartością powiększenia. Jeśli dodatkowo ustawisz nową wartość powiększenia, gdy poprzednia operacja jest nadal wykonywana, ListenableFuture poprzedniej operacji powiększenia zakończy się niepowodzeniem.

Latarka

CameraControl.enableTorch(boolean) włącza lub wyłącza latarkę.

CameraInfo.getTorchState() może służyć do zapytania o obecny stan latarki. Możesz sprawdzić zwrócona wartość autor: CameraInfo.hasFlashUnit() aby ustalić, czy jest dostępna latarka. Jeśli nie, dzwonię CameraControl.enableTorch(boolean) sprawia, że zwrócona wartość ListenableFuture jest natychmiast z nieudanym wynikiem i ustawia stan uchwytu na TorchState.OFF

Gdy latarka jest włączona, pozostaje włączona podczas robienia zdjęć i filmowania niezależnie od ustawienia trybu lampy błyskowej. flashMode in ImageCapture działa tylko wtedy, gdy latarka jest wyłączona.

Ostrość i pomiar

CameraControl.startFocusAndMetering() uruchamia autofokus i automatyczny pomiar ekspozycji, ustawiając regiony pomiaru AF/AE/AWB na podstawie podanego działania FocusMeteringAction. Jest to często używane do implementacji funkcji „dotknij, aby ustawić ostrość” w wielu aplikacjach do obsługi aparatu.

MeteringPoint

Najpierw utwórz MeteringPoint za pomocą MeteringPointFactory.createPoint(float x, float y, float size). MeteringPoint oznacza pojedynczy punkt kamery Surface Jest on przechowywany w postaci unormowanej, aby można było łatwo przekształcić go w współrzędne czujnika na potrzeby określania regionów AF/AE/AWB.

Rozmiar MeteringPoint mieści się w zakresie od 0 do 1, a rozmiar domyślny to 0,15f. Podczas wywoływania metody MeteringPointFactory.createPoint(float x, float y, float size) CameraX tworzy prostokątny obszar z środkiem w miejscu (x, y) dla podanego size.

Ten kod pokazuje, jak utworzyć element MeteringPoint:

Kotlin

// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener((view, motionEvent) ->  {
val meteringPoint = previewView.meteringPointFactory
    .createPoint(motionEvent.x, motionEvent.y)

}

// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for
// preview. Please note that if the preview is scaled or cropped in the View,
// it’s the application's responsibility to transform the coordinates properly
// so that the width and height of this factory represents the full Preview FOV.
// And the (x,y) passed to create MeteringPoint might need to be adjusted with
// the offsets.
val meteringPointFactory = DisplayOrientedMeteringPointFactory(
     surfaceView.display,
     camera.cameraInfo,
     surfaceView.width,
     surfaceView.height
)

// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
     imageWidth,
     imageHeight,
     imageAnalysis)

startFocusAndMetering i FocusMeteringAction

Do wywołania startFocusAndMetering() aplikacje muszą utworzyć FocusMeteringAction, składający się z co najmniej jednego elementu MeteringPoints z opcjonalnym trybem pomiaru kombinacje z FLAG_AF, FLAG_AE, FLAG_AWB. jest to kod ilustrujący to użycie:

Kotlin

val meteringPoint1 = meteringPointFactory.createPoint(x1, x1)
val meteringPoint2 = meteringPointFactory.createPoint(x2, y2)
val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB
      // Optionally add meteringPoint2 for AF/AE.
      .addPoint(meteringPoint2, FLAG_AF | FLAG_AE)
      // The action is canceled in 3 seconds (if not set, default is 5s).
      .setAutoCancelDuration(3, TimeUnit.SECONDS)
      .build()

val result = cameraControl.startFocusAndMetering(action)
// Adds listener to the ListenableFuture if you need to know the focusMetering result.
result.addListener({
   // result.get().isFocusSuccessful returns if the auto focus is successful or not.
}, ContextCompat.getMainExecutor(this)

Jak widać w poprzednim kodzie, startFocusAndMetering() przyjmuje FocusMeteringAction składający się z jednego MeteringPoint dla regionów pomiaru AF/AE/AWB i drugiego punktu pomiaru MeteringPoint tylko dla AF i AE.

Wewnętrznie CameraX konwertuje go na Camera2MeteringRectanglesi ustawia odpowiednie parametryCONTROL_AF_REGIONSCONTROL_AE_REGIONSCONTROL_AWB_REGIONS w żądaniu rejestrowania.

Ponieważ nie każde urządzenie obsługuje AF/AE/AWB i wielu regionów, AparatX FocusMeteringAction. CameraX używa maksymalnej liczby obsługiwanych punktów pomiarowych w kolejności dodawania. Wszystkie Punkty MeteringPoint dodane po maksymalnej liczbie punktów są ignorowane. Jeśli na przykład plik FocusMeteringAction jest wyposażony w 3 punkty MeteringPoints na platformie obsługującej tylko 2, używane są tylko 2 pierwsze punkty MeteringPoint. Ostatni parametr MeteringPoint jest ignorowany przez CameraX.

Kompensacja ekspozycji

Kompensacja ekspozycji jest przydatna, gdy aplikacje muszą precyzyjnie dostosować ekspozycję (EV) poza wynikami automatycznej ekspozycji (AE). Wartości kompensacji ekspozycji są łączone w ten sposób, aby określić ekspozycję wymaganą w obecnych warunkach obrazu:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX udostępnia funkcję Camera.CameraControl.setExposureCompensationIndex()do ustawiania kompensacji ekspozycji jako wartości indeksu.

Wartości dodatnie powodują jaśniejsze obrazy, a ujemne – ciemniejsze. Aplikacje mogą wysyłać zapytania dotyczące obsługiwanego zakresu za pomocą CameraInfo.ExposureState.exposureCompensationRange(), jak opisano w następnej sekcji. Jeśli wartość jest obsługiwana, zwrócona wartość ListenableFuture jest realizowana po włączeniu tej wartości w żądaniu rejestrowania. Jeśli określony indeks wykracza poza obsługiwany zakres, funkcja setExposureCompensationIndex() powoduje, że zwrócona wartość ListenableFuture jest realizowana natychmiast z wynikiem niepowodzenia.

CameraX przechowuje tylko najnowsze oczekujące żądanie setExposureCompensationIndex(). Wywołanie tej funkcji wiele razy przed wykonaniem poprzedniego żądania powoduje jego anulowanie.

Poniższy fragment kodu ustawia indeks kompensacji ekspozycji i rejestruje zwrotny po zrealizowaniu żądania zmiany ekspozycji:

Kotlin

camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
   .addListener({
      // Get the current exposure compensation index, it might be
      // different from the asked value in case this request was
      // canceled by a newer setting request.
      val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex
      
   }, mainExecutor)
  • Camera.CameraInfo.getExposureState() pobiera bieżącą ExposureState w tym:

    • Obsługa kontroli kompensacji ekspozycji.
    • Bieżący indeks kompensacji ekspozycji.
    • Zakres indeksu kompensacji ekspozycji.
    • Krok kompensacji ekspozycji używany w obliczeniach wartości kompensacji ekspozycji.

Na przykład ten kod inicjuje ustawienia ekspozycji SeekBar z aktualnymi wartościami ExposureState:

Kotlin

val exposureState = camera.cameraInfo.exposureState
binding.seekBar.apply {
   isEnabled = exposureState.isExposureCompensationSupported
   max = exposureState.exposureCompensationRange.upper
   min = exposureState.exposureCompensationRange.lower
   progress = exposureState.exposureCompensationIndex
}

Dodatkowe materiały

Więcej informacji o CameraX znajdziesz w tych dodatkowych materiałach.

Ćwiczenia z programowania

  • Pierwsze kroki z CameraX
  • Przykładowy kod

  • Przykładowe aplikacje CameraX
  • Społeczność programistów

    Grupa dyskusyjna dotyczące aparatu Android CameraX