Skip to content

Commit

Permalink
helpers: examples: Add helpers and an example converter utility
Browse files Browse the repository at this point in the history
Signed-off-by: Naushir Patuck <[email protected]>
  • Loading branch information
naushir committed Jan 10, 2025
1 parent d7a29b2 commit 040e8cf
Show file tree
Hide file tree
Showing 12 changed files with 1,208 additions and 0 deletions.
1 change: 1 addition & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
# Copyright (C) 2023, Raspberry Pi Ltd

option('logging', type : 'feature', value : 'auto')
option('examples', type : 'boolean', value : false)
340 changes: 340 additions & 0 deletions src/examples/convert.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@

/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2024 Raspberry Pi Ltd
*
* convert.cpp - libpisp simple image converter example
*/

#include <assert.h>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <string>

#include <cxxopts.hpp>

#include "helpers/backend_device.hpp"
#include "helpers/media_device.hpp"

#include "libpisp/backend/backend.hpp"
#include "libpisp/common/logging.hpp"
#include "libpisp/common/utils.hpp"
#include "libpisp/variants/variant.hpp"

void read_plane(char *mem, std::ifstream &in, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
width = std::min(width, file_stride);

for (unsigned int y = 0; y < height; y++)
{
in.read(mem + y * buffer_stride, width);
in.seekg(file_stride - width, std::ios_base::cur);
}
}

void write_plane(std::ofstream &out, char *mem, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
width = std::min(width, file_stride);

for (unsigned int y = 0; y < height; y++)
{
out.write(mem + y * buffer_stride, width);
out.seekp(file_stride - width, std::ios_base::cur);
}
}

void read_rgb(char *mem, std::ifstream &in, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
read_plane(mem, in, width * 3, height, file_stride, buffer_stride);
}

void write_rgb(std::ofstream &out, char *mem, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
write_plane(out, mem, width * 3, height, file_stride, buffer_stride);
}

void read_yuv(char *mem, std::ifstream &in, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride, unsigned int ss_x, unsigned int ss_y)
{
// Y
read_plane(mem, in, width, height, file_stride, buffer_stride);
// U
mem += buffer_stride * height;
read_plane(mem, in, width / ss_x, height / ss_y, file_stride / ss_x,
buffer_stride / ss_x);
// V
mem += buffer_stride / ss_x * height / ss_y;
read_plane(mem, in, width / ss_x, height / ss_y, file_stride / ss_x,
buffer_stride / ss_x);
}

void write_yuv(std::ofstream &out, char *mem, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride, unsigned int ss_x, unsigned int ss_y)
{
// Y
write_plane(out, mem, width, height, file_stride, buffer_stride);
// U
mem += buffer_stride * height;
write_plane(out, mem, width / ss_x, height / ss_y, file_stride / ss_x,
buffer_stride / ss_x);
// V
mem += buffer_stride / ss_x * height / ss_y;
write_plane(out, mem, width / ss_x, height / ss_y, file_stride / ss_x,
buffer_stride / ss_x);
}

void read_yuv420(char *mem, std::ifstream &in, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
read_yuv(mem, in, width, height, file_stride, buffer_stride, 2, 2);
}

void read_yuv422p(char *mem, std::ifstream &in, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
read_yuv(mem, in, width, height, file_stride, buffer_stride, 2, 1);
}

void read_yuv422i(char *mem, std::ifstream &in, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
read_plane(mem, in, width * 2, height, file_stride, buffer_stride);
}

void write_yuv420(std::ofstream &out, char *mem, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
write_yuv(out, mem, width, height, file_stride, buffer_stride, 2, 2);
}

void write_yuv422p(std::ofstream &out, char *mem, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
write_yuv(out, mem, width, height, file_stride, buffer_stride, 2, 1);
}

void write_yuv422i(std::ofstream &out, char *mem, unsigned int width, unsigned int height, unsigned int file_stride,
unsigned int buffer_stride)
{
write_plane(out, mem, width * 2, height, file_stride, buffer_stride);
}

struct FormatFuncs
{
std::function<void(char *, std::ifstream &, unsigned int, unsigned int, unsigned int, unsigned int)> read_file;
std::function<void(std::ofstream &, char *, unsigned int, unsigned int, unsigned int, unsigned int)> write_file;
};

const std::map<std::string, FormatFuncs> Formats =
{
{ "RGB888", { read_rgb, write_rgb } },
{ "YUV420", { read_yuv420, write_yuv420 } },
{ "YUV422", { read_yuv422p, write_yuv422p } },
{ "YUYV", { read_yuv422p, write_yuv422i } },
{ "UYVY", { read_yuv422i, write_yuv422i } },
};

struct Format
{
unsigned int width;
unsigned int height;
unsigned int stride;
std::string format;
};

Format parse_format(const std::string &fmt)
{
Format format;
size_t pos = 0, start = 0;

pos = fmt.find(':', start);
if (pos == std::string::npos)
return {};
format.width = std::stoi(fmt.substr(start, pos - start));
start = pos + 1;

pos = fmt.find(':', start);
if (pos == std::string::npos)
return {};
format.height = std::stoi(fmt.substr(start, pos - start));
start = pos + 1;

pos = fmt.find(':', start);
if (pos == std::string::npos)
return {};
format.stride = std::stoi(fmt.substr(start, pos - start));
start = pos + 1;

format.format = fmt.substr(start);

return format;
}

int main(int argc, char *argv[])
{
libpisp::MediaDevice devices;

libpisp::logging_init();

cxxopts::Options options(argv[0], "libpisp image converter");

options.add_options()
("input", "Input file", cxxopts::value<std::string>())
("output", "Output file", cxxopts::value<std::string>())
("input-format", "Input format in the form width:height:stride:format\n"
"Bit-depth is assumed to be 8-bit.",cxxopts::value<std::string>()->default_value(""))
("output-format", "Output format in the form width:height:stride:format\n"
"Bit-depth is assumed to be 8-bit.", cxxopts::value<std::string>()->default_value(""))
("f,formats", "List available format strings that can be used")
("l,list", "Enumerate the media device nodes")
("h,help", "Print usage")
;

options.parse_positional({ "input", "output" });
options.positional_help("<input file> <output file>");
options.set_width(120);

auto args = options.parse(argc, argv);

if (args.count("help"))
{
std::cerr << options.help() << std::endl;
exit(0);
}
else if (args.count("list"))
{
std::cerr << devices.List() << std::endl;
exit(0);
}
else if (args.count("formats"))
{
for (const auto &f : Formats)
std::cerr << f.first << " ";
std::cerr << std::endl;
exit(0);
}

std::string media_dev = devices.Acquire();
if (media_dev.empty())
{
std::cerr << "Unable to acquire any pisp_be device!" << std::endl;
exit(-1);
}

libpisp::BackendDevice backend_device { media_dev };
std::cerr << "Acquired device " << media_dev << std::endl;

auto in_file = parse_format(args["input-format"].as<std::string>());
if (!Formats.count(in_file.format))
{
std::cerr << "Invalid input-format specified" << std::endl;
exit(-1);
}

auto out_file = parse_format(args["output-format"].as<std::string>());
if (!Formats.count(out_file.format))
{
std::cerr << "Invalid output-format specified" << std::endl;
exit(-1);
}

const std::vector<libpisp::PiSPVariant> variants = libpisp::get_variants();
libpisp::BackEnd be(libpisp::BackEnd::Config({}), variants[0]);

pisp_be_global_config global;
be.GetGlobal(global);
global.bayer_enables = 0;
global.rgb_enables = PISP_BE_RGB_ENABLE_INPUT + PISP_BE_RGB_ENABLE_OUTPUT0;

pisp_image_format_config i = {};
i.width = in_file.width;
i.height = in_file.height;
i.format = libpisp::get_pisp_image_format(in_file.format);
assert(i.format);
libpisp::compute_optimal_stride(i);
be.SetInputFormat(i);

pisp_be_output_format_config o = {};
o.image.width = out_file.width;
o.image.height = out_file.height;
o.image.format = libpisp::get_pisp_image_format(out_file.format);
assert(o.image.format);
libpisp::compute_optimal_stride(o.image, true);
be.SetOutputFormat(0, o);

if (!out_file.stride)
out_file.stride = o.image.stride;

if (in_file.format != "RGB888")
{
pisp_be_ccm_config csc;
be.InitialiseYcbcrInverse(csc, "jpeg");
be.SetCcm(csc);
global.rgb_enables += PISP_BE_RGB_ENABLE_CCM;
}

if (out_file.format != "RGB888")
{
pisp_be_ccm_config csc;
be.InitialiseYcbcr(csc, "jpeg");
be.SetCsc(0, csc);
global.rgb_enables += PISP_BE_RGB_ENABLE_CSC0;
}

be.SetGlobal(global);
be.SetCrop(0, { 0, 0, i.width, i.height });
be.SetSmartResize(0, { o.image.width, o.image.height });

pisp_be_tiles_config config = {};
be.Prepare(&config);

backend_device.Setup(config);
auto buffers = backend_device.GetBuffers();

std::string input_filename = args["input"].as<std::string>();
std::ifstream in(input_filename, std::ios::binary);
if (!in.is_open())
{
std::cerr << "Unable to open " << input_filename << std::endl;
exit(-1);
}

std::cerr << "Reading " << input_filename << " "
<< in_file.width << ":" << in_file.height << ":" << in_file.stride << ":" << in_file.format << std::endl;

Formats.at(in_file.format)
.read_file((char *)buffers["pispbe-input"].mem, in, in_file.width, in_file.height, in_file.stride,
i.stride);
in.close();

int ret = backend_device.Run();
if (ret)
{
std::cerr << "Job run error!" << std::endl;
exit(-1);
}

std::string output_file = args["output"].as<std::string>();
std::ofstream out(output_file, std::ios::binary);
if (!out.is_open())
{
std::cerr << "Unable to open " << output_file << std::endl;
exit(-1);
}

Formats.at(out_file.format)
.write_file(out, (char *)buffers["pispbe-output0"].mem, out_file.width, out_file.height, out_file.stride,
o.image.stride);
out.close();

std::cerr << "Writing " << output_file << " "
<< out_file.width << ":" << out_file.height << ":" << out_file.stride << ":" << out_file.format << std::endl;

return 0;
}
8 changes: 8 additions & 0 deletions src/examples/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: CC0-1.0
# Copyright (C) 2024, Raspberry Pi Ltd

opts_dep = dependency('cxxopts', fallback : ['cxxopts', 'cxxopts_dep'])

libpisp_convert = executable('convert', files('convert.cpp'),
dependencies: [libpisp_dep, opts_dep],
install : false)
Loading

0 comments on commit 040e8cf

Please sign in to comment.