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