[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:43:10 CEST 2020
Hi David,
Another small comment.
On Tue, Sep 01, 2020 at 03:42:14AM +0300, Laurent Pinchart wrote:
> 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. */
> > + /*
There's a trailing white space here.
> > + * 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