Skip to content

Commit e03281d

Browse files
committed
helpers: examples: Add helpers and an example converter utility
Signed-off-by: Naushir Patuck <[email protected]>
1 parent 00e568d commit e03281d

File tree

14 files changed

+1292
-3
lines changed

14 files changed

+1292
-3
lines changed

meson_options.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
# Copyright (C) 2023, Raspberry Pi Ltd
33

44
option('logging', type : 'feature', value : 'auto')
5+
option('examples', type : 'boolean', value : false)

src/examples/convert.cpp

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
2+
/* SPDX-License-Identifier: BSD-2-Clause */
3+
/*
4+
* Copyright (C) 2025 Raspberry Pi Ltd
5+
*
6+
* convert.cpp - libpisp simple image converter example
7+
*/
8+
9+
#include <array>
10+
#include <assert.h>
11+
#include <fstream>
12+
#include <functional>
13+
#include <iostream>
14+
#include <map>
15+
#include <stdint.h>
16+
#include <string>
17+
18+
#include <cxxopts.hpp>
19+
20+
#include "helpers/backend_device.hpp"
21+
#include "helpers/media_device.hpp"
22+
23+
#include "libpisp/backend/backend.hpp"
24+
#include "libpisp/common/logging.hpp"
25+
#include "libpisp/common/utils.hpp"
26+
#include "libpisp/variants/variant.hpp"
27+
28+
void read_plane(uint8_t *mem, std::ifstream &in, unsigned int width, unsigned int height, unsigned int file_stride,
29+
unsigned int buffer_stride)
30+
{
31+
width = std::min(width, file_stride);
32+
33+
for (unsigned int y = 0; y < height; y++)
34+
{
35+
in.read((char *)mem + y * buffer_stride, width);
36+
in.seekg(file_stride - width, std::ios_base::cur);
37+
}
38+
}
39+
40+
void write_plane(std::ofstream &out, uint8_t *mem, unsigned int width, unsigned int height, unsigned int file_stride,
41+
unsigned int buffer_stride)
42+
{
43+
width = std::min(width, file_stride);
44+
45+
for (unsigned int y = 0; y < height; y++)
46+
{
47+
out.write((char *)mem + y * buffer_stride, width);
48+
for (unsigned int i = 0; i < file_stride - width; i++)
49+
out.put(0);
50+
}
51+
}
52+
53+
void read_rgb888(std::array<uint8_t *, 3> &mem, std::ifstream &in, unsigned int width, unsigned int height,
54+
unsigned int file_stride, unsigned int buffer_stride)
55+
{
56+
read_plane((uint8_t *)mem[0], in, width * 3, height, file_stride, buffer_stride);
57+
}
58+
59+
void write_rgb888(std::ofstream &out, std::array<uint8_t *, 3> &mem, unsigned int width, unsigned int height,
60+
unsigned int file_stride, unsigned int buffer_stride)
61+
{
62+
write_plane(out, (uint8_t *)mem[0], width * 3, height, file_stride, buffer_stride);
63+
}
64+
65+
void read_yuv(std::array<uint8_t *, 3> &mem, std::ifstream &in, unsigned int width, unsigned int height,
66+
unsigned int file_stride, unsigned int buffer_stride, unsigned int ss_x, unsigned int ss_y)
67+
{
68+
uint8_t *dst = mem[0];
69+
// Y
70+
read_plane(dst, in, width, height, file_stride, buffer_stride);
71+
// U
72+
dst = mem[1] ? mem[1] : dst + buffer_stride * height;
73+
read_plane(dst, in, width / ss_x, height / ss_y, file_stride / ss_x, buffer_stride / ss_x);
74+
// V
75+
dst = mem[2] ? mem[2] : dst + buffer_stride / ss_x * height / ss_y;
76+
read_plane(dst, in, width / ss_x, height / ss_y, file_stride / ss_x, buffer_stride / ss_x);
77+
}
78+
79+
void write_yuv(std::ofstream &out, std::array<uint8_t *, 3> &mem, unsigned int width, unsigned int height,
80+
unsigned int file_stride, unsigned int buffer_stride, unsigned int ss_x, unsigned int ss_y)
81+
{
82+
uint8_t *src = mem[0];
83+
// Y
84+
write_plane(out, src, width, height, file_stride, buffer_stride);
85+
// U
86+
src = mem[1] ? mem[1] : src + buffer_stride * height;
87+
write_plane(out, src, width / ss_x, height / ss_y, file_stride / ss_x, buffer_stride / ss_x);
88+
// V
89+
src = mem[2] ? mem[2] : src + buffer_stride / ss_x * height / ss_y;
90+
write_plane(out, src, width / ss_x, height / ss_y, file_stride / ss_x, buffer_stride / ss_x);
91+
}
92+
93+
void read_yuv420(std::array<uint8_t *, 3> &mem, std::ifstream &in, unsigned int width, unsigned int height,
94+
unsigned int file_stride, unsigned int buffer_stride)
95+
{
96+
read_yuv(mem, in, width, height, file_stride, buffer_stride, 2, 2);
97+
}
98+
99+
void read_yuv422p(std::array<uint8_t *, 3> &mem, std::ifstream &in, unsigned int width, unsigned int height,
100+
unsigned int file_stride, unsigned int buffer_stride)
101+
{
102+
read_yuv(mem, in, width, height, file_stride, buffer_stride, 2, 1);
103+
}
104+
105+
void read_yuv444p(std::array<uint8_t *, 3> &mem, std::ifstream &in, unsigned int width, unsigned int height,
106+
unsigned int file_stride, unsigned int buffer_stride)
107+
{
108+
read_yuv(mem, in, width, height, file_stride, buffer_stride, 1, 1);
109+
}
110+
111+
void read_yuv422i(std::array<uint8_t *, 3> &mem, std::ifstream &in, unsigned int width, unsigned int height,
112+
unsigned int file_stride, unsigned int buffer_stride)
113+
{
114+
read_plane(mem[0], in, width * 2, height, file_stride, buffer_stride);
115+
}
116+
117+
void write_yuv420(std::ofstream &out, std::array<uint8_t *, 3> &mem, unsigned int width, unsigned int height,
118+
unsigned int file_stride, unsigned int buffer_stride)
119+
{
120+
write_yuv(out, mem, width, height, file_stride, buffer_stride, 2, 2);
121+
}
122+
123+
void write_yuv422p(std::ofstream &out, std::array<uint8_t *, 3> &mem, unsigned int width, unsigned int height,
124+
unsigned int file_stride, unsigned int buffer_stride)
125+
{
126+
write_yuv(out, mem, width, height, file_stride, buffer_stride, 2, 1);
127+
}
128+
129+
void write_yuv444p(std::ofstream &out, std::array<uint8_t *, 3> &mem, unsigned int width, unsigned int height,
130+
unsigned int file_stride, unsigned int buffer_stride)
131+
{
132+
write_yuv(out, mem, width, height, file_stride, buffer_stride, 1, 1);
133+
}
134+
135+
void write_yuv422i(std::ofstream &out, std::array<uint8_t *, 3> &mem, unsigned int width, unsigned int height,
136+
unsigned int file_stride, unsigned int buffer_stride)
137+
{
138+
write_plane(out, mem[0], width * 2, height, file_stride, buffer_stride);
139+
}
140+
141+
struct FormatFuncs
142+
{
143+
std::function<void(std::array<uint8_t *, 3> &, std::ifstream &, unsigned int, unsigned int, unsigned int,
144+
unsigned int)> read_file;
145+
std::function<void(std::ofstream &, std::array<uint8_t *, 3> &, unsigned int, unsigned int, unsigned int,
146+
unsigned int)> write_file;
147+
};
148+
149+
const std::map<std::string, FormatFuncs> Formats =
150+
{
151+
{ "RGB888", { read_rgb888, write_rgb888 } },
152+
{ "YUV420P", { read_yuv420, write_yuv420 } },
153+
{ "YUV422P", { read_yuv422p, write_yuv422p } },
154+
{ "YUV444P", { read_yuv444p, write_yuv444p } },
155+
{ "YUYV", { read_yuv422i, write_yuv422i } },
156+
{ "UYVY", { read_yuv422i, write_yuv422i } },
157+
};
158+
159+
struct Format
160+
{
161+
unsigned int width;
162+
unsigned int height;
163+
unsigned int stride;
164+
std::string format;
165+
};
166+
167+
Format parse_format(const std::string &fmt)
168+
{
169+
Format format;
170+
size_t pos = 0, start = 0;
171+
172+
pos = fmt.find(':', start);
173+
if (pos == std::string::npos)
174+
return {};
175+
format.width = std::stoi(fmt.substr(start, pos - start));
176+
start = pos + 1;
177+
178+
pos = fmt.find(':', start);
179+
if (pos == std::string::npos)
180+
return {};
181+
format.height = std::stoi(fmt.substr(start, pos - start));
182+
start = pos + 1;
183+
184+
pos = fmt.find(':', start);
185+
if (pos == std::string::npos)
186+
return {};
187+
format.stride = std::stoi(fmt.substr(start, pos - start));
188+
start = pos + 1;
189+
190+
format.format = fmt.substr(start);
191+
192+
return format;
193+
}
194+
195+
int main(int argc, char *argv[])
196+
{
197+
libpisp::MediaDevice devices;
198+
199+
libpisp::logging_init();
200+
201+
cxxopts::Options options(argv[0], "libpisp image converter");
202+
203+
options.add_options()
204+
("input", "Input file", cxxopts::value<std::string>())
205+
("output", "Output file", cxxopts::value<std::string>())
206+
("input-format", "Input format in the form width:height:stride:format\n"
207+
"Bit-depth is assumed to be 8-bit.",cxxopts::value<std::string>()->default_value(""))
208+
("output-format", "Output format in the form width:height:stride:format\n"
209+
"Bit-depth is assumed to be 8-bit.", cxxopts::value<std::string>()->default_value(""))
210+
("f,formats", "List available format strings that can be used")
211+
("l,list", "Enumerate the media device nodes")
212+
("h,help", "Print usage")
213+
;
214+
215+
options.parse_positional({ "input", "output" });
216+
options.positional_help("<input file> <output file>");
217+
options.set_width(120);
218+
219+
auto args = options.parse(argc, argv);
220+
221+
if (args.count("help"))
222+
{
223+
std::cerr << options.help() << std::endl;
224+
exit(0);
225+
}
226+
else if (args.count("list"))
227+
{
228+
std::cerr << devices.List() << std::endl;
229+
exit(0);
230+
}
231+
else if (args.count("formats"))
232+
{
233+
for (const auto &f : Formats)
234+
std::cerr << f.first << " ";
235+
std::cerr << std::endl;
236+
exit(0);
237+
}
238+
239+
std::string media_dev = devices.Acquire();
240+
if (media_dev.empty())
241+
{
242+
std::cerr << "Unable to acquire any pisp_be device!" << std::endl;
243+
exit(-1);
244+
}
245+
246+
libpisp::BackendDevice backend_device { media_dev };
247+
std::cerr << "Acquired device " << media_dev << std::endl;
248+
249+
auto in_file = parse_format(args["input-format"].as<std::string>());
250+
if (!Formats.count(in_file.format))
251+
{
252+
std::cerr << "Invalid input-format specified" << std::endl;
253+
exit(-1);
254+
}
255+
256+
auto out_file = parse_format(args["output-format"].as<std::string>());
257+
if (!Formats.count(out_file.format))
258+
{
259+
std::cerr << "Invalid output-format specified" << std::endl;
260+
exit(-1);
261+
}
262+
263+
const std::vector<libpisp::PiSPVariant> variants = libpisp::get_variants();
264+
libpisp::BackEnd be(libpisp::BackEnd::Config({}), variants[0]);
265+
266+
pisp_be_global_config global;
267+
be.GetGlobal(global);
268+
global.bayer_enables = 0;
269+
global.rgb_enables = PISP_BE_RGB_ENABLE_INPUT + PISP_BE_RGB_ENABLE_OUTPUT0;
270+
271+
pisp_image_format_config i = {};
272+
i.width = in_file.width;
273+
i.height = in_file.height;
274+
i.format = libpisp::get_pisp_image_format(in_file.format);
275+
assert(i.format);
276+
libpisp::compute_optimal_stride(i);
277+
be.SetInputFormat(i);
278+
279+
pisp_be_output_format_config o = {};
280+
o.image.width = out_file.width;
281+
o.image.height = out_file.height;
282+
o.image.format = libpisp::get_pisp_image_format(out_file.format);
283+
assert(o.image.format);
284+
libpisp::compute_optimal_stride(o.image, true);
285+
be.SetOutputFormat(0, o);
286+
287+
if (!out_file.stride)
288+
out_file.stride = o.image.stride;
289+
290+
if (in_file.format != "RGB888")
291+
{
292+
pisp_be_ccm_config csc;
293+
be.InitialiseYcbcrInverse(csc, "jpeg");
294+
be.SetCcm(csc);
295+
global.rgb_enables += PISP_BE_RGB_ENABLE_CCM;
296+
}
297+
298+
if (out_file.format != "RGB888")
299+
{
300+
pisp_be_ccm_config csc;
301+
be.InitialiseYcbcr(csc, "jpeg");
302+
be.SetCsc(0, csc);
303+
global.rgb_enables += PISP_BE_RGB_ENABLE_CSC0;
304+
}
305+
306+
be.SetGlobal(global);
307+
be.SetCrop(0, { 0, 0, i.width, i.height });
308+
be.SetSmartResize(0, { o.image.width, o.image.height });
309+
310+
pisp_be_tiles_config config = {};
311+
be.Prepare(&config);
312+
313+
backend_device.Setup(config);
314+
auto buffers = backend_device.GetBuffers();
315+
316+
std::string input_filename = args["input"].as<std::string>();
317+
std::ifstream in(input_filename, std::ios::binary);
318+
if (!in.is_open())
319+
{
320+
std::cerr << "Unable to open " << input_filename << std::endl;
321+
exit(-1);
322+
}
323+
324+
std::cerr << "Reading " << input_filename << " "
325+
<< in_file.width << ":" << in_file.height << ":" << in_file.stride << ":" << in_file.format << std::endl;
326+
327+
Formats.at(in_file.format)
328+
.read_file(buffers["pispbe-input"].mem, in, in_file.width, in_file.height, in_file.stride,
329+
i.stride);
330+
in.close();
331+
332+
int ret = backend_device.Run();
333+
if (ret)
334+
{
335+
std::cerr << "Job run error!" << std::endl;
336+
exit(-1);
337+
}
338+
339+
std::string output_file = args["output"].as<std::string>();
340+
std::ofstream out(output_file, std::ios::binary);
341+
if (!out.is_open())
342+
{
343+
std::cerr << "Unable to open " << output_file << std::endl;
344+
exit(-1);
345+
}
346+
347+
Formats.at(out_file.format)
348+
.write_file(out, buffers["pispbe-output0"].mem, out_file.width, out_file.height, out_file.stride,
349+
o.image.stride);
350+
out.close();
351+
352+
std::cerr << "Writing " << output_file << " "
353+
<< out_file.width << ":" << out_file.height << ":" << out_file.stride << ":" << out_file.format << std::endl;
354+
355+
return 0;
356+
}

src/examples/meson.build

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: CC0-1.0
2+
# Copyright (C) 2025, Raspberry Pi Ltd
3+
4+
opts_dep = dependency('cxxopts', fallback : ['cxxopts', 'cxxopts_dep'])
5+
6+
libpisp_convert = executable('convert', files('convert.cpp'),
7+
dependencies: [libpisp_dep, opts_dep],
8+
install : false)

0 commit comments

Comments
 (0)