Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various changes + helper utils #35

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading