@@ -16,6 +16,7 @@
#include <libcamera/camera.h>
#include "../common/dng_writer.h"
+#include "../common/pnm_writer.h"
#include "../common/image.h"
#include "file_sink.h"
@@ -73,6 +74,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
if (!pattern_.empty())
filename = pattern_;
+ bool pnm = filename.find(".pnm", filename.size() - 4) != std::string::npos;
#ifdef HAVE_TIFF
bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos;
#endif /* HAVE_TIFF */
@@ -90,6 +92,15 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
Image *image = mappedBuffers_[buffer].get();
+ if (pnm) {
+ ret = PNMWriter::write(filename.c_str(), stream->configuration(),
+ image->data(0).data());
+ if (ret < 0)
+ std::cerr << "failed to write PNM file `" << filename
+ << "'" << std::endl;
+
+ return;
+ }
#ifdef HAVE_TIFF
if (dng) {
ret = DNGWriter::write(filename.c_str(), camera_,
@@ -150,6 +150,8 @@ int CamApp::parseOptions(int argc, char *argv[])
"to write files, using the default file name. Otherwise it sets the\n"
"full file path and name. The first '#' character in the file name\n"
"is expanded to the camera index, stream name and frame sequence number.\n"
+ "If the file name ends with '.pnm', then the frame will be written to\n"
+ "the output file(s) in PNM format.\n"
#ifdef HAVE_TIFF
"If the file name ends with '.dng', then the frame will be written to\n"
"the output file(s) in DNG format.\n"
@@ -3,6 +3,7 @@
apps_sources = files([
'image.cpp',
'options.cpp',
+ 'pnm_writer.cpp',
'stream_options.cpp',
])
new file mode 100644
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * pnm_writer.cpp - PNM writer
+ */
+
+#include "pnm_writer.h"
+
+#include <fstream>
+#include <iostream>
+
+#include <libcamera/formats.h>
+#include <libcamera/pixel_format.h>
+
+using namespace libcamera;
+
+int PNMWriter::write(const char *filename,
+ const StreamConfiguration &config,
+ const void *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 pnm 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 unsigned int paddedRowLength = config.stride;
+ const char *row = reinterpret_cast<const char *>(data);
+ for (unsigned int y = 0; y < config.size.height; y++, row += paddedRowLength) {
+ output.write(row, rowLength);
+ if (!output) {
+ std::cerr << "Failed to write image data at row " << y << std::endl;
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Raspberry Pi Ltd
+ *
+ * pnm_writer.h - PNM writer
+ */
+
+#pragma once
+
+#include <libcamera/stream.h>
+
+class PNMWriter
+{
+public:
+ static int write(const char *filename,
+ const libcamera::StreamConfiguration &config,
+ const void *data);
+};
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 pnm 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. Signed-off-by: Milan Zamazal <mzamazal@redhat.com> --- src/apps/cam/file_sink.cpp | 11 +++++++ src/apps/cam/main.cpp | 2 ++ src/apps/common/meson.build | 1 + src/apps/common/pnm_writer.cpp | 54 ++++++++++++++++++++++++++++++++++ src/apps/common/pnm_writer.h | 18 ++++++++++++ 5 files changed, 86 insertions(+) create mode 100644 src/apps/common/pnm_writer.cpp create mode 100644 src/apps/common/pnm_writer.h