-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
helpers: examples: Add helpers and an example converter utility
Signed-off-by: Naushir Patuck <[email protected]>
- Loading branch information
Showing
12 changed files
with
1,208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.