Konfigurujesz każdy przypadek użycia AparatuX, aby kontrolować różne aspekty jego działań.
Na przykład w przypadku przechwytywania obrazu możesz ustawić docelowy format obrazu oraz tryb lampy błyskowej. Przykładowy kod:
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 do dynamicznego zmieniania ustawień po utworzeniu przypadku użycia. Informacje o konfiguracji związanej z poszczególnymi zastosowaniami znajdziesz w artykułach Wdrażanie podglądu, Analiza obrazów i Przechwytywanie obrazu.
CameraXConfig
W celu uproszczenia CameraX zawiera 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ż wykonawcę aplikacji do CameraX za pomocą interfejsu
setCameraExecutor()
. - Zastąp domyślny moduł obsługi harmonogramu elementem
setSchedulerHandler()
. - Zmień poziom logowania za pomocą polecenia
setMinimumLoggingLevel()
.
Model użycia
Poniżej znajdziesz opis korzystania z CameraXConfig
:
- Utwórz obiekt
CameraXConfig
z własnymi konfiguracjami. - Zaimplementuj interfejs
CameraXConfig.Provider
wApplication
i zwróć obiektCameraXConfig
wgetCameraXConfig()
. - Dodaj klasę
Application
do plikuAndroidManifest.xml
zgodnie z opisem tutaj.
Na przykład ten przykładowy kod ogranicza logowanie w aplikacji CameraX tylko do komunikatów 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 musi znać konfigurację CameraX po jej skonfigurowaniu.
Ogranicznik aparatu
Podczas pierwszego wywołania funkcji
ProcessCameraProvider.getInstance()
CameraX wylicza i wysyła zapytania o właściwości kamer dostępnych na urządzeniu. 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 Twoja aplikacja korzysta tylko z określonych kamer na urządzeniu, na przykład domyślnej przedniej kamery, możesz skonfigurować CameraX tak, aby ignorował inne kamery. Dzięki temu możesz skrócić czas uruchamiania kamer, których używa aplikacja.
Jeśli CameraSelector
przekazane do CameraXConfig.Builder.setAvailableCamerasLimiter()
odfiltrowuje kamerę, CameraX zachowuje się tak, jakby ta kamera 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ę CameraX, wymaga blokowania komunikacji międzyprocesowej (IPC) z urządzeniami, która może czasami trwać setki 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, aby to działanie było przezroczyste. Niektóre aplikacje wymagają jednak
ścisłej kontroli wątków. CameraXConfig
pozwala aplikacji ustawić wątki w tle, które są używane przez CameraXConfig.Builder.setCameraExecutor()
i CameraXConfig.Builder.setSchedulerHandler()
.
Wykonawca DIME
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. Do wykonywania tych zadań CameraX przydziela wewnętrzny Executor
i zarządza nim.
Jeśli jednak Twoja aplikacja wymaga ściślejszej kontroli nad wątkami, użyj CameraXConfig.Builder.setCameraExecutor()
.
Moduł obsługi algorytmu szeregowania
Moduł obsługi algorytmu szeregowania służy do planowania zadań wewnętrznych w stałych odstępach czasu, na przykład ponawianie próby otwarcia kamery, gdy jest ona niedostępna. Ten moduł nie wykonuje zadań, tylko przekazuje je do modułu wykonawczego kamery. Czasami jest też używany na starszych platformach interfejsu API, które wymagają korzystania z Handler
w przypadku wywołań zwrotnych. W takich przypadkach wywołania zwrotne są nadal wysyłane bezpośrednio do wykonawcy kamery. CameraX przydziela do wykonywania tych zadań wewnętrzny HandlerThread
i nim zarządza, ale możesz zastąpić go ustawieniem CameraXConfig.Builder.setSchedulerHandler()
.
Logowanie
Logowanie w CameraX umożliwia aplikacjom filtrowanie komunikatów logcat, ponieważ dobrze jest unikać tych 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 specyficzne dla urządzenia, na którym działa aplikacja. 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 CameraX musi iść na kompromisy w zakresie rozdzielczości i formatów obrazu ze względu na możliwości urządzenia. 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 przy określonych współczynnikach proporcji.
- Urządzenie preferuje kodowanie JPEG lub wideo „nearest mod16”. Więcej informacji znajdziesz w sekcji
SCALER_STREAM_CONFIGURATION_MAP
.
Chociaż AparatX tworzy sesję i zarządza nią, zawsze sprawdzaj rozmiary zwracanych obrazów w danych wyjściowych przypadku użycia w kodzie i odpowiednio dostosowuj te rozmiary.
Obrót
Domyślnie podczas tworzenia przykładu zastosowania obrót kamery jest ustawiony tak, aby pasował do obrotu domyślnego 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 przypadku użycia lub dynamicznie po ich utworzeniu.
Aplikacja może ustawić docelową orientację za pomocą ustawień konfiguracji. Dzięki temu może aktualizować ustawienia rotacji za pomocą metod z interfejsów API przypadku użycia (takich jak ImageAnalysis.setTargetRotation()
), nawet gdy cykl życia jest uruchomiony. Możesz z niego korzystać, gdy aplikacja jest zablokowana w trybie pionowym – dzięki temu po obróceniu nie następuje ponowna konfiguracja – ale w przypadku zastosowania zdjęcia lub analizy trzeba pamiętać o bieżącym obróceniu 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 zawierają informacje o obrocie, dzięki czemu aplikacje galerii mogą wyświetlać obraz w prawidłowej orientacji 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 rotacji danych w każdym przypadku użycia następuje bezpośrednia rotacja danych obrazu lub dostarczanie metadanych obrotu konsumentom danych obrazów nieobróconych.
- Podgląd: dane wyjściowe metadanych są dostarczane w taki sposób, aby rotacja docelowej rozdzielczości była znana za pomocą
Preview.getTargetRotation()
. - Analiza obrazu: podawane są metadane, dzięki czemu współrzędne bufora obrazu są znane względem współrzędnych wyświetlania.
- ImageCapture: metadane obrazu Exif, bufor lub zarówno bufor, jak i metadane są zmieniane, by uwzględnić ustawienie rotacji. Zmieniona wartość zależy od implementacji HAL.
Ramka kadrowania
Domyślnie prostokąt przycięcia jest równy pełnemu prostokątowi bufora. Możesz go dostosować za pomocą pól 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 prostokąt bufora widoczny 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. Zwykle, aby uzyskać efekt WYSIWYG, trzeba skonfigurować widoczny obszar na podstawie przypadku użycia podglądu. Prosty sposób na uzyskanie widocznego obszaru to użycie PreviewView
.
Poniższe fragmenty kodu pokazują, jak pobrać 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 informacji o stosowaniu informacji o przekształceniu znajdziesz w sekcji transform
output.
Wybór kamery
CameraX automatycznie wybiera najlepszy aparat dopasowany do wymagań i zastosowania aplikacji. Jeśli chcesz użyć innego urządzenia niż wybrane przez nas, masz kilka opcji:
- Zażądaj domyślnego przedniego aparatu za pomocą
CameraSelector.DEFAULT_FRONT_CAMERA
. - Zażądaj domyślnego tylnego aparatu za pomocą usługi
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ć element CameraSelector
, aby 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)
Wybieranie kilku kamer jednocześnie
Począwszy od CameraX 1.3, można również wybrać wiele 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 używasz funkcji Równoczesny aparat, urządzenie może jednocześnie obsługiwać 2 aparaty z różnymi obiektywami lub 2 tylne aparaty jednocześnie. Ten blok kodu pokazuje, jak ustawić 2 kamery przy wywołaniu funkcji bindToLifecycle
i jak pobierać obiekty Camera ze zwróconego obiektu ConcurrentCamera
.
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 zezwolić aplikacji CameraX na ustawianie 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 współczynnik proporcji w przypadkach, które obsługują tę konfigurację.
Automatyczne rozwiązanie
CameraX może automatycznie określać najlepsze ustawienia rozdzielczości na podstawie przypadków użycia określonych w cameraProcessProvider.bindToLifecycle()
. W miarę możliwości określ wszystkie przypadki użycia, które wymagają jednoczesnego działania w ramach jednej sesji, w jednym wywołaniu bindToLifecycle()
. CameraX określa rozdzielczości na podstawie zestawu przypadków użycia związanych z uwzględnianiem obsługiwanego poziomu sprzętu urządzenia oraz uwzględniając zmienność związaną z danym urządzeniem (gdy urządzenie przekracza lub nie spełnia dostępnych konfiguracji strumienia).
Celem jest umożliwienie uruchamiania aplikacji na wielu urządzeniach przy jednoczesnym minimalizowaniu ścieżek kodu związanych z konkretnym urządzeniem.
Domyślny format obrazu w przypadku rejestrowania i analizowania obrazów to 4:3.
Przypadki użycia mają konfigurowalny współczynnik proporcji, co pozwala aplikacji określić odpowiedni format na podstawie projektu UI. Wyjście CameraX jest tworzone w taki sposób, aby jak najdokładniej dopasować format obrazu do wymaganego, w miarę możliwości urządzenia. Jeśli nie ma obsługiwanego rozwiązania dopasowania do wyrażenia, wybierane jest to, które spełnia najwięcej warunków. Aplikacja określa wygląd aparatu w aplikacji, a CameraX określa najlepsze ustawienia rozdzielczości aparatu, aby spełnić te wymagania na różnych urządzeniach.
Aplikacja może na przykład wykonywać dowolne z tych czynności:
- Określ docelową rozdzielczość 4:3 lub 16:9 w przypadku konkretnego przypadku użycia
- Określ niestandardową rozdzielczość, której CameraX spróbuje znaleźć najbliższe dopasowanie.
- Określ format przycinania dla
ImageCapture
CameraX automatycznie wybiera wewnętrzne rozdzielczości powierzchni Camera2. W tabeli poniżej znajdziesz te rozdzielczości:
Przypadek użycia | Rozdzielczość wewnętrznej powierzchni | Rozdzielczość danych wyjściowych |
---|---|---|
Podgląd | Współczynnik proporcji: rozdzielczość, która najlepiej pasuje do ustawienia docelowego. | Rozdzielczość wewnętrznej powierzchni. Metadane umożliwiają kadrowanie, skalowanie i obracanie widoku w celu dostosowania do docelowego współczynnika proporcji. |
Rozdzielczość domyślna: najwyższa rozdzielczość podglądu lub najwyższa rozdzielczość preferowana na urządzeniu, która odpowiada współczynnikowi proporcji 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 ustawienia docelowego. | Rozdzielczość wewnętrznej powierzchni. |
Domyślna rozdzielczość: domyślne ustawienie docelowej rozdzielczości to 640 x 480. Dostosowanie zarówno docelowej rozdzielczości, jak i odpowiedniego współczynnika proporcji skutkuje uzyskaniem najlepszej obsługiwanej rozdzielczości. | ||
Maksymalna rozdzielczość: maksymalna rozdzielczość wyjściowa aparatu w formacie YUV_420_888, pobierana z StreamConfigurationMap.getOutputSizes() .
Domyślnie docelową rozdzielczość ustawia się na 640 x 480, więc jeśli chcesz użyć rozdzielczości większej niż 640 x 480, musisz użyć opcji setTargetResolution() i setTargetAspectRatio() , aby wybrać najbliższą obsługiwaną rozdzielczość.
|
||
Robienie zdjęć | Format obrazu: format obrazu, który najlepiej pasuje do ustawienia. | Rozdzielczość wewnętrznej powierzchni. |
Domyślna rozdzielczość: najwyższa dostępna rozdzielczość lub najwyższa preferowana rozdzielczość urządzenia, która odpowiada formatowi obrazu ImageCapture. | ||
Maksymalna rozdzielczość: maksymalna rozdzielczość wyjściowa aparatu w formacie JPEG. Aby pobrać te dane, użyj kodu
StreamConfigurationMap.getOutputSizes() .
|
Określ rozdzielczość
Podczas tworzenia przypadków użycia możesz ustawić określone rozdzielczości za pomocą metody setTargetResolution(Size resolution)
, jak pokazano w tym przykładzie kodu:
Kotlin
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .build()
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() .setTargetResolution(new Size(1280, 720)) .build();
Nie można ustawić zarówno docelowego formatu obrazu, jak i docelowej rozdzielczości w ramach tego samego przypadku użycia. Spowoduje to wyjątek IllegalArgumentException
podczas tworzenia obiektu konfiguracji.
Wyraź rozdzielczość Size
w układzie współrzędnych po obróceniu obsługiwanych rozmiarów zgodnie z docelowym obrotem. 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, która jest równa lub większa od docelowej, wybierana jest najbliższa dostępna rozdzielczość mniejsza od docelowej. Rozdzielczości o tym samym współczynniku proporcji w polu Size
mają wyższy priorytet niż rozdzielczość o różnych współczynnikach proporcji.
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 główną potrzebą aplikacji jest określenie rozdzielczości w celu wydajniejszego przetwarzania obrazu (np. małe lub średnie obrazy w zależności od możliwości przetwarzania na urządzeniu), użyj funkcji setTargetResolution(Size resolution)
.
Jeśli Twoja aplikacja wymaga dokładnej rozdzielczości, zapoznaj się z tabelą w sekcji createCaptureSession()
, aby sprawdzić, jakie maksymalne rozdzielczości są obsługiwane przez poszczególne poziomy sprzętowe. Aby sprawdzić, jakie rozdzielczości obsługuje Twoje urządzenie, zobacz StreamConfigurationMap.getOutputSizes(int)
.
Jeśli aplikacja działa na Androidzie 10 lub nowszym, możesz użyć isSessionConfigurationSupported()
do weryfikacji konkretnego SessionConfiguration
.
Sterowanie wyjściem z kamery
Oprócz możliwości konfigurowania wyjścia z kamery w razie potrzeby w każdym indywidualnym przypadku użycia CameraX implementuje też te interfejsy, aby obsługiwać operacje na kamerze wspólne 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.
Te funkcje aparatu są obsługiwane przez CameraControl:
- Zoom
- Latarka
- Ostrość i pomiar (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()
Po wywołaniu funkcji bindToLifecycle()
możesz na przykład przesłać operacje powiększania i inne operacje CameraControl
. Po zatrzymaniu lub usunięciu aktywności używanej do powiązania instancji 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 mieścić się w zakresie od
CameraInfo.getZoomState().getValue().getMinZoomRatio()
doCameraInfo.getZoomState().getValue().getMaxZoomRatio()
. W przeciwnym razie funkcja zwraca wartośćListenableFuture
.setLinearZoom()
ustawia bieżące powiększenie z wartością liniową w zakresie od 0 do 1,0.Zaletą liniowego powiększenia jest to, że pole widzenia (FOV) zmienia się wraz ze zmianami powiększenia. Dzięki temu doskonale nadaje się do korzystania z widoku
Slider
.
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 powoduje ustawienie wartości obsługujących ZoomState.getZoomRatio()
i ZoomState.getLinearZoom()
.
Jest to przydatne, jeśli chcesz wyświetlić tekst współczynnika powiększenia obok suwaka.
Wystarczy, że obserwujesz ZoomState
LiveData
, aby aktualizować oba te parametry bez konieczności przeprowadzania 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 zostanie natychmiast przerwana.
Latarka
CameraControl.enableTorch(boolean)
włącza i wyłącza latarkę.
CameraInfo.getTorchState()
może służyć do wysyłania zapytań o obecny stan latarki. Aby sprawdzić, czy latarka jest dostępna, możesz sprawdzić zwróconą wartość:
CameraInfo.hasFlashUnit()
. W przeciwnym razie wywołanie CameraControl.enableTorch(boolean)
spowoduje natychmiastowe zakończenie działania funkcji ListenableFuture
z wynikiem niepowodzenia i ustawienie stanu latarki na TorchState.OFF
.
Gdy latarka jest włączona, pozostaje włączona podczas robienia zdjęć i nagrywania filmów,
niezależnie od ustawienia flashMode. flashMode
w ImageCapture
działa tylko wtedy, gdy latarka jest wyłączona.
Ostrość i pomiar
CameraControl.startFocusAndMetering()
aktywuje autofokus i pomiar ekspozycji, ustawiając regiony pomiaru AF/AE/AWB na podstawie danego obiektu FocusMeteringAction. Jest to często wykorzystywane 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
reprezentuje pojedynczy punkt na kamerze
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.
Wartość MeteringPoint
ma zakres od 0 do 1, a domyślnie wynosi 0,15. 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
Aby wywołać
startFocusAndMetering()
,
aplikacje muszą tworzyć
FocusMeteringAction
,
które składają się z jednego lub większej liczby MeteringPoints
z opcjonalnymi kombinacjami trybu pomiaru z FLAG_AF
,
FLAG_AE
,
FLAG_AWB
. Oto przykładowy kod:
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 innego 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.
Nie wszystkie urządzenia obsługują AF/AE/AWB i wiele regionów, dlatego CameraX wykonuje FocusMeteringAction
w najlepszy możliwy sposób. CameraX używa maksymalnej liczby obsługiwanych punktów pomiarowych w kolejności dodawania punktów. Wszystkie pomiary dodane po przekroczeniu maksymalnej liczby są ignorowane. Jeśli na przykład FocusMeteringAction
jest dostarczany z 3 punktami pomiarowymi na platformie obsługującej tylko 2 punkty pomiarowe, używane są tylko 2 pierwsze punkty pomiarowe. Ostatni element MeteringPoint
jest ignorowany przez aparat X.
Kompensacja ekspozycji
Kompensacja ekspozycji jest przydatna, gdy aplikacje muszą dostosować wartości ekspozycji (EV) poza wynikiem wyjściowym 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 indeksu dodatnie rozjaśniają obraz, a ujemne – go przyciemniają. Aplikacje mogą wysyłać zapytania o obsługiwany zakres 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 zachowuje tylko ostatnie oczekujące żądanie setExposureCompensationIndex()
i wielokrotnie wywołuje tę funkcję, zanim poprzednie żądanie zostanie anulowane.
Ten fragment kodu ustawia indeks kompensacji ekspozycji i rejestruje wywołanie zwrotne 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 aparacie AparatX znajdziesz w tych dodatkowych materiałach.
Ćwiczenia z programowania
Przykładowy kod
Społeczność programistów
Grupa dyskusyjna CameraX na Androida