[v11,1/8] libcamera: software_isp: Assign colour spaces in configurations
diff mbox series

Message ID 20250723180815.82450-2-mzamazal@redhat.com
State Superseded
Headers show
Series
  • Enable raw streams with software ISP
Related show

Commit Message

Milan Zamazal July 23, 2025, 6:08 p.m. UTC
StreamConfiguration's should have colorSpace set.  This is not the case
in the simple pipeline.  Let's set it there.  This also fixes a crash in
`cam' due to accessing an unset colorSpace.

The colour space is assigned as follows:

- If raw role is requested, then the colour space must be raw,
  regardless of the stream format.
- Otherwise, if software ISP is used, the default colour space is
  set to Srgb (YcbcrEncoding::None, Range::Full).
- Otherwise, if a converter is used, the default colour space is left
  unset.
- Then in configuration validation, if the pixel format is changed, the
  colour space is set according to the new pixel format.
- If the colour space is still unset, it is set according to the
  resulting pixel format.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 src/libcamera/pipeline/simple/simple.cpp | 38 +++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

Comments

Laurent Pinchart July 27, 2025, 10:34 p.m. UTC | #1
Hi Milan,

Thank you for the patch.

On Wed, Jul 23, 2025 at 08:08:06PM +0200, Milan Zamazal wrote:
> StreamConfiguration's should have colorSpace set.  This is not the case
> in the simple pipeline.  Let's set it there.  This also fixes a crash in
> `cam' due to accessing an unset colorSpace.
> 
> The colour space is assigned as follows:
> 
> - If raw role is requested, then the colour space must be raw,
>   regardless of the stream format.
> - Otherwise, if software ISP is used, the default colour space is
>   set to Srgb (YcbcrEncoding::None, Range::Full).
> - Otherwise, if a converter is used, the default colour space is left
>   unset.
> - Then in configuration validation, if the pixel format is changed, the
>   colour space is set according to the new pixel format.
> - If the colour space is still unset, it is set according to the
>   resulting pixel format.
> 
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> ---
>  src/libcamera/pipeline/simple/simple.cpp | 38 +++++++++++++++++++++++-
>  1 file changed, 37 insertions(+), 1 deletion(-)
> 
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index efb07051b..d45480fe7 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -25,6 +25,7 @@
>  #include <libcamera/base/log.h>
>  
>  #include <libcamera/camera.h>
> +#include <libcamera/color_space.h>
>  #include <libcamera/control_ids.h>
>  #include <libcamera/request.h>
>  #include <libcamera/stream.h>
> @@ -35,6 +36,7 @@
>  #include "libcamera/internal/converter.h"
>  #include "libcamera/internal/delayed_controls.h"
>  #include "libcamera/internal/device_enumerator.h"
> +#include "libcamera/internal/formats.h"
>  #include "libcamera/internal/media_device.h"
>  #include "libcamera/internal/pipeline_handler.h"
>  #include "libcamera/internal/software_isp/software_isp.h"
> @@ -1206,6 +1208,28 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
>  		if (cfg.pixelFormat != pixelFormat) {
>  			LOG(SimplePipeline, Debug) << "Adjusting pixel format";
>  			cfg.pixelFormat = pixelFormat;
> +			/*
> +			 * Do not touch the colour space for raw requested roles.
> +			 * Even if the pixel format is non-raw (whatever it means), we
> +			 * shouldn't try to interpret the colour space of raw data.
> +			 */
> +			if (cfg.colorSpace && cfg.colorSpace != ColorSpace::Raw)
> +				cfg.colorSpace->adjust(pixelFormat);

I'm trying to understand the rationale here. In validate() there's no
such thing as a raw role anymore, as roles are used in
generateConfiguration() only. Even if the configuration was generated
with a raw role, applications can change the pixel format and get a
processed stream.

Could you please explain what you're trying to do ?

> +			status = Adjusted;
> +		}

Please add a blank line here.

> +		if (!cfg.colorSpace) {
> +			const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);
> +			switch (info.colourEncoding) {
> +			case PixelFormatInfo::ColourEncodingRGB:
> +				cfg.colorSpace = ColorSpace::Srgb;
> +				break;
> +			case libcamera::PixelFormatInfo::ColourEncodingYUV:
> +				cfg.colorSpace = ColorSpace::Sycc;
> +				break;
> +			default:
> +				cfg.colorSpace = ColorSpace::Raw;
> +			}
> +			cfg.colorSpace->adjust(pixelFormat);
>  			status = Adjusted;
>  		}
>  
> @@ -1314,11 +1338,23 @@ SimplePipelineHandler::generateConfiguration(Camera *camera, Span<const StreamRo
>  	 *
>  	 * \todo Implement a better way to pick the default format
>  	 */
> -	for ([[maybe_unused]] StreamRole role : roles) {
> +	for (StreamRole role : roles) {
>  		StreamConfiguration cfg{ StreamFormats{ formats } };
>  		cfg.pixelFormat = formats.begin()->first;
>  		cfg.size = formats.begin()->second[0].max;
>  
> +		if (role == StreamRole::Raw) {
> +			/* Enforce raw colour space for raw roles. */
> +			cfg.colorSpace = ColorSpace::Raw;
> +		} else if (data->swIsp_) {
> +			/*
> +			 * This is what software ISP currently produces. It may be arguable
> +			 * whether this applies also when CCM is not used but even then it's
> +			 * still likely to be a reasonable setting.
> +			 */
> +			cfg.colorSpace = ColorSpace::Srgb;
> +		}
> +

The stream will still have no colorspace set for processed formats. The
logic in validate() that picks a default colorspace based on the pixel
format seems right for here too, you could move it to a separate
function and use it in both places.

>  		config->addConfiguration(cfg);
>  	}
>
Milan Zamazal July 28, 2025, 9:23 a.m. UTC | #2
Hi Laurent,

thank you for review.

Laurent Pinchart <laurent.pinchart@ideasonboard.com> writes:

> Hi Milan,
>
> Thank you for the patch.
>
> On Wed, Jul 23, 2025 at 08:08:06PM +0200, Milan Zamazal wrote:
>> StreamConfiguration's should have colorSpace set.  This is not the case
>> in the simple pipeline.  Let's set it there.  This also fixes a crash in
>> `cam' due to accessing an unset colorSpace.
>> 
>> The colour space is assigned as follows:
>> 
>> - If raw role is requested, then the colour space must be raw,
>>   regardless of the stream format.
>> - Otherwise, if software ISP is used, the default colour space is
>>   set to Srgb (YcbcrEncoding::None, Range::Full).
>> - Otherwise, if a converter is used, the default colour space is left
>>   unset.
>> - Then in configuration validation, if the pixel format is changed, the
>>   colour space is set according to the new pixel format.
>> - If the colour space is still unset, it is set according to the
>>   resulting pixel format.
>> 
>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
>> ---
>>  src/libcamera/pipeline/simple/simple.cpp | 38 +++++++++++++++++++++++-
>>  1 file changed, 37 insertions(+), 1 deletion(-)
>> 
>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
>> index efb07051b..d45480fe7 100644
>> --- a/src/libcamera/pipeline/simple/simple.cpp
>> +++ b/src/libcamera/pipeline/simple/simple.cpp
>> @@ -25,6 +25,7 @@
>>  #include <libcamera/base/log.h>
>>  
>>  #include <libcamera/camera.h>
>> +#include <libcamera/color_space.h>
>>  #include <libcamera/control_ids.h>
>>  #include <libcamera/request.h>
>>  #include <libcamera/stream.h>
>> @@ -35,6 +36,7 @@
>>  #include "libcamera/internal/converter.h"
>>  #include "libcamera/internal/delayed_controls.h"
>>  #include "libcamera/internal/device_enumerator.h"
>> +#include "libcamera/internal/formats.h"
>>  #include "libcamera/internal/media_device.h"
>>  #include "libcamera/internal/pipeline_handler.h"
>>  #include "libcamera/internal/software_isp/software_isp.h"
>> @@ -1206,6 +1208,28 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
>>  		if (cfg.pixelFormat != pixelFormat) {
>>  			LOG(SimplePipeline, Debug) << "Adjusting pixel format";
>>  			cfg.pixelFormat = pixelFormat;
>> +			/*
>> +			 * Do not touch the colour space for raw requested roles.
>> +			 * Even if the pixel format is non-raw (whatever it means), we
>> +			 * shouldn't try to interpret the colour space of raw data.
>> +			 */
>> +			if (cfg.colorSpace && cfg.colorSpace != ColorSpace::Raw)
>> +				cfg.colorSpace->adjust(pixelFormat);
>
> I'm trying to understand the rationale here. In validate() there's no
> such thing as a raw role anymore, as roles are used in
> generateConfiguration() only.

The first sentence in the comment should be changed to: "Do not touch
raw colour spaces."

> Even if the configuration was generated with a raw role, applications
> can change the pixel format and get a processed stream.

If an application changes the pixel format, shouldn't it also change the
colour space to a non-raw one or unspecified?

> Could you please explain what you're trying to do ?

The rationale is that if a raw colour space is explicitly requested,
there is probably a reason and we shouldn't try to change that.  I don't
say it's a completely correct argument but it looks to me like a safer
choice in case we don't have more information.  If you think otherwise,
I can drop the condition.

>> +			status = Adjusted;
>> +		}
>
> Please add a blank line here.

OK.

>> +		if (!cfg.colorSpace) {
>> +			const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);
>> +			switch (info.colourEncoding) {
>> +			case PixelFormatInfo::ColourEncodingRGB:
>> +				cfg.colorSpace = ColorSpace::Srgb;
>> +				break;
>> +			case libcamera::PixelFormatInfo::ColourEncodingYUV:
>> +				cfg.colorSpace = ColorSpace::Sycc;
>> +				break;
>> +			default:
>> +				cfg.colorSpace = ColorSpace::Raw;
>> +			}
>> +			cfg.colorSpace->adjust(pixelFormat);
>>  			status = Adjusted;
>>  		}
>>  
>> @@ -1314,11 +1338,23 @@ SimplePipelineHandler::generateConfiguration(Camera *camera, Span<const StreamRo
>>  	 *
>>  	 * \todo Implement a better way to pick the default format
>>  	 */
>> -	for ([[maybe_unused]] StreamRole role : roles) {
>> +	for (StreamRole role : roles) {
>>  		StreamConfiguration cfg{ StreamFormats{ formats } };
>>  		cfg.pixelFormat = formats.begin()->first;
>>  		cfg.size = formats.begin()->second[0].max;
>>  
>> +		if (role == StreamRole::Raw) {
>> +			/* Enforce raw colour space for raw roles. */
>> +			cfg.colorSpace = ColorSpace::Raw;
>> +		} else if (data->swIsp_) {
>> +			/*
>> +			 * This is what software ISP currently produces. It may be arguable
>> +			 * whether this applies also when CCM is not used but even then it's
>> +			 * still likely to be a reasonable setting.
>> +			 */
>> +			cfg.colorSpace = ColorSpace::Srgb;
>> +		}
>> +
>
> The stream will still have no colorspace set for processed formats. The
> logic in validate() that picks a default colorspace based on the pixel
> format seems right for here too, you could move it to a separate
> function and use it in both places.

Do you mean

  if (role == StreamRole::Raw) {
          /* Enforce raw colour space for raw roles. */
          cfg.colorSpace = ColorSpace::Raw;
  } else {
          /* Select according to the pixel format. */
          ...
  }

or also for raw?  Does "raw" mean, in the context of the simple
pipeline, not interpreted in any way (including not trying to set the
colour space) or just unprocessed?

>>  		config->addConfiguration(cfg);
>>  	}
>>
Laurent Pinchart July 28, 2025, 11:24 a.m. UTC | #3
Hi Milan,

On Mon, Jul 28, 2025 at 11:23:58AM +0200, Milan Zamazal wrote:
> Laurent Pinchart writes:
> > On Wed, Jul 23, 2025 at 08:08:06PM +0200, Milan Zamazal wrote:
> >> StreamConfiguration's should have colorSpace set.  This is not the case
> >> in the simple pipeline.  Let's set it there.  This also fixes a crash in
> >> `cam' due to accessing an unset colorSpace.
> >> 
> >> The colour space is assigned as follows:
> >> 
> >> - If raw role is requested, then the colour space must be raw,
> >>   regardless of the stream format.
> >> - Otherwise, if software ISP is used, the default colour space is
> >>   set to Srgb (YcbcrEncoding::None, Range::Full).
> >> - Otherwise, if a converter is used, the default colour space is left
> >>   unset.
> >> - Then in configuration validation, if the pixel format is changed, the
> >>   colour space is set according to the new pixel format.
> >> - If the colour space is still unset, it is set according to the
> >>   resulting pixel format.
> >> 
> >> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> >> ---
> >>  src/libcamera/pipeline/simple/simple.cpp | 38 +++++++++++++++++++++++-
> >>  1 file changed, 37 insertions(+), 1 deletion(-)
> >> 
> >> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> >> index efb07051b..d45480fe7 100644
> >> --- a/src/libcamera/pipeline/simple/simple.cpp
> >> +++ b/src/libcamera/pipeline/simple/simple.cpp
> >> @@ -25,6 +25,7 @@
> >>  #include <libcamera/base/log.h>
> >>  
> >>  #include <libcamera/camera.h>
> >> +#include <libcamera/color_space.h>
> >>  #include <libcamera/control_ids.h>
> >>  #include <libcamera/request.h>
> >>  #include <libcamera/stream.h>
> >> @@ -35,6 +36,7 @@
> >>  #include "libcamera/internal/converter.h"
> >>  #include "libcamera/internal/delayed_controls.h"
> >>  #include "libcamera/internal/device_enumerator.h"
> >> +#include "libcamera/internal/formats.h"
> >>  #include "libcamera/internal/media_device.h"
> >>  #include "libcamera/internal/pipeline_handler.h"
> >>  #include "libcamera/internal/software_isp/software_isp.h"
> >> @@ -1206,6 +1208,28 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
> >>  		if (cfg.pixelFormat != pixelFormat) {
> >>  			LOG(SimplePipeline, Debug) << "Adjusting pixel format";
> >>  			cfg.pixelFormat = pixelFormat;
> >> +			/*
> >> +			 * Do not touch the colour space for raw requested roles.
> >> +			 * Even if the pixel format is non-raw (whatever it means), we
> >> +			 * shouldn't try to interpret the colour space of raw data.
> >> +			 */
> >> +			if (cfg.colorSpace && cfg.colorSpace != ColorSpace::Raw)
> >> +				cfg.colorSpace->adjust(pixelFormat);
> >
> > I'm trying to understand the rationale here. In validate() there's no
> > such thing as a raw role anymore, as roles are used in
> > generateConfiguration() only.
> 
> The first sentence in the comment should be changed to: "Do not touch
> raw colour spaces."
> 
> > Even if the configuration was generated with a raw role, applications
> > can change the pixel format and get a processed stream.
> 
> If an application changes the pixel format, shouldn't it also change the
> colour space to a non-raw one or unspecified?

Probably, as YUV + ColorSpace::Raw doesn't make too much sense, but we
need to be prepared for applications making mistakes. validate() should
return a valid, sensible configuration.

> > Could you please explain what you're trying to do ?
> 
> The rationale is that if a raw colour space is explicitly requested,
> there is probably a reason and we shouldn't try to change that.  I don't
> say it's a completely correct argument but it looks to me like a safer
> choice in case we don't have more information.  If you think otherwise,
> I can drop the condition.

I don't think we shouldn't consider ColorSpace::Raw is having any
special meaning when set by an application. Or if we did, we would need
to define what special meaning it has, and I don't know what that would
be :-)

> >> +			status = Adjusted;
> >> +		}
> >
> > Please add a blank line here.
> 
> OK.
> 
> >> +		if (!cfg.colorSpace) {
> >> +			const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);
> >> +			switch (info.colourEncoding) {
> >> +			case PixelFormatInfo::ColourEncodingRGB:
> >> +				cfg.colorSpace = ColorSpace::Srgb;
> >> +				break;
> >> +			case libcamera::PixelFormatInfo::ColourEncodingYUV:
> >> +				cfg.colorSpace = ColorSpace::Sycc;
> >> +				break;
> >> +			default:
> >> +				cfg.colorSpace = ColorSpace::Raw;
> >> +			}
> >> +			cfg.colorSpace->adjust(pixelFormat);
> >>  			status = Adjusted;
> >>  		}
> >>  
> >> @@ -1314,11 +1338,23 @@ SimplePipelineHandler::generateConfiguration(Camera *camera, Span<const StreamRo
> >>  	 *
> >>  	 * \todo Implement a better way to pick the default format
> >>  	 */
> >> -	for ([[maybe_unused]] StreamRole role : roles) {
> >> +	for (StreamRole role : roles) {
> >>  		StreamConfiguration cfg{ StreamFormats{ formats } };
> >>  		cfg.pixelFormat = formats.begin()->first;
> >>  		cfg.size = formats.begin()->second[0].max;
> >>  
> >> +		if (role == StreamRole::Raw) {
> >> +			/* Enforce raw colour space for raw roles. */
> >> +			cfg.colorSpace = ColorSpace::Raw;
> >> +		} else if (data->swIsp_) {
> >> +			/*
> >> +			 * This is what software ISP currently produces. It may be arguable
> >> +			 * whether this applies also when CCM is not used but even then it's
> >> +			 * still likely to be a reasonable setting.
> >> +			 */
> >> +			cfg.colorSpace = ColorSpace::Srgb;
> >> +		}
> >> +
> >
> > The stream will still have no colorspace set for processed formats. The
> > logic in validate() that picks a default colorspace based on the pixel
> > format seems right for here too, you could move it to a separate
> > function and use it in both places.
> 
> Do you mean
> 
>   if (role == StreamRole::Raw) {
>           /* Enforce raw colour space for raw roles. */
>           cfg.colorSpace = ColorSpace::Raw;
>   } else {
>           /* Select according to the pixel format. */
>           ...
>   }
> 
> or also for raw?

I would first set the pixel format based on the role (and the supported
pixel formats of course), and then the colorspace based on the pixel
format.

> Does "raw" mean, in the context of the simple
> pipeline, not interpreted in any way (including not trying to set the
> colour space) or just unprocessed?

I think StreamRole::Raw should be considered as real raw images. Even if
the camera pipeline in the SoC doesn't process the images, a YUV camera
sensor doesn't produce raw images.

> >>  		config->addConfiguration(cfg);
> >>  	}
> >>
Milan Zamazal July 28, 2025, 1:49 p.m. UTC | #4
Laurent Pinchart <laurent.pinchart@ideasonboard.com> writes:

> Hi Milan,
>
> On Mon, Jul 28, 2025 at 11:23:58AM +0200, Milan Zamazal wrote:
>> Laurent Pinchart writes:
>> > On Wed, Jul 23, 2025 at 08:08:06PM +0200, Milan Zamazal wrote:
>> >> StreamConfiguration's should have colorSpace set.  This is not the case
>> >> in the simple pipeline.  Let's set it there.  This also fixes a crash in
>> >> `cam' due to accessing an unset colorSpace.
>> >> 
>> >> The colour space is assigned as follows:
>> >> 
>> >> - If raw role is requested, then the colour space must be raw,
>> >>   regardless of the stream format.
>> >> - Otherwise, if software ISP is used, the default colour space is
>> >>   set to Srgb (YcbcrEncoding::None, Range::Full).
>> >> - Otherwise, if a converter is used, the default colour space is left
>> >>   unset.
>> >> - Then in configuration validation, if the pixel format is changed, the
>> >>   colour space is set according to the new pixel format.
>> >> - If the colour space is still unset, it is set according to the
>> >>   resulting pixel format.
>> >> 
>> >> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
>> >> ---
>> >>  src/libcamera/pipeline/simple/simple.cpp | 38 +++++++++++++++++++++++-
>> >>  1 file changed, 37 insertions(+), 1 deletion(-)
>> >> 
>> >> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
>> >> index efb07051b..d45480fe7 100644
>> >> --- a/src/libcamera/pipeline/simple/simple.cpp
>> >> +++ b/src/libcamera/pipeline/simple/simple.cpp
>> >> @@ -25,6 +25,7 @@
>> >>  #include <libcamera/base/log.h>
>> >>  
>> >>  #include <libcamera/camera.h>
>> >> +#include <libcamera/color_space.h>
>> >>  #include <libcamera/control_ids.h>
>> >>  #include <libcamera/request.h>
>> >>  #include <libcamera/stream.h>
>> >> @@ -35,6 +36,7 @@
>> >>  #include "libcamera/internal/converter.h"
>> >>  #include "libcamera/internal/delayed_controls.h"
>> >>  #include "libcamera/internal/device_enumerator.h"
>> >> +#include "libcamera/internal/formats.h"
>> >>  #include "libcamera/internal/media_device.h"
>> >>  #include "libcamera/internal/pipeline_handler.h"
>> >>  #include "libcamera/internal/software_isp/software_isp.h"
>> >> @@ -1206,6 +1208,28 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
>> >>  		if (cfg.pixelFormat != pixelFormat) {
>> >>  			LOG(SimplePipeline, Debug) << "Adjusting pixel format";
>> >>  			cfg.pixelFormat = pixelFormat;
>> >> +			/*
>> >> +			 * Do not touch the colour space for raw requested roles.
>> >> +			 * Even if the pixel format is non-raw (whatever it means), we
>> >> +			 * shouldn't try to interpret the colour space of raw data.
>> >> +			 */
>> >> +			if (cfg.colorSpace && cfg.colorSpace != ColorSpace::Raw)
>> >> +				cfg.colorSpace->adjust(pixelFormat);
>> >
>> > I'm trying to understand the rationale here. In validate() there's no
>> > such thing as a raw role anymore, as roles are used in
>> > generateConfiguration() only.
>> 
>> The first sentence in the comment should be changed to: "Do not touch
>> raw colour spaces."
>> 
>> > Even if the configuration was generated with a raw role, applications
>> > can change the pixel format and get a processed stream.
>> 
>> If an application changes the pixel format, shouldn't it also change the
>> colour space to a non-raw one or unspecified?
>
> Probably, as YUV + ColorSpace::Raw doesn't make too much sense, but we
> need to be prepared for applications making mistakes. validate() should
> return a valid, sensible configuration.

Not completely sure about this particular combination, but I'd say that
raw colour space is a more correct colour space assignment than
e.g. sRGB for an RGB pixel format output without gamma & CCM applied.  I
can imagine that an application setting the pixel format to RGB and the
colour space to raw is ready to get a debayered RGB without any
corrections after debayering applied in the image processing pipeline.

I admit I may be well beyond practical considerations here, expecting
too much sophistication from both libcamera and applications, still much
simplifying and ignoring the fact it's difficult to be 100% correct here
in any case.  But if we are going to derive the colour space from the
pixel format unconditionally, despite those are actually two different
while not completely independent things, we should have some answer to
"why?".  Taking this opportunity to clarify the things.

>> > Could you please explain what you're trying to do ?
>> 
>> The rationale is that if a raw colour space is explicitly requested,
>> there is probably a reason and we shouldn't try to change that.  I don't
>> say it's a completely correct argument but it looks to me like a safer
>> choice in case we don't have more information.  If you think otherwise,
>> I can drop the condition.
>
> I don't think we shouldn't consider ColorSpace::Raw is having any
> special meaning when set by an application. Or if we did, we would need
> to define what special meaning it has, and I don't know what that would
> be :-)

This may be one of the answers :-).  An obvious objection could be that
if we return e.g. sRGB colour space then the pixel values should
represent, according to our & camera's best effort, the actual colours
as defined by sRGB.  Is it true for the current simple pipeline?  And if
not, does anybody care?

>> >> +			status = Adjusted;
>> >> +		}
>> >
>> > Please add a blank line here.
>> 
>> OK.
>> 
>> >> +		if (!cfg.colorSpace) {
>> >> +			const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);
>> >> +			switch (info.colourEncoding) {
>> >> +			case PixelFormatInfo::ColourEncodingRGB:
>> >> +				cfg.colorSpace = ColorSpace::Srgb;
>> >> +				break;
>> >> +			case libcamera::PixelFormatInfo::ColourEncodingYUV:
>> >> +				cfg.colorSpace = ColorSpace::Sycc;
>> >> +				break;
>> >> +			default:
>> >> +				cfg.colorSpace = ColorSpace::Raw;
>> >> +			}
>> >> +			cfg.colorSpace->adjust(pixelFormat);
>> >>  			status = Adjusted;
>> >>  		}
>> >>  
>> >> @@ -1314,11 +1338,23 @@ SimplePipelineHandler::generateConfiguration(Camera *camera, Span<const StreamRo
>> >>  	 *
>> >>  	 * \todo Implement a better way to pick the default format
>> >>  	 */
>> >> -	for ([[maybe_unused]] StreamRole role : roles) {
>> >> +	for (StreamRole role : roles) {
>> >>  		StreamConfiguration cfg{ StreamFormats{ formats } };
>> >>  		cfg.pixelFormat = formats.begin()->first;
>> >>  		cfg.size = formats.begin()->second[0].max;
>> >>  
>> >> +		if (role == StreamRole::Raw) {
>> >> +			/* Enforce raw colour space for raw roles. */
>> >> +			cfg.colorSpace = ColorSpace::Raw;
>> >> +		} else if (data->swIsp_) {
>> >> +			/*
>> >> +			 * This is what software ISP currently produces. It may be arguable
>> >> +			 * whether this applies also when CCM is not used but even then it's
>> >> +			 * still likely to be a reasonable setting.
>> >> +			 */
>> >> +			cfg.colorSpace = ColorSpace::Srgb;
>> >> +		}
>> >> +
>> >
>> > The stream will still have no colorspace set for processed formats. The
>> > logic in validate() that picks a default colorspace based on the pixel
>> > format seems right for here too, you could move it to a separate
>> > function and use it in both places.
>> 
>> Do you mean
>> 
>>   if (role == StreamRole::Raw) {
>>           /* Enforce raw colour space for raw roles. */
>>           cfg.colorSpace = ColorSpace::Raw;
>>   } else {
>>           /* Select according to the pixel format. */
>>           ...
>>   }
>> 
>> or also for raw?
>
> I would first set the pixel format based on the role (and the supported
> pixel formats of course), and then the colorspace based on the pixel
> format.
>
>> Does "raw" mean, in the context of the simple
>> pipeline, not interpreted in any way (including not trying to set the
>> colour space) or just unprocessed?
>
> I think StreamRole::Raw should be considered as real raw images. Even if
> the camera pipeline in the SoC doesn't process the images, a YUV camera
> sensor doesn't produce raw images.
>
>> >>  		config->addConfiguration(cfg);
>> >>  	}
>> >>

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index efb07051b..d45480fe7 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -25,6 +25,7 @@ 
 #include <libcamera/base/log.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/color_space.h>
 #include <libcamera/control_ids.h>
 #include <libcamera/request.h>
 #include <libcamera/stream.h>
@@ -35,6 +36,7 @@ 
 #include "libcamera/internal/converter.h"
 #include "libcamera/internal/delayed_controls.h"
 #include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/formats.h"
 #include "libcamera/internal/media_device.h"
 #include "libcamera/internal/pipeline_handler.h"
 #include "libcamera/internal/software_isp/software_isp.h"
@@ -1206,6 +1208,28 @@  CameraConfiguration::Status SimpleCameraConfiguration::validate()
 		if (cfg.pixelFormat != pixelFormat) {
 			LOG(SimplePipeline, Debug) << "Adjusting pixel format";
 			cfg.pixelFormat = pixelFormat;
+			/*
+			 * Do not touch the colour space for raw requested roles.
+			 * Even if the pixel format is non-raw (whatever it means), we
+			 * shouldn't try to interpret the colour space of raw data.
+			 */
+			if (cfg.colorSpace && cfg.colorSpace != ColorSpace::Raw)
+				cfg.colorSpace->adjust(pixelFormat);
+			status = Adjusted;
+		}
+		if (!cfg.colorSpace) {
+			const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);
+			switch (info.colourEncoding) {
+			case PixelFormatInfo::ColourEncodingRGB:
+				cfg.colorSpace = ColorSpace::Srgb;
+				break;
+			case libcamera::PixelFormatInfo::ColourEncodingYUV:
+				cfg.colorSpace = ColorSpace::Sycc;
+				break;
+			default:
+				cfg.colorSpace = ColorSpace::Raw;
+			}
+			cfg.colorSpace->adjust(pixelFormat);
 			status = Adjusted;
 		}
 
@@ -1314,11 +1338,23 @@  SimplePipelineHandler::generateConfiguration(Camera *camera, Span<const StreamRo
 	 *
 	 * \todo Implement a better way to pick the default format
 	 */
-	for ([[maybe_unused]] StreamRole role : roles) {
+	for (StreamRole role : roles) {
 		StreamConfiguration cfg{ StreamFormats{ formats } };
 		cfg.pixelFormat = formats.begin()->first;
 		cfg.size = formats.begin()->second[0].max;
 
+		if (role == StreamRole::Raw) {
+			/* Enforce raw colour space for raw roles. */
+			cfg.colorSpace = ColorSpace::Raw;
+		} else if (data->swIsp_) {
+			/*
+			 * This is what software ISP currently produces. It may be arguable
+			 * whether this applies also when CCM is not used but even then it's
+			 * still likely to be a reasonable setting.
+			 */
+			cfg.colorSpace = ColorSpace::Srgb;
+		}
+
 		config->addConfiguration(cfg);
 	}