[v7,3/3] libcamera: android: Add face detection control support
diff mbox series

Message ID 20240924110353.2025397-4-chenghaoyang@google.com
State Superseded
Headers show
Series
  • Add Face Detection Controls
Related show

Commit Message

Cheng-Hao Yang Sept. 24, 2024, 11:02 a.m. UTC
From: Harvey Yang <chenghaoyang@chromium.org>

Allow Android HAL adapter to pass the face detection metadata control to
the pipeline and also send face detection metadata to the camera client
if the pipeline generates it.

Signed-off-by: Yudhistira Erlandinata <yerlandinata@chromium.org>
Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
---
 src/android/camera_capabilities.cpp | 45 ++++++++++++++++--
 src/android/camera_device.cpp       | 73 ++++++++++++++++++++++++++++-
 2 files changed, 112 insertions(+), 6 deletions(-)

Comments

Jacopo Mondi Sept. 24, 2024, 1:02 p.m. UTC | #1
Hi Harvey

On Tue, Sep 24, 2024 at 11:02:46AM GMT, Harvey Yang wrote:
> From: Harvey Yang <chenghaoyang@chromium.org>
>
> Allow Android HAL adapter to pass the face detection metadata control to
> the pipeline and also send face detection metadata to the camera client
> if the pipeline generates it.
>
> Signed-off-by: Yudhistira Erlandinata <yerlandinata@chromium.org>
> Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> ---
>  src/android/camera_capabilities.cpp | 45 ++++++++++++++++--
>  src/android/camera_device.cpp       | 73 ++++++++++++++++++++++++++++-
>  2 files changed, 112 insertions(+), 6 deletions(-)
>
> diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
> index 71043e12..d52d58ed 100644
> --- a/src/android/camera_capabilities.cpp
> +++ b/src/android/camera_capabilities.cpp
> @@ -7,6 +7,8 @@
>
>  #include "camera_capabilities.h"
>
> +#include <stdint.h>
> +
>  #include <algorithm>
>  #include <array>
>  #include <cmath>
> @@ -1176,11 +1178,46 @@ int CameraCapabilities::initializeStaticMetadata()
>  				  maxFrameDuration_);
>
>  	/* Statistics static metadata. */
> -	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> -	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> -				  faceDetectMode);
> -
>  	int32_t maxFaceCount = 0;
> +	auto iter = camera_->controls().find(controls::android::FaceDetectMode.id());
> +	if (iter != camera_->controls().end()) {
> +		const ControlInfo &faceDetectCtrlInfo = iter->second;
> +		std::vector<uint8_t> faceDetectModes;
> +		bool hasFaceDetection = false;
> +
> +		for (const auto &value : faceDetectCtrlInfo.values()) {
> +			uint8_t mode = value.get<uint8_t>();
> +			uint8_t androidMode = 0;
> +
> +			switch (mode) {
> +			case controls::android::FaceDetectModeOff:
> +				androidMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> +				break;
> +			case controls::android::FaceDetectModeSimple:
> +				androidMode = ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE;
> +				hasFaceDetection = true;
> +				break;
> +			default:
> +				LOG(HAL, Fatal) << "Received invalid face detect mode: " << mode;
> +			}
> +			faceDetectModes.push_back(androidMode);
> +		}
> +		if (hasFaceDetection) {
> +			/*
> +			 * \todo Create new libcamera controls to query max
> +			 * possible faces detected.
> +			 */
> +			maxFaceCount = 10;
> +			staticMetadata_->addEntry(
> +				ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> +				faceDetectModes.data(), faceDetectModes.size());
> +		}
> +	} else {
> +		uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> +		staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> +					  faceDetectMode);
> +	}
> +
>  	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
>  				  maxFaceCount);
>
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index 493f66e7..1982509d 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -15,6 +15,7 @@
>  #include <vector>
>
>  #include <libcamera/base/log.h>
> +#include <libcamera/base/span.h>
>  #include <libcamera/base/unique_fd.h>
>  #include <libcamera/base/utils.h>
>
> @@ -22,6 +23,7 @@
>  #include <libcamera/controls.h>
>  #include <libcamera/fence.h>
>  #include <libcamera/formats.h>
> +#include <libcamera/geometry.h>
>  #include <libcamera/property_ids.h>
>
>  #include "system/graphics.h"
> @@ -813,6 +815,11 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)
>  		controls.set(controls::ScalerCrop, cropRegion);
>  	}
>
> +	if (settings.getEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, &entry)) {
> +		const uint8_t *data = entry.data.u8;
> +		controls.set(controls::android::FaceDetectMode, data[0]);
> +	}
> +
>  	if (settings.getEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, &entry)) {
>  		const int32_t data = *entry.data.i32;
>  		int32_t testPatternMode = controls::draft::TestPatternModeOff;
> @@ -1540,8 +1547,9 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
>  	value32 = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
>  	resultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, value32);
>
> -	value = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> -	resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, value);
> +	settings.getEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, &entry);
> +	resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> +				 entry.data.u8, 1);
>
>  	value = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
>  	resultMetadata->addEntry(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> @@ -1580,6 +1588,67 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
>  		resultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,
>  					 *frameDuration * 1000);
>
> +	const auto &faceDetectRectangles =
> +		metadata.get(controls::android::FaceDetectFaceRectangles);
> +	if (faceDetectRectangles) {
> +		const Span<const Rectangle> rectangles = *faceDetectRectangles;

You can spare all these temporary Span<>, but the rest of the code
goes through temporaries too

Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Thanks
  j


> +		std::vector<int32_t> flatRectangles;
> +		for (const Rectangle &rect : rectangles) {
> +			flatRectangles.push_back(rect.x);
> +			flatRectangles.push_back(rect.y);
> +			flatRectangles.push_back(rect.x + rect.width);
> +			flatRectangles.push_back(rect.y + rect.height);
> +		}
> +		resultMetadata->addEntry(
> +			ANDROID_STATISTICS_FACE_RECTANGLES, flatRectangles);
> +	}
> +
> +	const auto &faceDetectFaceScores =
> +		metadata.get(controls::android::FaceDetectFaceScores);
> +	if (faceDetectRectangles && faceDetectFaceScores) {
> +		const Span<const uint8_t> &scores = *faceDetectFaceScores;
> +		if (scores.size() != faceDetectRectangles->size()) {
> +			LOG(HAL, Error) << "Pipeline returned wrong number of face scores; "
> +					<< "Expected: "
> +					<< faceDetectRectangles->size()
> +					<< ", got: " << scores.size();
> +		}
> +		resultMetadata->addEntry(ANDROID_STATISTICS_FACE_SCORES, scores);
> +	}
> +
> +	const auto &faceDetectFaceLandmarks =
> +		metadata.get(controls::android::FaceDetectFaceLandmarks);
> +	if (faceDetectRectangles && faceDetectFaceLandmarks) {
> +		const auto &landmarks = *faceDetectFaceLandmarks;
> +		size_t expectedLandmarks = faceDetectRectangles->size() * 3;
> +		if (landmarks.size() != expectedLandmarks) {
> +			LOG(HAL, Error) << "Pipeline returned wrong number of face landmarks; "
> +					<< "Expected: " << expectedLandmarks
> +					<< ", got: " << landmarks.size();
> +		}
> +
> +		std::vector<int32_t> androidLandmarks;
> +		for (const auto &landmark : landmarks) {
> +			androidLandmarks.push_back(landmark.x);
> +			androidLandmarks.push_back(landmark.y);
> +		}
> +		resultMetadata->addEntry(
> +			ANDROID_STATISTICS_FACE_LANDMARKS, androidLandmarks);
> +	}
> +
> +	const auto &faceDetectFaceIds =
> +		metadata.get(controls::android::FaceDetectFaceIds);
> +	if (faceDetectRectangles && faceDetectFaceIds) {
> +		const Span<const int32_t> &ids = *faceDetectFaceIds;
> +		if (ids.size() != faceDetectRectangles->size()) {
> +			LOG(HAL, Error) << "Pipeline returned wrong number of face ids; "
> +					<< "Expected: "
> +					<< faceDetectRectangles->size()
> +					<< ", got: " << ids.size();
> +		}
> +		resultMetadata->addEntry(ANDROID_STATISTICS_FACE_IDS, ids);
> +	}
> +
>  	const auto &scalerCrop = metadata.get(controls::ScalerCrop);
>  	if (scalerCrop) {
>  		const Rectangle &crop = *scalerCrop;
> --
> 2.46.0.792.g87dc391469-goog
>
Cheng-Hao Yang Sept. 25, 2024, 8:16 a.m. UTC | #2
Hi Jacopo,

On Tue, Sep 24, 2024 at 9:02 PM Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi Harvey
>
> On Tue, Sep 24, 2024 at 11:02:46AM GMT, Harvey Yang wrote:
> > From: Harvey Yang <chenghaoyang@chromium.org>
> >
> > Allow Android HAL adapter to pass the face detection metadata control to
> > the pipeline and also send face detection metadata to the camera client
> > if the pipeline generates it.
> >
> > Signed-off-by: Yudhistira Erlandinata <yerlandinata@chromium.org>
> > Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> > ---
> >  src/android/camera_capabilities.cpp | 45 ++++++++++++++++--
> >  src/android/camera_device.cpp       | 73 ++++++++++++++++++++++++++++-
> >  2 files changed, 112 insertions(+), 6 deletions(-)
> >
> > diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
> > index 71043e12..d52d58ed 100644
> > --- a/src/android/camera_capabilities.cpp
> > +++ b/src/android/camera_capabilities.cpp
> > @@ -7,6 +7,8 @@
> >
> >  #include "camera_capabilities.h"
> >
> > +#include <stdint.h>
> > +
> >  #include <algorithm>
> >  #include <array>
> >  #include <cmath>
> > @@ -1176,11 +1178,46 @@ int CameraCapabilities::initializeStaticMetadata()
> >                                 maxFrameDuration_);
> >
> >       /* Statistics static metadata. */
> > -     uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > -     staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > -                               faceDetectMode);
> > -
> >       int32_t maxFaceCount = 0;
> > +     auto iter = camera_->controls().find(controls::android::FaceDetectMode.id());
> > +     if (iter != camera_->controls().end()) {
> > +             const ControlInfo &faceDetectCtrlInfo = iter->second;
> > +             std::vector<uint8_t> faceDetectModes;
> > +             bool hasFaceDetection = false;
> > +
> > +             for (const auto &value : faceDetectCtrlInfo.values()) {
> > +                     uint8_t mode = value.get<uint8_t>();
> > +                     uint8_t androidMode = 0;
> > +
> > +                     switch (mode) {
> > +                     case controls::android::FaceDetectModeOff:
> > +                             androidMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > +                             break;
> > +                     case controls::android::FaceDetectModeSimple:
> > +                             androidMode = ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE;
> > +                             hasFaceDetection = true;
> > +                             break;
> > +                     default:
> > +                             LOG(HAL, Fatal) << "Received invalid face detect mode: " << mode;
> > +                     }
> > +                     faceDetectModes.push_back(androidMode);
> > +             }
> > +             if (hasFaceDetection) {
> > +                     /*
> > +                      * \todo Create new libcamera controls to query max
> > +                      * possible faces detected.
> > +                      */
> > +                     maxFaceCount = 10;
> > +                     staticMetadata_->addEntry(
> > +                             ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > +                             faceDetectModes.data(), faceDetectModes.size());
> > +             }
> > +     } else {
> > +             uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > +             staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> > +                                       faceDetectMode);
> > +     }
> > +
> >       staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> >                                 maxFaceCount);
> >
> > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > index 493f66e7..1982509d 100644
> > --- a/src/android/camera_device.cpp
> > +++ b/src/android/camera_device.cpp
> > @@ -15,6 +15,7 @@
> >  #include <vector>
> >
> >  #include <libcamera/base/log.h>
> > +#include <libcamera/base/span.h>
> >  #include <libcamera/base/unique_fd.h>
> >  #include <libcamera/base/utils.h>
> >
> > @@ -22,6 +23,7 @@
> >  #include <libcamera/controls.h>
> >  #include <libcamera/fence.h>
> >  #include <libcamera/formats.h>
> > +#include <libcamera/geometry.h>
> >  #include <libcamera/property_ids.h>
> >
> >  #include "system/graphics.h"
> > @@ -813,6 +815,11 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)
> >               controls.set(controls::ScalerCrop, cropRegion);
> >       }
> >
> > +     if (settings.getEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, &entry)) {
> > +             const uint8_t *data = entry.data.u8;
> > +             controls.set(controls::android::FaceDetectMode, data[0]);
> > +     }
> > +
> >       if (settings.getEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, &entry)) {
> >               const int32_t data = *entry.data.i32;
> >               int32_t testPatternMode = controls::draft::TestPatternModeOff;
> > @@ -1540,8 +1547,9 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
> >       value32 = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
> >       resultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, value32);
> >
> > -     value = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> > -     resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, value);
> > +     settings.getEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, &entry);
> > +     resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> > +                              entry.data.u8, 1);
> >
> >       value = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
> >       resultMetadata->addEntry(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> > @@ -1580,6 +1588,67 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
> >               resultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,
> >                                        *frameDuration * 1000);
> >
> > +     const auto &faceDetectRectangles =
> > +             metadata.get(controls::android::FaceDetectFaceRectangles);
> > +     if (faceDetectRectangles) {
> > +             const Span<const Rectangle> rectangles = *faceDetectRectangles;
>
> You can spare all these temporary Span<>, but the rest of the code
> goes through temporaries too

Sure, removed the temporary variables.


>
> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>
> Thanks
>   j
>
>
> > +             std::vector<int32_t> flatRectangles;
> > +             for (const Rectangle &rect : rectangles) {
> > +                     flatRectangles.push_back(rect.x);
> > +                     flatRectangles.push_back(rect.y);
> > +                     flatRectangles.push_back(rect.x + rect.width);
> > +                     flatRectangles.push_back(rect.y + rect.height);
> > +             }
> > +             resultMetadata->addEntry(
> > +                     ANDROID_STATISTICS_FACE_RECTANGLES, flatRectangles);
> > +     }
> > +
> > +     const auto &faceDetectFaceScores =
> > +             metadata.get(controls::android::FaceDetectFaceScores);
> > +     if (faceDetectRectangles && faceDetectFaceScores) {
> > +             const Span<const uint8_t> &scores = *faceDetectFaceScores;
> > +             if (scores.size() != faceDetectRectangles->size()) {
> > +                     LOG(HAL, Error) << "Pipeline returned wrong number of face scores; "
> > +                                     << "Expected: "
> > +                                     << faceDetectRectangles->size()
> > +                                     << ", got: " << scores.size();
> > +             }
> > +             resultMetadata->addEntry(ANDROID_STATISTICS_FACE_SCORES, scores);
> > +     }
> > +
> > +     const auto &faceDetectFaceLandmarks =
> > +             metadata.get(controls::android::FaceDetectFaceLandmarks);
> > +     if (faceDetectRectangles && faceDetectFaceLandmarks) {
> > +             const auto &landmarks = *faceDetectFaceLandmarks;
> > +             size_t expectedLandmarks = faceDetectRectangles->size() * 3;
> > +             if (landmarks.size() != expectedLandmarks) {
> > +                     LOG(HAL, Error) << "Pipeline returned wrong number of face landmarks; "
> > +                                     << "Expected: " << expectedLandmarks
> > +                                     << ", got: " << landmarks.size();
> > +             }
> > +
> > +             std::vector<int32_t> androidLandmarks;
> > +             for (const auto &landmark : landmarks) {
> > +                     androidLandmarks.push_back(landmark.x);
> > +                     androidLandmarks.push_back(landmark.y);
> > +             }
> > +             resultMetadata->addEntry(
> > +                     ANDROID_STATISTICS_FACE_LANDMARKS, androidLandmarks);
> > +     }
> > +
> > +     const auto &faceDetectFaceIds =
> > +             metadata.get(controls::android::FaceDetectFaceIds);
> > +     if (faceDetectRectangles && faceDetectFaceIds) {
> > +             const Span<const int32_t> &ids = *faceDetectFaceIds;
> > +             if (ids.size() != faceDetectRectangles->size()) {
> > +                     LOG(HAL, Error) << "Pipeline returned wrong number of face ids; "
> > +                                     << "Expected: "
> > +                                     << faceDetectRectangles->size()
> > +                                     << ", got: " << ids.size();
> > +             }
> > +             resultMetadata->addEntry(ANDROID_STATISTICS_FACE_IDS, ids);
> > +     }
> > +
> >       const auto &scalerCrop = metadata.get(controls::ScalerCrop);
> >       if (scalerCrop) {
> >               const Rectangle &crop = *scalerCrop;
> > --
> > 2.46.0.792.g87dc391469-goog
> >



--
BR,
Harvey Yang

Patch
diff mbox series

diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
index 71043e12..d52d58ed 100644
--- a/src/android/camera_capabilities.cpp
+++ b/src/android/camera_capabilities.cpp
@@ -7,6 +7,8 @@ 
 
 #include "camera_capabilities.h"
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <array>
 #include <cmath>
@@ -1176,11 +1178,46 @@  int CameraCapabilities::initializeStaticMetadata()
 				  maxFrameDuration_);
 
 	/* Statistics static metadata. */
-	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
-	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
-				  faceDetectMode);
-
 	int32_t maxFaceCount = 0;
+	auto iter = camera_->controls().find(controls::android::FaceDetectMode.id());
+	if (iter != camera_->controls().end()) {
+		const ControlInfo &faceDetectCtrlInfo = iter->second;
+		std::vector<uint8_t> faceDetectModes;
+		bool hasFaceDetection = false;
+
+		for (const auto &value : faceDetectCtrlInfo.values()) {
+			uint8_t mode = value.get<uint8_t>();
+			uint8_t androidMode = 0;
+
+			switch (mode) {
+			case controls::android::FaceDetectModeOff:
+				androidMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+				break;
+			case controls::android::FaceDetectModeSimple:
+				androidMode = ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE;
+				hasFaceDetection = true;
+				break;
+			default:
+				LOG(HAL, Fatal) << "Received invalid face detect mode: " << mode;
+			}
+			faceDetectModes.push_back(androidMode);
+		}
+		if (hasFaceDetection) {
+			/*
+			 * \todo Create new libcamera controls to query max
+			 * possible faces detected.
+			 */
+			maxFaceCount = 10;
+			staticMetadata_->addEntry(
+				ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+				faceDetectModes.data(), faceDetectModes.size());
+		}
+	} else {
+		uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+		staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+					  faceDetectMode);
+	}
+
 	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
 				  maxFaceCount);
 
diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index 493f66e7..1982509d 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -15,6 +15,7 @@ 
 #include <vector>
 
 #include <libcamera/base/log.h>
+#include <libcamera/base/span.h>
 #include <libcamera/base/unique_fd.h>
 #include <libcamera/base/utils.h>
 
@@ -22,6 +23,7 @@ 
 #include <libcamera/controls.h>
 #include <libcamera/fence.h>
 #include <libcamera/formats.h>
+#include <libcamera/geometry.h>
 #include <libcamera/property_ids.h>
 
 #include "system/graphics.h"
@@ -813,6 +815,11 @@  int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)
 		controls.set(controls::ScalerCrop, cropRegion);
 	}
 
+	if (settings.getEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, &entry)) {
+		const uint8_t *data = entry.data.u8;
+		controls.set(controls::android::FaceDetectMode, data[0]);
+	}
+
 	if (settings.getEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, &entry)) {
 		const int32_t data = *entry.data.i32;
 		int32_t testPatternMode = controls::draft::TestPatternModeOff;
@@ -1540,8 +1547,9 @@  CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
 	value32 = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
 	resultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, value32);
 
-	value = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
-	resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, value);
+	settings.getEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, &entry);
+	resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
+				 entry.data.u8, 1);
 
 	value = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
 	resultMetadata->addEntry(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
@@ -1580,6 +1588,67 @@  CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
 		resultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,
 					 *frameDuration * 1000);
 
+	const auto &faceDetectRectangles =
+		metadata.get(controls::android::FaceDetectFaceRectangles);
+	if (faceDetectRectangles) {
+		const Span<const Rectangle> rectangles = *faceDetectRectangles;
+		std::vector<int32_t> flatRectangles;
+		for (const Rectangle &rect : rectangles) {
+			flatRectangles.push_back(rect.x);
+			flatRectangles.push_back(rect.y);
+			flatRectangles.push_back(rect.x + rect.width);
+			flatRectangles.push_back(rect.y + rect.height);
+		}
+		resultMetadata->addEntry(
+			ANDROID_STATISTICS_FACE_RECTANGLES, flatRectangles);
+	}
+
+	const auto &faceDetectFaceScores =
+		metadata.get(controls::android::FaceDetectFaceScores);
+	if (faceDetectRectangles && faceDetectFaceScores) {
+		const Span<const uint8_t> &scores = *faceDetectFaceScores;
+		if (scores.size() != faceDetectRectangles->size()) {
+			LOG(HAL, Error) << "Pipeline returned wrong number of face scores; "
+					<< "Expected: "
+					<< faceDetectRectangles->size()
+					<< ", got: " << scores.size();
+		}
+		resultMetadata->addEntry(ANDROID_STATISTICS_FACE_SCORES, scores);
+	}
+
+	const auto &faceDetectFaceLandmarks =
+		metadata.get(controls::android::FaceDetectFaceLandmarks);
+	if (faceDetectRectangles && faceDetectFaceLandmarks) {
+		const auto &landmarks = *faceDetectFaceLandmarks;
+		size_t expectedLandmarks = faceDetectRectangles->size() * 3;
+		if (landmarks.size() != expectedLandmarks) {
+			LOG(HAL, Error) << "Pipeline returned wrong number of face landmarks; "
+					<< "Expected: " << expectedLandmarks
+					<< ", got: " << landmarks.size();
+		}
+
+		std::vector<int32_t> androidLandmarks;
+		for (const auto &landmark : landmarks) {
+			androidLandmarks.push_back(landmark.x);
+			androidLandmarks.push_back(landmark.y);
+		}
+		resultMetadata->addEntry(
+			ANDROID_STATISTICS_FACE_LANDMARKS, androidLandmarks);
+	}
+
+	const auto &faceDetectFaceIds =
+		metadata.get(controls::android::FaceDetectFaceIds);
+	if (faceDetectRectangles && faceDetectFaceIds) {
+		const Span<const int32_t> &ids = *faceDetectFaceIds;
+		if (ids.size() != faceDetectRectangles->size()) {
+			LOG(HAL, Error) << "Pipeline returned wrong number of face ids; "
+					<< "Expected: "
+					<< faceDetectRectangles->size()
+					<< ", got: " << ids.size();
+		}
+		resultMetadata->addEntry(ANDROID_STATISTICS_FACE_IDS, ids);
+	}
+
 	const auto &scalerCrop = metadata.get(controls::ScalerCrop);
 	if (scalerCrop) {
 		const Rectangle &crop = *scalerCrop;