[PATCH v4 1/1] apps: cam: Add support for PPM output format

Milan Zamazal mzamazal at redhat.com
Thu Mar 28 09:12:50 CET 2024


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

> Actually,
>
> On Thu, Mar 28, 2024 at 03:06:23AM +0200, Laurent Pinchart wrote:
>> On Fri, Mar 22, 2024 at 08:50:32PM +0100, Milan Zamazal wrote:
>> > When file output is requested from cam app, it simply dumps the processed data
>> > and it must be converted to a readable image format manually.  Let's add support
>> > for PPM output file format to make files produced by cam directly readable by
>> > image display and processing software.
>> > 
>> > For now, only BGR888 output format, which is the simplest one to use, is
>> > supported but nothing prevents adding support for other output formats if
>> > needed.  Nevertheless, they would typically need byte reordering or other
>> > conversions for PPM file format.  It may be better to find a way to dump the
>> > image data in other output formats directly using some of the already existing
>> > file formats or raw file format converters.
>> > 
>> > Signed-off-by: Milan Zamazal <mzamazal at redhat.com>
>> > Reviewed-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
>> 
>> Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
>> 
>> > ---
>> >  src/apps/cam/file_sink.cpp     | 11 +++++++
>> >  src/apps/cam/main.cpp          |  2 ++
>> >  src/apps/common/meson.build    |  1 +
>> >  src/apps/common/ppm_writer.cpp | 53 ++++++++++++++++++++++++++++++++++
>> >  src/apps/common/ppm_writer.h   | 20 +++++++++++++
>> >  5 files changed, 87 insertions(+)
>> >  create mode 100644 src/apps/common/ppm_writer.cpp
>> >  create mode 100644 src/apps/common/ppm_writer.h
>> > 
>> > diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp
>> > index dca350c4..906b50e6 100644
>> > --- a/src/apps/cam/file_sink.cpp
>> > +++ b/src/apps/cam/file_sink.cpp
>> > @@ -17,6 +17,7 @@
>> >  
>> >  #include "../common/dng_writer.h"
>> >  #include "../common/image.h"
>> > +#include "../common/ppm_writer.h"
>> >  
>> >  #include "file_sink.h"
>> >  
>> > @@ -76,6 +77,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
>> >  #ifdef HAVE_TIFF
>> >  	bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos;
>> >  #endif /* HAVE_TIFF */
>> > +	bool ppm = filename.find(".ppm", filename.size() - 4) != std::string::npos;
>> >  
>> >  	if (filename.empty() || filename.back() == '/')
>> >  		filename += "frame-#.bin";
>> > @@ -102,6 +104,15 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
>> >  		return;
>> >  	}
>> >  #endif /* HAVE_TIFF */
>> > +	if (ppm) {
>> > +		ret = PPMWriter::write(filename.c_str(), stream->configuration(),
>> > +				       image->data(0));
>> > +		if (ret < 0)
>> > +			std::cerr << "failed to write PPM file `" << filename
>> > +				  << "'" << std::endl;
>> > +
>> > +		return;
>> > +	}
>> >  
>> >  	fd = open(filename.c_str(), O_CREAT | O_WRONLY |
>> >  		  (pos == std::string::npos ? O_APPEND : O_TRUNC),
>> > diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp
>> > index 179cc376..1aabee01 100644
>> > --- a/src/apps/cam/main.cpp
>> > +++ b/src/apps/cam/main.cpp
>> > @@ -154,6 +154,8 @@ int CamApp::parseOptions(int argc, char *argv[])
>> >  			 "If the file name ends with '.dng', then the frame will be written to\n"
>> >  			 "the output file(s) in DNG format.\n"
>> >  #endif
>> > +			 "If the file name ends with '.ppm', then the frame will be written to\n"
>> > +			 "the output file(s) in PPM format.\n"
>> >  			 "The default file name is 'frame-#.bin'.",
>> >  			 "file", ArgumentOptional, "filename", false,
>> >  			 OptCamera);
>> > diff --git a/src/apps/common/meson.build b/src/apps/common/meson.build
>> > index 479326cd..5b683390 100644
>> > --- a/src/apps/common/meson.build
>> > +++ b/src/apps/common/meson.build
>> > @@ -3,6 +3,7 @@
>> >  apps_sources = files([
>> >      'image.cpp',
>> >      'options.cpp',
>> > +    'ppm_writer.cpp',
>> >      'stream_options.cpp',
>> >  ])
>> >  
>> > diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp
>> > new file mode 100644
>> > index 00000000..04a49499
>> > --- /dev/null
>> > +++ b/src/apps/common/ppm_writer.cpp
>> > @@ -0,0 +1,53 @@
>> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> > +/*
>> > + * Copyright (C) 2024 Red Hat, Inc.
>> > + *
>> > + * ppm_writer.cpp - PPM writer
>> > + */
>> > +
>> > +#include <fstream>
>> > +#include <iostream>
>> > +
>> > +#include <libcamera/formats.h>
>> > +#include <libcamera/pixel_format.h>
>> > +
>> > +#include "ppm_writer.h"
>
> checkstyle.py told me that this should go to the top. I'll fix when
> applying.

Thanks!  (And sorry for this omission.)

>> > +
>> > +using namespace libcamera;
>> > +
>> > +int PPMWriter::write(const char *filename,
>> > +		     const StreamConfiguration &config,
>> > +		     const Span<uint8_t> &data)
>> > +{
>> > +	if (config.pixelFormat != formats::BGR888) {
>> > +		std::cerr << "Only BGR888 output pixel format is supported ("
>> > +			  << config.pixelFormat << " requested)" << std::endl;
>> > +		return -EINVAL;
>> > +	}
>> > +
>> > +	std::ofstream output(filename, std::ios::binary);
>> > +	if (!output) {
>> > +		std::cerr << "Failed to open ppm file: " << filename << std::endl;
>> > +		return -EINVAL;
>> > +	}
>> > +
>> > +	output << "P6" << std::endl
>> > +	       << config.size.width << " " << config.size.height << std::endl
>> > +	       << "255" << std::endl;
>> > +	if (!output) {
>> > +		std::cerr << "Failed to write the file header" << std::endl;
>> > +		return -EINVAL;
>> > +	}
>> > +
>> > +	const unsigned int rowLength = config.size.width * 3;
>> > +	const char *row = reinterpret_cast<const char *>(data.data());
>> > +	for (unsigned int y = 0; y < config.size.height; y++, row += config.stride) {
>> > +		output.write(row, rowLength);
>> > +		if (!output) {
>> > +			std::cerr << "Failed to write image data at row " << y << std::endl;
>> > +			return -EINVAL;
>> > +		}
>> > +	}
>> > +
>> > +	return 0;
>> > +}
>> > diff --git a/src/apps/common/ppm_writer.h b/src/apps/common/ppm_writer.h
>> > new file mode 100644
>> > index 00000000..4c38f5ce
>> > --- /dev/null
>> > +++ b/src/apps/common/ppm_writer.h
>> > @@ -0,0 +1,20 @@
>> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> > +/*
>> > + * Copyright (C) 2024, Red Hat, Inc.
>> > + *
>> > + * ppm_writer.h - PPM writer
>> > + */
>> > +
>> > +#pragma once
>> > +
>> > +#include <libcamera/base/span.h>
>> > +
>> > +#include <libcamera/stream.h>
>> > +
>> > +class PPMWriter
>> > +{
>> > +public:
>> > +	static int write(const char *filename,
>> > +			 const libcamera::StreamConfiguration &config,
>> > +			 const libcamera::Span<uint8_t> &data);
>> > +};



More information about the libcamera-devel mailing list