blob: 9c304d90a1c45ed01358a40367a1d8a10ce098a3 [file] [log] [blame]
Neil Birkbeck0f0b3702018-03-15 17:14:16 -07001/*
2 * Copyright (c) 2018, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 */
11
12/*!\file
13 * \brief This is an sample binary to create noise params from input video.
14 *
15 * To allow for external denoising applications, this sample binary illustrates
16 * how to create a film grain table (film grain params as a function of time)
17 * from an input video and its corresponding denoised source.
18 *
19 * The --output-grain-table file can be passed as input to the encoder (in
20 * aomenc this is done through the "--film-grain-table" parameter).
21 */
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "../args.h"
27#include "../tools_common.h"
28#include "../video_writer.h"
29#include "aom/aom_encoder.h"
30#include "aom_dsp/aom_dsp_common.h"
31#include "aom_dsp/noise_model.h"
32#include "aom_dsp/noise_util.h"
33#include "aom_dsp/grain_table.h"
34#include "aom_mem/aom_mem.h"
35
36static const char *exec_name;
37
38void usage_exit(void) {
39 fprintf(stderr,
40 "Usage: %s --input=<input> --input-denoised=<denoised> "
41 "--output-grain-table=<outfile> "
42 "See comments in noise_model.c for more information.\n",
43 exec_name);
44 exit(EXIT_FAILURE);
45}
46
47static const arg_def_t help =
48 ARG_DEF(NULL, "help", 0, "Show usage options and exit");
49static const arg_def_t width_arg =
50 ARG_DEF("w", "width", 1, "Input width (if rawvideo)");
51static const arg_def_t height_arg =
52 ARG_DEF("h", "height", 1, "Input height (if rawvideo)");
53static const arg_def_t skip_frames_arg =
54 ARG_DEF("s", "skip-frames", 1, "Number of frames to skip (default = 1)");
55static const arg_def_t fps_arg = ARG_DEF(NULL, "fps", 1, "Frame rate");
56static const arg_def_t input_arg = ARG_DEF("-i", "input", 1, "Input filename");
57static const arg_def_t output_grain_table_arg =
58 ARG_DEF("n", "output-grain-table", 1, "Output noise file");
59static const arg_def_t input_denoised_arg =
60 ARG_DEF("d", "input-denoised", 1, "Input denoised filename (YUV) only");
61static const arg_def_t block_size_arg =
62 ARG_DEF("b", "block_size", 1, "Block size");
63static const arg_def_t use_i420 =
64 ARG_DEF(NULL, "i420", 0, "Input file (and denoised) is I420 (default)");
65static const arg_def_t use_i422 =
66 ARG_DEF(NULL, "i422", 0, "Input file (and denoised) is I422");
67static const arg_def_t use_i444 =
68 ARG_DEF(NULL, "i444", 0, "Input file (and denoised) is I444");
69
70typedef struct {
71 int width;
72 int height;
73 struct aom_rational fps;
74 const char *input;
75 const char *input_denoised;
76 const char *output_grain_table;
77 int img_fmt;
78 int block_size;
79 int run_flat_block_finder;
80 int force_flat_psd;
81 int skip_frames;
82} noise_model_args_t;
83
84void parse_args(noise_model_args_t *noise_args, int *argc, char **argv) {
85 struct arg arg;
86 static const arg_def_t *main_args[] = { &help,
87 &input_arg,
88 &fps_arg,
89 &width_arg,
90 &height_arg,
91 &block_size_arg,
92 &output_grain_table_arg,
93 &input_denoised_arg,
94 &use_i420,
95 &use_i422,
96 &use_i444,
97 NULL };
98 for (int argi = *argc + 1; *argv; argi++, argv++) {
99 if (arg_match(&arg, &help, argv)) {
100 fprintf(stdout, "\nOptions:\n");
101 arg_show_usage(stdout, main_args);
102 exit(0);
103 } else if (arg_match(&arg, &width_arg, argv)) {
104 noise_args->width = atoi(arg.val);
105 } else if (arg_match(&arg, &height_arg, argv)) {
106 noise_args->height = atoi(arg.val);
107 } else if (arg_match(&arg, &input_arg, argv)) {
108 noise_args->input = arg.val;
109 } else if (arg_match(&arg, &input_denoised_arg, argv)) {
110 noise_args->input_denoised = arg.val;
111 } else if (arg_match(&arg, &output_grain_table_arg, argv)) {
112 noise_args->output_grain_table = arg.val;
113 } else if (arg_match(&arg, &block_size_arg, argv)) {
114 noise_args->block_size = atoi(arg.val);
115 } else if (arg_match(&arg, &fps_arg, argv)) {
116 noise_args->fps = arg_parse_rational(&arg);
117 } else if (arg_match(&arg, &use_i420, argv)) {
118 noise_args->img_fmt = AOM_IMG_FMT_I420;
119 } else if (arg_match(&arg, &use_i422, argv)) {
120 noise_args->img_fmt = AOM_IMG_FMT_I422;
121 } else if (arg_match(&arg, &use_i444, argv)) {
122 noise_args->img_fmt = AOM_IMG_FMT_I444;
123 } else if (arg_match(&arg, &skip_frames_arg, argv)) {
124 noise_args->skip_frames = atoi(arg.val);
125 } else {
126 fprintf(stdout, "Unknown arg: %s\n\nUsage:\n", *argv);
127 arg_show_usage(stdout, main_args);
128 exit(0);
129 }
130 }
131}
132
133int main(int argc, char *argv[]) {
134 noise_model_args_t args = { 0, 0, { 1, 25 }, 0, 0, 0, AOM_IMG_FMT_I420,
135 32, 0, 0, 1 };
136 aom_image_t raw, denoised;
137 FILE *infile = NULL;
138 AvxVideoInfo info;
139
140 memset(&info, 0, sizeof(info));
141
142 exec_name = argv[0];
143 parse_args(&args, &argc, argv + 1);
144
145 info.frame_width = args.width;
146 info.frame_height = args.height;
147 info.time_base.numerator = args.fps.den;
148 info.time_base.denominator = args.fps.num;
149
150 if (info.frame_width <= 0 || info.frame_height <= 0 ||
151 (info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
152 die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
153 }
154 if (!aom_img_alloc(&raw, args.img_fmt, info.frame_width, info.frame_height,
155 1)) {
156 die("Failed to allocate image.");
157 }
158 if (!aom_img_alloc(&denoised, args.img_fmt, info.frame_width,
159 info.frame_height, 1)) {
160 die("Failed to allocate image.");
161 }
162 infile = fopen(args.input, "r");
163 if (!infile) {
164 die("Failed to open input file:", args.input);
165 }
166 const int block_size = args.block_size;
167 aom_flat_block_finder_t block_finder;
168 aom_flat_block_finder_init(&block_finder, block_size);
169
170 const int num_blocks_w = (info.frame_width + block_size - 1) / block_size;
171 const int num_blocks_h = (info.frame_height + block_size - 1) / block_size;
172 uint8_t *flat_blocks = (uint8_t *)aom_malloc(num_blocks_w * num_blocks_h);
173 aom_noise_model_t noise_model;
174 aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 3 };
175 aom_noise_model_init(&noise_model, params);
176
177 FILE *denoised_file = 0;
178 if (args.input_denoised) {
179 denoised_file = fopen(args.input_denoised, "rb");
180 if (!denoised_file)
181 die("Unable to open input_denoised: %s", args.input_denoised);
182 } else {
183 die("--input-denoised file must be specified");
184 }
Yaowu Xuadbe7b12018-03-27 10:34:46 -0700185 aom_film_grain_table_t grain_table = { 0, 0 };
Neil Birkbeck0f0b3702018-03-15 17:14:16 -0700186
187 int64_t prev_timestamp = 0;
188 int frame_count = 0;
189 while (aom_img_read(&raw, infile)) {
190 if (args.input_denoised) {
191 if (!aom_img_read(&denoised, denoised_file)) {
192 die("Unable to read input denoised file");
193 }
194 }
195 if (frame_count % args.skip_frames == 0) {
196 int num_flat_blocks = num_blocks_w * num_blocks_h;
197 memset(flat_blocks, 1, num_flat_blocks);
198 if (args.run_flat_block_finder) {
199 memset(flat_blocks, 0, num_flat_blocks);
200 num_flat_blocks = aom_flat_block_finder_run(
201 &block_finder, raw.planes[0], info.frame_width, info.frame_height,
202 info.frame_width, flat_blocks);
203 fprintf(stdout, "Num flat blocks %d\n", num_flat_blocks);
204 }
205
206 const uint8_t *planes[3] = { raw.planes[0], raw.planes[1],
207 raw.planes[2] };
208 uint8_t *denoised_planes[3] = { denoised.planes[0], denoised.planes[1],
209 denoised.planes[2] };
210 int strides[3] = { raw.stride[0], raw.stride[1], raw.stride[2] };
211 int chroma_sub[3] = { raw.x_chroma_shift, raw.y_chroma_shift, 0 };
212
213 fprintf(stdout, "Updating noise model...\n");
214 aom_noise_status_t status = aom_noise_model_update(
215 &noise_model, (const uint8_t *const *)planes,
216 (const uint8_t *const *)denoised_planes, info.frame_width,
217 info.frame_height, strides, chroma_sub, flat_blocks, block_size);
218
219 int64_t cur_timestamp =
220 frame_count * 10000000ULL * args.fps.den / args.fps.num;
221 if (status == AOM_NOISE_STATUS_DIFFERENT_NOISE_TYPE) {
222 fprintf(stdout,
223 "Noise type is different, updating parameters for time "
224 "[ %" PRId64 ", %" PRId64 ")\n",
225 prev_timestamp, cur_timestamp);
226 aom_film_grain_t grain;
227 aom_noise_model_get_grain_parameters(&noise_model, &grain);
228 aom_film_grain_table_append(&grain_table, prev_timestamp, cur_timestamp,
229 &grain);
230 aom_noise_model_save_latest(&noise_model);
231 prev_timestamp = cur_timestamp;
232 }
233
234 fprintf(stdout, "Done noise model update, status = %d\n", status);
235 }
236 frame_count++;
237 }
238
239 aom_film_grain_t grain;
240 aom_noise_model_get_grain_parameters(&noise_model, &grain);
241 aom_film_grain_table_append(&grain_table, prev_timestamp, INT64_MAX, &grain);
242 if (args.output_grain_table) {
243 struct aom_internal_error_info error_info;
244 if (AOM_CODEC_OK != aom_film_grain_table_write(&grain_table,
245 args.output_grain_table,
246 &error_info)) {
247 die("Unable to write output film grain table");
248 }
249 }
250 aom_film_grain_table_free(&grain_table);
251
252 if (infile) fclose(infile);
253 if (denoised_file) fclose(denoised_file);
254 aom_img_free(&raw);
255 aom_img_free(&denoised);
256
257 return EXIT_SUCCESS;
258}