Chromium Code Reviews| 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 |