1 #include <iostream>
2 #include <memory>
3 #include "common/except.h"
4 #include "common/make_unique.h"
5 #include "common/pixel.h"
6 #include "unresize/unresize.h"
7
8 #include "apps.h"
9 #include "argparse.h"
10 #include "frame.h"
11 #include "pair_filter.h"
12 #include "timer.h"
13 #include "utils.h"
14
15 namespace {
16
is_set_pixel_format(const zimg::PixelFormat & format)17 constexpr bool is_set_pixel_format(const zimg::PixelFormat &format) noexcept
18 {
19 return format != zimg::PixelFormat{};
20 }
21
22
23 struct Arguments {
24 const char *inpath;
25 const char *outpath;
26 unsigned width_in;
27 unsigned height_in;
28 unsigned width_out;
29 unsigned height_out;
30 double shift_w;
31 double shift_h;
32 zimg::PixelFormat working_format;
33 const char *visualise_path;
34 unsigned times;
35 zimg::CPUClass cpu;
36 };
37
38 const ArgparseOption program_switches[] = {
39 { OPTION_UINT, "w", "width-in", offsetof(Arguments, width_in), nullptr, "image width" },
40 { OPTION_UINT, "h", "height-in", offsetof(Arguments, height_in), nullptr, "image height"},
41 { OPTION_FLOAT, nullptr, "shift-w", offsetof(Arguments, shift_w), nullptr, "subpixel shift" },
42 { OPTION_FLOAT, nullptr, "shift-h", offsetof(Arguments, shift_h), nullptr, "subpixel shift" },
43 { OPTION_USER1, nullptr, "format", offsetof(Arguments, working_format), arg_decode_pixfmt, "working pixel format" },
44 { OPTION_STRING, nullptr, "visualise", offsetof(Arguments, visualise_path), nullptr, "path to BMP file for visualisation" },
45 { OPTION_UINT, nullptr, "times", offsetof(Arguments, times), nullptr, "number of benchmark cycles" },
46 { OPTION_USER1, nullptr, "cpu", offsetof(Arguments, cpu), arg_decode_cpu, "select CPU type" },
47 { OPTION_NULL }
48 };
49
50 const ArgparseOption program_positional[] = {
51 { OPTION_STRING, nullptr, "inpath", offsetof(Arguments, inpath), nullptr, "input path specifier" },
52 { OPTION_STRING, nullptr, "outpath", offsetof(Arguments, outpath), nullptr, "output path specifier" },
53 { OPTION_UINT, nullptr, "width-out", offsetof(Arguments, width_out), nullptr, "output width" },
54 { OPTION_UINT, nullptr, "height-out", offsetof(Arguments, height_out), nullptr, "output height" },
55 { OPTION_NULL }
56 };
57
58 const ArgparseCommandLine program_def = {
59 program_switches,
60 program_positional,
61 "unresize",
62 "unresize images",
63 PIXFMT_SPECIFIER_HELP_STR "\n" PATH_SPECIFIER_HELP_STR
64 };
65
ns_per_sample(const ImageFrame & frame,double seconds)66 double ns_per_sample(const ImageFrame &frame, double seconds)
67 {
68 double samples = static_cast<double>(static_cast<size_t>(frame.width()) * frame.height() * frame.planes());
69 return seconds * 1e9 / samples;
70 }
71
execute(const zimg::graph::ImageFilter * filter,const ImageFrame * src_frame,ImageFrame * dst_frame,unsigned times)72 void execute(const zimg::graph::ImageFilter *filter, const ImageFrame *src_frame, ImageFrame *dst_frame, unsigned times)
73 {
74 auto results = measure_benchmark(times, FilterExecutor{ filter, nullptr, src_frame, dst_frame }, [](unsigned n, double d)
75 {
76 std::cout << '#' << n << ": " << d << '\n';
77 });
78
79 std::cout << "avg: " << results.first << " (" << ns_per_sample(*dst_frame, results.first) << " ns/sample)\n";
80 std::cout << "min: " << results.second << " (" << ns_per_sample(*dst_frame, results.second) << " ns/sample)\n";
81 }
82
83 } // namespace
84
85
unresize_main(int argc,char ** argv)86 int unresize_main(int argc, char **argv)
87 {
88 Arguments args{};
89 int ret;
90
91 args.times = 1;
92
93 if ((ret = argparse_parse(&program_def, &args, argc, argv)) < 0)
94 return ret == ARGPARSE_HELP_MESSAGE ? 0 : ret;
95
96 if (!is_set_pixel_format(args.working_format))
97 args.working_format = zimg::PixelType::FLOAT;
98
99 try {
100 ImageFrame src_frame = imageframe::read(args.inpath, "i444s", args.width_in, args.height_in, args.working_format.type, false);
101
102 if (src_frame.subsample_w() || src_frame.subsample_h())
103 throw std::runtime_error{ "can only unresize greyscale/4:4:4 images" };
104
105 ImageFrame dst_frame{ args.width_out, args.height_out, src_frame.pixel_type(), src_frame.planes(), src_frame.is_yuv() };
106
107 auto filter_pair = zimg::unresize::UnresizeConversion{ src_frame.width(), src_frame.height(), src_frame.pixel_type() }
108 .set_orig_width(dst_frame.width())
109 .set_orig_height(dst_frame.height())
110 .set_shift_w(args.shift_w)
111 .set_shift_h(args.shift_h)
112 .set_cpu(args.cpu)
113 .create();
114
115 if (filter_pair.second)
116 filter_pair.first = ztd::make_unique<PairFilter>(std::move(filter_pair.first), std::move(filter_pair.second));
117
118 execute(filter_pair.first.get(), &src_frame, &dst_frame, args.times);
119
120 if (args.visualise_path)
121 imageframe::write(dst_frame, args.visualise_path, "bmp", true);
122
123 imageframe::write(dst_frame, args.outpath, "i444s", false);
124 } catch (const zimg::error::Exception &e) {
125 std::cerr << e.what() << '\n';
126 return 2;
127 } catch (const std::exception &e) {
128 std::cerr << e.what() << '\n';
129 return 2;
130 }
131
132 return 0;
133 }
134