[libcamera-devel] [PATCH v5 6/8] libcamera: raspberrypi: Set camera flips correctly from user transform
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Tue Sep 1 02:42:12 CEST 2020
Hi David,
Thank you for the patch.
On Sat, Aug 29, 2020 at 12:54:27PM +0100, David Plowman wrote:
> The Raspberry Pi pipeline handler allows all transforms except those
> involving a transpose. The user transform is combined with any
> inherent rotation of the camera, and the camera's H and V flip bits
> are set accordingly.
>
> Note that the validate() method has to work out what the final Bayer
> order of any raw streams will be, before configure() actually applies
> the transform to the sensor.
>
> Signed-off-by: David Plowman <david.plowman at raspberrypi.com>
> ---
> .../pipeline/raspberrypi/raspberrypi.cpp | 82 ++++++++++++++++---
> 1 file changed, 71 insertions(+), 11 deletions(-)
>
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> index c554532..333aa94 100644
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> @@ -294,7 +294,7 @@ public:
> void frameStarted(uint32_t sequence);
>
> int loadIPA();
> - int configureIPA();
> + int configureIPA(CameraConfiguration *config);
>
> void queueFrameAction(unsigned int frame, const IPAOperationData &action);
>
> @@ -400,8 +400,34 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()
> if (config_.empty())
> return Invalid;
>
> - if (transform != Transform::Identity) {
> - transform = Transform::Identity;
> + /*
> + * What if the platform has a non-90 degree rotation? We can't even
> + * "adjust" the configuration and carry on. Alternatively, raising an
> + * error means the platform can never run. Let's just print a warning
> + * and continue regardless; the rotation is effectively set to zero.
> + */
> + int32_t rotation = data_->sensor_->properties().get(properties::Rotation);
> + bool success;
> + Transform combined = transform * transformFromRotation(rotation, &success);
> + if (!success)
> + LOG(RPI, Warning) << "Invalid rotation of " << rotation
> + << " degrees - ignoring";
> +
> + /*
> + * We combine the platform and user transform, but must "adjust away"
> + * any combined result that includes a transform, as we can't do those.
> + * In this case, flipping only the transpose bit is helpful to
> + * applications - they either get the transform they requested, or have
> + * to do a simple transpose themselves (they don't have to worry about
> + * the other possible cases).
> + */
> + if (!!(combined & Transform::Transpose)) {
> + /*
> + * Flipping the transpose bit in "transform" flips it in
> + * combined result too (as it's the last thing that happens).
> + */
> + transform ^= Transform::Transpose;
> + combined ^= Transform::Transpose;
> status = Adjusted;
> }
Shouldn't thus logic handle the case where the sensor can't flip ? I
think we can keep it simple that considering that either both or neither
flips are supported.
>
> @@ -414,13 +440,42 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()
> * Calculate the best sensor mode we can use based on
> * the user request.
> */
> - V4L2VideoDevice::Formats fmts = data_->unicam_[Unicam::Image].dev()->formats();
> + V4L2VideoDevice *dev = data_->unicam_[Unicam::Image].dev();
> + V4L2VideoDevice::Formats fmts = dev->formats();
> V4L2DeviceFormat sensorFormat = findBestMode(fmts, cfg.size);
> - int ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&sensorFormat);
> + int ret = dev->tryFormat(&sensorFormat);
> if (ret)
> return Invalid;
>
> - PixelFormat sensorPixFormat = sensorFormat.fourcc.toPixelFormat();
> + /*
> + * Some sensors change their Bayer order when they are
> + * h-flipped or v-flipped, according to the transform.
> + * If the controls own up to "modifying the layout" we
> + * will assume that's what is going on and advertise
> + * the transformed Bayer order in the stream.
> + */
> + V4L2PixelFormat fourcc = sensorFormat.fourcc;
> +
> + /*
> + * The camera may already be transformed, so we must
> + * only transform the fourcc if the new transform is
> + * different.
> + */
> + ControlList ctrls = dev->getControls({ V4L2_CID_HFLIP, V4L2_CID_VFLIP });
> +
> + const struct v4l2_query_ext_ctrl *hflipCtrl = dev->queryControl(V4L2_CID_HFLIP);
> + if (hflipCtrl &&
> + (hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT) &&
> + ctrls.get(V4L2_CID_HFLIP).get<int32_t>() != !!(combined & Transform::HFlip))
> + fourcc = fourcc.transform(Transform::HFlip);
> +
> + const struct v4l2_query_ext_ctrl *vflipCtrl = dev->queryControl(V4L2_CID_VFLIP);
> + if (vflipCtrl &&
> + (vflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT) &&
> + ctrls.get(V4L2_CID_VFLIP).get<int32_t>() != !!(combined & Transform::VFlip))
> + fourcc = fourcc.transform(Transform::VFlip);
To simplify this, would it make sense to reset the h/v flip controls at
init time, and then read and store the bayer format ? That would be the
native bayer format, and we would only need to care about the
V4L2_CTRL_FLAG_MODIFY_LAYOUT flags here.
If we consider it safe to assume that V4L2_CTRL_FLAG_MODIFY_LAYOUT will
be set for either both or neither flip controls, the code would be
simplified to
V4L2PixelFormat fourcc = data_->sensorFormat_;
const struct v4l2_query_ext_ctrl *hflipCtrl = dev->queryControl(V4L2_CID_HFLIP);
const struct v4l2_query_ext_ctrl *vflipCtrl = dev->queryControl(V4L2_CID_VFLIP);
if (hflipCtrl && vflipCtrl &&
(hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT) &&
(vflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT))
fourcc = fourcc.transform(combined);
> +
> + PixelFormat sensorPixFormat = fourcc.toPixelFormat();
> if (cfg.size != sensorFormat.size ||
> cfg.pixelFormat != sensorPixFormat) {
> cfg.size = sensorFormat.size;
> @@ -756,7 +811,7 @@ 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);
>
> - ret = data->configureIPA();
> + ret = data->configureIPA(config);
> if (ret)
> LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
>
> @@ -1112,7 +1167,7 @@ int RPiCameraData::loadIPA()
> return ipa_->init(settings);
> }
>
> -int RPiCameraData::configureIPA()
> +int RPiCameraData::configureIPA(CameraConfiguration *config)
> {
> std::map<unsigned int, IPAStream> streamConfig;
> std::map<unsigned int, const ControlInfoMap &> entityControls;
> @@ -1170,11 +1225,16 @@ int RPiCameraData::configureIPA()
> sensorMetadata_ = result.data[2];
> }
>
> - /* Configure the H/V flip controls based on the sensor rotation. */
> + /*
> + * Configure the H/V flip controls based on the sensor rotation
> + * and user transform.
> + */
> ControlList ctrls(unicam_[Unicam::Image].dev()->controls());
> int32_t rotation = sensor_->properties().get(properties::Rotation);
> - ctrls.set(V4L2_CID_HFLIP, static_cast<int32_t>(!!rotation));
> - ctrls.set(V4L2_CID_VFLIP, static_cast<int32_t>(!!rotation));
> + /* The rotation angle was already checked in validate(). */
> + Transform combined = config->transform * transformFromRotation(rotation);
> + ctrls.set(V4L2_CID_HFLIP, static_cast<int32_t>(!!(combined & Transform::HFlip)));
> + ctrls.set(V4L2_CID_VFLIP, static_cast<int32_t>(!!(combined & Transform::VFlip)));
> unicam_[Unicam::Image].dev()->setControls(&ctrls);
> }
>
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list