[libcamera-devel,v3,2/2] libcamera: raspberrypi: Implement digital zoom

Message ID 20200723084338.13711-3-david.plowman@raspberrypi.com
State Superseded
Headers show
Series
  • Digital zoom implementatino
Related show

Commit Message

David Plowman July 23, 2020, 8:43 a.m. UTC
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.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 include/libcamera/ipa/raspberrypi.h           |  1 +
 src/ipa/raspberrypi/raspberrypi.cpp           | 10 +++
 .../pipeline/raspberrypi/raspberrypi.cpp      | 72 +++++++++++++++++++
 3 files changed, 83 insertions(+)

Patch

diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h
index a493776..f42e13e 100644
--- a/include/libcamera/ipa/raspberrypi.h
+++ b/include/libcamera/ipa/raspberrypi.h
@@ -56,6 +56,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 7bd0488..f759d4d 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -679,6 +679,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 bf1c771..6bc595f 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -308,6 +308,14 @@  public:
 	void handleStreamBuffer(FrameBuffer *buffer, const RPiStream *stream);
 	void handleState();
 
+	void initPipelineCrop(Rectangle const &crop, Size const &ispMinSize)
+	{
+		/* The initial zoom region is the whole of the pipelineCrop_. */
+		pipelineCrop_ = 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_;
@@ -344,6 +352,15 @@  private:
 
 	bool dropFrame_;
 	int ispOutputCount_;
+	/*
+	 * pipelineCrop_ 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 pipelineCrop_;
+	Rectangle zoomRect_;
+	Size ispMinSize_;
 };
 
 class RPiCameraConfiguration : public CameraConfiguration
@@ -736,6 +753,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);
+	unsigned int minSize = std::max(testCrop.width, testCrop.height);
+
 	/* Adjust aspect ratio by providing crops on the input image. */
 	Rectangle crop{ 0, 0, sensorFormat.size };
 
@@ -750,8 +771,17 @@  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);
 
+	pipelineCrop_ = Size(crop.width, crop.height);
+	Size ispMinSize(minSize, minSize);
+	if (crop.width > crop.height)
+		ispMinSize.width = minSize * crop.width / crop.height;
+	else if (crop.width < crop.height)
+		ispMinSize.height = minSize * crop.height / crop.width;
+	data->initPipelineCrop(crop, ispMinSize);
+
 	ret = data->configureIPA();
 	if (ret)
 		LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
@@ -1488,6 +1518,30 @@  void RPiCameraData::checkRequestCompleted()
 	}
 }
 
+static void fixup_dimension(int &start, unsigned int &size,
+			    unsigned int minSize, unsigned int maxSize)
+{
+	/*
+	 * If the size being requested it too small we must increase it, but
+	 * we try to keep the mid point the same. Though if this would mean
+	 * extending beyond the available bounds, then we have to line ourselves
+	 * up flush.
+	 */
+	int mid = start + size / 2;
+	size = std::min(std::max(size, minSize), maxSize);
+	start = mid - size / 2;
+	if (start < 0)
+		start = 0;
+	else if (start + size > maxSize)
+		start = maxSize - size;
+}
+
+static void fixup_rectangle(Rectangle &region, Size const &minSize, Size const &maxSize)
+{
+	fixup_dimension(region.x, region.width, minSize.width, maxSize.width);
+	fixup_dimension(region.y, region.height, minSize.height, maxSize.height);
+}
+
 void RPiCameraData::tryRunPipeline()
 {
 	FrameBuffer *bayerBuffer, *embeddedBuffer;
@@ -1541,6 +1595,24 @@  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 pipelineCrop_, and that it's not smaller than permitted.
+		 */
+		if (rect.width && rect.height) {
+			zoomRect_ = rect;
+			fixup_rectangle(zoomRect_, ispMinSize_,
+					Size(pipelineCrop_.width, pipelineCrop_.height));
+		}
+		Rectangle sensorRect = zoomRect_;
+		sensorRect.x += pipelineCrop_.x;
+		sensorRect.y += pipelineCrop_.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