[PATCH v4 1/1] apps: cam: Add support for PPM output format
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Thu Mar 28 02:06:23 CET 2024
Hi Milan,
Thank you for the patch.
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"
> +
> +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);
> +};
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list