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ów i Zapisywanie 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:
- Zoptymalizuj czas oczekiwania na uruchomienie dzięki
setAvailableCameraLimiter()
- Przekaż wykonawcy aplikacji CameraX kod
setCameraExecutor()
- Zastąp domyślny moduł obsługi algorytmu szeregowania
setSchedulerHandler()
- Zmień poziom rejestrowania za pomocą
setMinimumLoggingLevel()
.
Model wykorzystania
Poniżej znajdziesz opis procedury korzystania z CameraXConfig
:
- Utwórz obiekt
CameraXConfig
z niestandardowymi konfiguracjami. - Zaimplementuj tag
CameraXConfig.Provider
wApplication
, zwraca obiektCameraXConfig
wgetCameraXConfig()
. - Dodaj zajęcia
Application
do plikuAndroidManifest.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 ImageAnalysis
i ImageCapture
, 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:
- Zażądaj domyślnego przedniego aparatu, używając
CameraSelector.DEFAULT_FRONT_CAMERA
- Poproś o domyślny tylny aparat, klikając
CameraSelector.DEFAULT_BACK_CAMERA
. - Filtruj listę dostępnych urządzeń według ich
CameraCharacteristics
CameraSelector.Builder.addCameraFilter()
.
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 CameraControl
i CameraInfo
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()
iCameraInfo.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 Camera2MeteringRectangles
i ustawia odpowiednie parametryCONTROL_AF_REGIONS
CONTROL_AE_REGIONS
CONTROL_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
Przykładowy kod
Społeczność programistów
Grupa dyskusyjna dotyczące aparatu Android CameraX