[libcamera-devel,RFC,4/4] libcamera: pipeline: raspberrypi: Implementation of digital zoom

Message ID 20200907164450.13082-5-david.plowman@raspberrypi.com
State Accepted
Headers show
Series
  • Digital zoom
Related show

Commit Message

David Plowman Sept. 7, 2020, 4:44 p.m. UTC
During configure() we make a note of the minimum crop size allowed by
the ISP, and the image output size of the chosen sensor mode.

Whenever a new SensorCrop request is received we check it's valid and
apply it to the ISP V4L2 device. We also forward it to the IPA so that
the IPA can return the values used in the image metadata.
---
 include/libcamera/ipa/raspberrypi.h           |  1 +
 src/ipa/raspberrypi/raspberrypi.cpp           |  7 ++++
 .../pipeline/raspberrypi/raspberrypi.cpp      | 39 +++++++++++++++++++
 3 files changed, 47 insertions(+)

Patch

diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h
index ca62990..361b47d 100644
--- a/include/libcamera/ipa/raspberrypi.h
+++ b/include/libcamera/ipa/raspberrypi.h
@@ -57,6 +57,7 @@  static const ControlInfoMap RPiControls = {
 	{ &controls::Saturation, ControlInfo(0.0f, 32.0f) },
 	{ &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
 	{ &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) },
+	{ &controls::SensorCrop, 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 4557016..4a364b7 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -687,6 +687,13 @@  void IPARPi::queueRequest(const ControlList &controls)
 			break;
 		}
 
+		case controls::SENSOR_CROP: {
+			/* Just copy the information back. */
+			Rectangle crop = ctrl.second.get<Rectangle>();
+			libcameraMetadata_.set(controls::SensorCrop, 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 7f151cb..3196a61 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -335,6 +335,11 @@  public:
 	std::queue<FrameBuffer *> embeddedQueue_;
 	std::deque<Request *> requestQueue_;
 
+	/* For handling digital zoom. */
+	Size ispMinSize_;
+	Size sensorOutputSize_;
+	Rectangle lastSensorCrop_;
+
 private:
 	void checkRequestCompleted();
 	void tryRunPipeline();
@@ -737,6 +742,16 @@  int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
 		return ret;
 	}
 
+	/*
+	 * Figure out the smallest selection the ISP will allow. We also store
+	 * the output image size, in pixels, from the sensor. These will be
+	 * used for digital zoom.
+	 */
+	Rectangle testCrop(0, 0, 1, 1);
+	data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop);
+	data->ispMinSize_ = testCrop.size();
+	data->sensorOutputSize_ = sensorFormat.size;
+
 	/* Adjust aspect ratio by providing crops on the input image. */
 	Rectangle crop{ 0, 0, sensorFormat.size };
 
@@ -753,6 +768,8 @@  int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
 	crop.y = (sensorFormat.size.height - crop.height) >> 1;
 	data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);
 
+	data->lastSensorCrop_ = crop;
+
 	ret = data->configureIPA();
 	if (ret)
 		LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
@@ -1545,6 +1562,28 @@  void RPiCameraData::tryRunPipeline()
 	 */
 	Request *request = requestQueue_.front();
 
+	if (request->controls().contains(controls::SensorCrop)) {
+		Rectangle crop = request->controls().get<Rectangle>(controls::SensorCrop);
+		if (crop.width && crop.height) {
+			/*
+			 * The crop that we set must be:
+			 * 1. At least as big as ispMinSize_, once that's been
+			 *    enlarged to the same aspect ratio.
+			 * 2. With the same mid-point, if possible.
+			 * 3. But it can't go outside the sensor area.
+			 */
+			Size minSize = ispMinSize_.aspectRatioUp(crop.size());
+			Size size = crop.size().expandedTo(minSize);
+			crop = size.centre(crop).clamp(Rectangle(sensorOutputSize_));
+
+			if (crop != lastSensorCrop_)
+				isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);
+			lastSensorCrop_ = crop;
+		}
+
+		request->controls().set(controls::SensorCrop, lastSensorCrop_);
+	}
+
 	/*
 	 * 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