Skip to content

Commit 03591d8

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

File tree

12 files changed

+1218
-0
lines changed

12 files changed

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

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) 2024, 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)