OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/tools/frame_analyzer/video_quality_analysis.h" | 11 #include "webrtc/tools/frame_analyzer/video_quality_analysis.h" |
12 | 12 |
13 #include <assert.h> | 13 #include <assert.h> |
14 #include <stdio.h> | 14 #include <stdio.h> |
15 #include <stdlib.h> | 15 #include <stdlib.h> |
16 #include <algorithm> | |
16 #include <string> | 17 #include <string> |
18 #include <map> | |
19 #include <utility> | |
17 | 20 |
18 #define STATS_LINE_LENGTH 32 | 21 #define STATS_LINE_LENGTH 32 |
19 #define Y4M_FILE_HEADER_MAX_SIZE 200 | 22 #define Y4M_FILE_HEADER_MAX_SIZE 200 |
20 #define Y4M_FRAME_DELIMITER "FRAME" | 23 #define Y4M_FRAME_DELIMITER "FRAME" |
21 #define Y4M_FRAME_HEADER_SIZE 6 | 24 #define Y4M_FRAME_HEADER_SIZE 6 |
22 | 25 |
23 namespace webrtc { | 26 namespace webrtc { |
24 namespace test { | 27 namespace test { |
25 | 28 |
26 using std::string; | 29 using std::string; |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
217 src_u_b, stride_uv, src_v_b, stride_uv, | 220 src_u_b, stride_uv, src_v_b, stride_uv, |
218 width, height); | 221 width, height); |
219 break; | 222 break; |
220 default: | 223 default: |
221 assert(false); | 224 assert(false); |
222 } | 225 } |
223 | 226 |
224 return result; | 227 return result; |
225 } | 228 } |
226 | 229 |
227 void RunAnalysis(const char* reference_file_name, const char* test_file_name, | 230 void RunAnalysis(const char* reference_file_name, |
228 const char* stats_file_name, int width, int height, | 231 const char* test_file_name, |
232 const char* stats_file_reference_name, | |
233 const char* stats_file_test_name, | |
234 int width, | |
235 int height, | |
229 ResultsContainer* results) { | 236 ResultsContainer* results) { |
230 // Check if the reference_file_name ends with "y4m". | 237 // Check if the reference_file_name ends with "y4m". |
231 bool y4m_mode = false; | 238 bool y4m_mode = false; |
232 if (std::string(reference_file_name).find("y4m") != std::string::npos) { | 239 if (std::string(reference_file_name).find("y4m") != std::string::npos) { |
233 y4m_mode = true; | 240 y4m_mode = true; |
234 } | 241 } |
235 | 242 |
236 int size = GetI420FrameSize(width, height); | 243 int size = GetI420FrameSize(width, height); |
237 FILE* stats_file = fopen(stats_file_name, "r"); | 244 FILE* stats_file_ref = fopen(stats_file_reference_name, "r"); |
245 FILE* stats_file_test = fopen(stats_file_test_name, "r"); | |
238 | 246 |
239 // String buffer for the lines in the stats file. | 247 // String buffer for the lines in the stats file. |
240 char line[STATS_LINE_LENGTH]; | 248 char line[STATS_LINE_LENGTH]; |
241 | 249 |
242 // Allocate buffers for test and reference frames. | 250 // Allocate buffers for test and reference frames. |
243 uint8_t* test_frame = new uint8_t[size]; | 251 uint8_t* test_frame = new uint8_t[size]; |
244 uint8_t* reference_frame = new uint8_t[size]; | 252 uint8_t* reference_frame = new uint8_t[size]; |
245 int previous_frame_number = -1; | 253 int previous_frame_number = -1; |
246 | 254 |
255 // Maps barcode id to the frame id for the reference video. | |
256 // In case two frames have same id, then we only save the first one. | |
phoglund
2016/12/08 09:55:44
Add: "This can happen if there are freezes in the
mandermo
2016/12/08 18:08:31
It can also happen that the camera captures two fr
phoglund
2016/12/12 12:46:20
I don't understand when you say the frames have th
| |
257 std::map<int, int> ref_barcode_to_frame; | |
247 // While there are entries in the stats file. | 258 // While there are entries in the stats file. |
248 while (GetNextStatsLine(stats_file, line)) { | 259 while (GetNextStatsLine(stats_file_ref, line)) { |
260 int extracted_ref_frame = ExtractFrameSequenceNumber(line); | |
261 int decoded_frame_number = ExtractDecodedFrameNumber(line); | |
262 | |
263 // Insert will only add if it is not in map already, so we only keep first. | |
phoglund
2016/12/08 09:55:44
nit: End this sentence at the ,
mandermo
2016/12/08 18:08:31
Done.
| |
264 ref_barcode_to_frame.insert( | |
265 std::make_pair(decoded_frame_number, extracted_ref_frame)); | |
266 } | |
267 | |
268 while (GetNextStatsLine(stats_file_test, line)) { | |
249 int extracted_test_frame = ExtractFrameSequenceNumber(line); | 269 int extracted_test_frame = ExtractFrameSequenceNumber(line); |
250 int decoded_frame_number = ExtractDecodedFrameNumber(line); | 270 int decoded_frame_number = ExtractDecodedFrameNumber(line); |
271 auto it = ref_barcode_to_frame.find(decoded_frame_number); | |
272 if (it == ref_barcode_to_frame.end()) { | |
273 // Not found in the reference video. | |
274 // TODO(mandermo) print | |
phoglund
2016/12/08 09:55:44
Yeah, print an error here and continue. You'll hav
phoglund
2016/12/12 12:46:20
Please address
| |
275 continue; | |
276 } | |
277 int extracted_ref_frame = it->second; | |
251 | 278 |
252 // If there was problem decoding the barcode in this frame or the frame has | 279 // If there was problem decoding the barcode in this frame or the frame has |
253 // been duplicated, continue. | 280 // been duplicated, continue. |
254 if (IsThereBarcodeError(line) || | 281 if (IsThereBarcodeError(line) || |
255 decoded_frame_number == previous_frame_number) { | 282 decoded_frame_number == previous_frame_number) { |
256 continue; | 283 continue; |
257 } | 284 } |
258 | 285 |
259 assert(extracted_test_frame != -1); | 286 assert(extracted_test_frame != -1); |
260 assert(decoded_frame_number != -1); | 287 assert(decoded_frame_number != -1); |
261 | 288 |
262 ExtractFrameFromYuvFile(test_file_name, width, height, extracted_test_frame, | 289 ExtractFrameFromYuvFile(test_file_name, width, height, extracted_test_frame, |
263 test_frame); | 290 test_frame); |
264 if (y4m_mode) { | 291 if (y4m_mode) { |
265 ExtractFrameFromY4mFile(reference_file_name, width, height, | 292 ExtractFrameFromY4mFile(reference_file_name, width, height, |
266 decoded_frame_number, reference_frame); | 293 extracted_ref_frame, reference_frame); |
267 } else { | 294 } else { |
268 ExtractFrameFromYuvFile(reference_file_name, width, height, | 295 ExtractFrameFromYuvFile(reference_file_name, width, height, |
269 decoded_frame_number, reference_frame); | 296 extracted_ref_frame, reference_frame); |
270 } | 297 } |
271 | 298 |
272 // Calculate the PSNR and SSIM. | 299 // Calculate the PSNR and SSIM. |
273 double result_psnr = CalculateMetrics(kPSNR, reference_frame, test_frame, | 300 double result_psnr = |
274 width, height); | 301 CalculateMetrics(kPSNR, reference_frame, test_frame, width, height); |
275 double result_ssim = CalculateMetrics(kSSIM, reference_frame, test_frame, | 302 double result_ssim = |
276 width, height); | 303 CalculateMetrics(kSSIM, reference_frame, test_frame, width, height); |
277 | 304 |
278 previous_frame_number = decoded_frame_number; | 305 previous_frame_number = decoded_frame_number; |
279 | 306 |
280 // Fill in the result struct. | 307 // Fill in the result struct. |
281 AnalysisResult result; | 308 AnalysisResult result; |
282 result.frame_number = decoded_frame_number; | 309 result.frame_number = decoded_frame_number; |
283 result.psnr_value = result_psnr; | 310 result.psnr_value = result_psnr; |
284 result.ssim_value = result_ssim; | 311 result.ssim_value = result_ssim; |
285 | 312 |
286 results->frames.push_back(result); | 313 results->frames.push_back(result); |
287 } | 314 } |
288 | 315 |
289 // Cleanup. | 316 // Cleanup. |
290 fclose(stats_file); | 317 fclose(stats_file_ref); |
318 fclose(stats_file_test); | |
291 delete[] test_frame; | 319 delete[] test_frame; |
292 delete[] reference_frame; | 320 delete[] reference_frame; |
293 } | 321 } |
294 | 322 |
295 void PrintMaxRepeatedAndSkippedFrames(const std::string& label, | 323 void PrintMaxRepeatedAndSkippedFrames(const std::string& label, |
296 const std::string& stats_file_name) { | 324 const std::string& stats_file_ref_name, |
297 PrintMaxRepeatedAndSkippedFrames(stdout, label, stats_file_name); | 325 const std::string& stats_file_test_name) { |
326 PrintMaxRepeatedAndSkippedFrames(stdout, label, stats_file_ref_name, | |
327 stats_file_test_name); | |
298 } | 328 } |
299 | 329 |
300 void PrintMaxRepeatedAndSkippedFrames(FILE* output, const std::string& label, | 330 namespace { |
301 const std::string& stats_file_name) { | 331 std::vector<std::pair<int, int> > GetBarcodeNrToFrequency(FILE* file) { |
phoglund
2016/12/08 09:55:44
Number
phoglund
2016/12/08 09:55:44
I would love to have unit tests for these guys sin
mandermo
2016/12/08 18:08:31
I agree the code needs to be easier to test. Maybe
phoglund
2016/12/12 12:46:20
Errors are not returned up the chain today, and I
| |
302 FILE* stats_file = fopen(stats_file_name.c_str(), "r"); | 332 std::vector<std::pair<int, int> > frame_cnt; |
phoglund
2016/12/08 09:55:44
It feels a lot more natural to make this a map. Fr
phoglund
2016/12/08 09:55:44
frame_frequency?
mandermo
2016/12/08 18:08:30
frame_frequency.insert(std::pair<int, int>(decoded
mandermo
2016/12/08 18:08:31
Maybe it is more of a frame count than a frequency
phoglund
2016/12/12 12:46:20
Right, it's actually a vector of frame "clusters",
mandermo
2016/12/21 16:42:06
I have renamed the function now and added a commen
| |
303 if (stats_file == NULL) { | 333 char line[STATS_LINE_LENGTH]; |
304 fprintf(stderr, "Couldn't open stats file for reading: %s\n", | 334 while (GetNextStatsLine(file, line)) { |
305 stats_file_name.c_str()); | 335 int decoded_frame_number = ExtractDecodedFrameNumber(line); |
336 if (frame_cnt.empty() || frame_cnt.back().first != decoded_frame_number) { | |
337 frame_cnt.push_back(std::make_pair(decoded_frame_number, 1)); | |
338 } else { | |
339 ++frame_cnt.back().second; | |
340 } | |
341 } | |
342 return frame_cnt; | |
343 } | |
344 } // namespace | |
345 | |
346 void PrintMaxRepeatedAndSkippedFrames(FILE* output, | |
347 const std::string& label, | |
348 const std::string& stats_file_ref_name, | |
349 const std::string& stats_file_test_name) { | |
350 FILE* stats_file_ref = fopen(stats_file_ref_name.c_str(), "r"); | |
351 FILE* stats_file_test = fopen(stats_file_test_name.c_str(), "r"); | |
352 if (stats_file_ref == NULL) { | |
353 fprintf(stderr, "Couldn't open reference stats file for reading: %s\n", | |
354 stats_file_ref_name.c_str()); | |
306 return; | 355 return; |
307 } | 356 } |
308 char line[STATS_LINE_LENGTH]; | 357 if (stats_file_test == NULL) { |
358 fprintf(stderr, "Couldn't open test stats file for reading: %s\n", | |
359 stats_file_test_name.c_str()); | |
360 fclose(stats_file_ref); | |
361 return; | |
362 } | |
309 | 363 |
310 int repeated_frames = 1; | |
311 int max_repeated_frames = 1; | 364 int max_repeated_frames = 1; |
312 int max_skipped_frames = 1; | 365 int max_skipped_frames = 1; |
313 int previous_frame_number = -1; | |
314 | 366 |
315 while (GetNextStatsLine(stats_file, line)) { | 367 std::vector<std::pair<int, int> > frame_cnt_ref = |
phoglund
2016/12/08 09:55:44
frame_frequency_ref
mandermo
2016/12/08 18:08:30
Maybe it is more of a frame count than a frequency
| |
316 int decoded_frame_number = ExtractDecodedFrameNumber(line); | 368 GetBarcodeNrToFrequency(stats_file_ref); |
317 | 369 |
318 if (decoded_frame_number == -1) { | 370 std::vector<std::pair<int, int> > frame_cnt_test = |
319 continue; | 371 GetBarcodeNrToFrequency(stats_file_test); |
372 | |
373 fclose(stats_file_ref); | |
374 fclose(stats_file_test); | |
375 | |
376 auto it_ref = frame_cnt_ref.begin(); | |
377 auto it_test = frame_cnt_test.begin(); | |
378 auto end_ref = frame_cnt_ref.end(); | |
379 auto end_test = frame_cnt_test.end(); | |
380 | |
381 if (it_test != end_test) { | |
phoglund
2016/12/08 09:55:44
if (it_test == end_test) the test file is empty, w
mandermo
2016/12/08 18:08:31
Done. Should I print to output or to stderr like t
| |
382 while (it_ref != end_ref && it_ref->first != it_test->first) { | |
phoglund
2016/12/08 09:55:44
It's hard to read what happens here. Maybe extract
mandermo
2016/12/08 18:08:31
Commented the code better.
mandermo
2016/12/08 18:08:31
Done. Should I print to output or to stderr like t
| |
383 ++it_ref; | |
320 } | 384 } |
385 if (it_ref == end_ref) { | |
386 fprintf(stderr, | |
387 "The barcode in the test videos first frame is not in the " | |
388 "reference video."); | |
389 return; | |
390 } | |
391 max_repeated_frames = | |
phoglund
2016/12/08 09:55:44
Also not sure if this algorithm is correct. To com
mandermo
2016/12/08 18:08:30
Lets make the unittests and see what output we get
phoglund
2016/12/12 12:46:20
Ok, I think counting the frame clusters like your
mandermo
2016/12/21 16:42:06
Done.
| |
392 std::max(max_repeated_frames, it_test->second - it_ref->second + 1); | |
393 } | |
321 | 394 |
322 // Calculate how many frames a cluster of repeated frames contains. | 395 for (;;) { |
323 if (decoded_frame_number == previous_frame_number) { | 396 ++it_test; |
324 ++repeated_frames; | 397 if (it_test == end_test) { |
325 if (repeated_frames > max_repeated_frames) { | 398 break; |
326 max_repeated_frames = repeated_frames; | |
327 } | |
328 } else { | |
329 repeated_frames = 1; | |
330 } | 399 } |
400 int skipped_frames = 0; | |
401 ++it_ref; | |
402 while (it_ref != end_ref && it_ref->first != it_test->first) { | |
403 skipped_frames += it_ref->second; | |
404 ++it_ref; | |
405 } | |
406 if (it_ref == end_ref) { | |
407 fprintf(stderr, | |
408 "The barcode in the test video is not in the reference video."); | |
409 return; | |
410 } | |
411 if (skipped_frames > max_skipped_frames) { | |
412 max_skipped_frames = skipped_frames; | |
413 } | |
414 max_repeated_frames = | |
415 std::max(max_repeated_frames, it_test->second - it_ref->second + 1); | |
416 } | |
331 | 417 |
332 // Calculate how much frames have been skipped. | |
333 if (decoded_frame_number != 0 && previous_frame_number != -1) { | |
334 int skipped_frames = decoded_frame_number - previous_frame_number - 1; | |
335 if (skipped_frames > max_skipped_frames) { | |
336 max_skipped_frames = skipped_frames; | |
337 } | |
338 } | |
339 previous_frame_number = decoded_frame_number; | |
340 } | |
341 fprintf(output, "RESULT Max_repeated: %s= %d\n", label.c_str(), | 418 fprintf(output, "RESULT Max_repeated: %s= %d\n", label.c_str(), |
342 max_repeated_frames); | 419 max_repeated_frames); |
343 fprintf(output, "RESULT Max_skipped: %s= %d\n", label.c_str(), | 420 fprintf(output, "RESULT Max_skipped: %s= %d\n", label.c_str(), |
344 max_skipped_frames); | 421 max_skipped_frames); |
345 fclose(stats_file); | |
346 } | 422 } |
347 | 423 |
348 void PrintAnalysisResults(const std::string& label, ResultsContainer* results) { | 424 void PrintAnalysisResults(const std::string& label, ResultsContainer* results) { |
349 PrintAnalysisResults(stdout, label, results); | 425 PrintAnalysisResults(stdout, label, results); |
350 } | 426 } |
351 | 427 |
352 void PrintAnalysisResults(FILE* output, const std::string& label, | 428 void PrintAnalysisResults(FILE* output, const std::string& label, |
353 ResultsContainer* results) { | 429 ResultsContainer* results) { |
354 std::vector<AnalysisResult>::iterator iter; | 430 std::vector<AnalysisResult>::iterator iter; |
355 | 431 |
(...skipping 12 matching lines...) Expand all Loading... | |
368 for (iter = results->frames.begin(); iter != results->frames.end() - 1; | 444 for (iter = results->frames.begin(); iter != results->frames.end() - 1; |
369 ++iter) { | 445 ++iter) { |
370 fprintf(output, "%f,", iter->ssim_value); | 446 fprintf(output, "%f,", iter->ssim_value); |
371 } | 447 } |
372 fprintf(output, "%f] score\n", iter->ssim_value); | 448 fprintf(output, "%f] score\n", iter->ssim_value); |
373 } | 449 } |
374 } | 450 } |
375 | 451 |
376 } // namespace test | 452 } // namespace test |
377 } // namespace webrtc | 453 } // namespace webrtc |
OLD | NEW |