[libcamera-devel] [PATCH v2 2/2] libcamera: raspberrypi: Implement digital zoom

David Plowman david.plowman at raspberrypi.com
Thu Jul 9 11:15:55 CEST 2020


These changes implement digital zoom for the Raspberry Pi. We detect
the digital zoom control and update the V4L2 "selection" before
starting the ISP. We also update the value in the control that we send
to the IPA, so that it has the correct value.
---
 include/libcamera/ipa/raspberrypi.h           |  1 +
 src/ipa/raspberrypi/raspberrypi.cpp           | 10 ++++
 .../pipeline/raspberrypi/raspberrypi.cpp      | 56 ++++++++++++++++++-
 3 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h
index a18ce9a..e66402e 100644
--- a/include/libcamera/ipa/raspberrypi.h
+++ b/include/libcamera/ipa/raspberrypi.h
@@ -52,6 +52,7 @@ static const ControlInfoMap RPiControls = {
 	{ &controls::Contrast, ControlInfo(0.0f, 32.0f) },
 	{ &controls::Saturation, ControlInfo(0.0f, 32.0f) },
 	{ &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
+	{ &controls::DigitalZoom, ControlInfo(Rectangle{ 0, 0, 0, 0 }, Rectangle{ 65535, 65535, 65535, 65535 }, Rectangle{ 0, 0, 0, 0 }) },
 };
 
 } /* namespace libcamera */
diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
index b1f2786..3c68078 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -658,6 +658,16 @@ void IPARPi::queueRequest(const ControlList &controls)
 			break;
 		}
 
+		case controls::DIGITAL_ZOOM: {
+			/*
+			 * We send the zoom info back in the metadata where the pipeline
+			 * handler will update it to the values actually used.
+			 */
+			Rectangle crop = ctrl.second.get<Rectangle>();
+			libcameraMetadata_.set(controls::DigitalZoom, crop);
+			break;
+		}
+
 		default:
 			LOG(IPARPI, Warning)
 				<< "Ctrl " << controls::controls.at(ctrl.first)->name()
diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index f4966f8..55db11d 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -287,7 +287,8 @@ class RPiCameraData : public CameraData
 public:
 	RPiCameraData(PipelineHandler *pipe)
 		: CameraData(pipe), sensor_(nullptr), lsTable_(nullptr),
-		  state_(State::Stopped), dropFrame_(false), ispOutputCount_(0)
+		  state_(State::Stopped), dropFrame_(false), ispOutputCount_(0),
+		  ispMinSize_(0)
 	{
 	}
 
@@ -322,6 +323,14 @@ public:
 	void handleStreamBuffer(FrameBuffer *buffer, const RPiStream *stream);
 	void handleState();
 
+	void initSensorCrop(Rectangle const &crop, unsigned int ispMinSize)
+	{
+		/* The initial zoom region is the whole of the sensorCrop_. */
+		sensorCrop_ = crop;
+		zoomRect_ = Rectangle{ 0, 0, crop.width, crop.height };
+		ispMinSize_ = ispMinSize;
+	}
+
 	CameraSensor *sensor_;
 	/* Array of Unicam and ISP device streams and associated buffers/streams. */
 	RPiDevice<Unicam, 2> unicam_;
@@ -358,6 +367,15 @@ private:
 
 	bool dropFrame_;
 	int ispOutputCount_;
+	/*
+	 * sensorCrop_ is the largest region of the full sensor output that matches
+	 * the desired aspect ratio, and therefore represents the area within
+	 * which we can pan and zoom. zoomRect_ is the portion from within the
+	 * sensorCrop_ that pan/zoom is currently using.
+	 */
+	Rectangle sensorCrop_;
+	Rectangle zoomRect_;
+	unsigned int ispMinSize_;
 };
 
 class RPiCameraConfiguration : public CameraConfiguration
@@ -744,6 +762,10 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
 		return ret;
 	}
 
+	Rectangle testCrop = { 0, 0, 1, 1 };
+	data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop);
+	const unsigned int ispMinSize = testCrop.width;
+
 	/* Adjust aspect ratio by providing crops on the input image. */
 	Rectangle crop = {
 		.x = 0,
@@ -763,8 +785,12 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
 
 	crop.x = (sensorFormat.size.width - crop.width) >> 1;
 	crop.y = (sensorFormat.size.height - crop.height) >> 1;
+
 	data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);
 
+	sensorCrop_ = Size(crop.width, crop.height);
+	data->initSensorCrop(crop, ispMinSize);
+
 	ret = configureIPA(camera);
 	if (ret)
 		LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
@@ -1553,6 +1579,34 @@ void RPiCameraData::tryRunPipeline()
 	 */
 	Request *request = requestQueue_.front();
 
+	if (request->controls().contains(controls::DigitalZoom)) {
+		Rectangle rect = request->controls().get<Rectangle>(controls::DigitalZoom);
+		/*
+		 * If a new digital zoom value was given, check that it lies within the
+		 * available sensorCrop_, coercing it if necessary.
+		 */
+		if (rect.width && rect.height) {
+			zoomRect_ = rect;
+			if (zoomRect_.width < ispMinSize_)
+				zoomRect_.width = ispMinSize_;
+			else if (zoomRect_.width > sensorCrop_.width)
+				zoomRect_.width = sensorCrop_.width;
+			if (zoomRect_.height < ispMinSize_)
+				zoomRect_.height = ispMinSize_;
+			else if (zoomRect_.height > sensorCrop_.height)
+				zoomRect_.height = sensorCrop_.height;
+			if (zoomRect_.x + zoomRect_.width > sensorCrop_.width)
+				zoomRect_.x = sensorCrop_.width - zoomRect_.width;
+			if (zoomRect_.y + zoomRect_.height > sensorCrop_.height)
+				zoomRect_.y = sensorCrop_.height - zoomRect_.height;
+		}
+		Rectangle sensorRect = zoomRect_;
+		sensorRect.x += sensorCrop_.x;
+		sensorRect.y += sensorCrop_.y;
+		isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &sensorRect);
+		request->controls().set(controls::DigitalZoom, zoomRect_);
+	}
+
 	/*
 	 * Process all the user controls by the IPA. Once this is complete, we
 	 * queue the ISP output buffer listed in the request to start the HW
-- 
2.20.1



More information about the libcamera-devel mailing list