| 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. |
| 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. |
| 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 |
| 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 // Clusters the frames in the file. First in the pair is the frame number and |
| 302 FILE* stats_file = fopen(stats_file_name.c_str(), "r"); | 332 // second is the number |
| 303 if (stats_file == NULL) { | 333 // of frames in that cluster. So if first frame in video has number 100 and it |
| 304 fprintf(stderr, "Couldn't open stats file for reading: %s\n", | 334 // is repeated 3 after |
| 305 stats_file_name.c_str()); | 335 // each other, then the first entry in the returned vector has first set to 100 |
| 306 return; | 336 // and second set |
| 307 } | 337 // to 3. |
| 338 std::vector<std::pair<int, int> > CalculateFrameClusters(FILE* file) { |
| 339 std::vector<std::pair<int, int> > frame_cnt; |
| 308 char line[STATS_LINE_LENGTH]; | 340 char line[STATS_LINE_LENGTH]; |
| 309 | 341 while (GetNextStatsLine(file, line)) { |
| 310 int repeated_frames = 1; | |
| 311 int max_repeated_frames = 1; | |
| 312 int max_skipped_frames = 1; | |
| 313 int previous_frame_number = -1; | |
| 314 | |
| 315 while (GetNextStatsLine(stats_file, line)) { | |
| 316 int decoded_frame_number = ExtractDecodedFrameNumber(line); | 342 int decoded_frame_number = ExtractDecodedFrameNumber(line); |
| 317 | |
| 318 if (decoded_frame_number == -1) { | 343 if (decoded_frame_number == -1) { |
| 319 continue; | 344 continue; |
| 320 } | 345 } |
| 346 if (frame_cnt.empty() || frame_cnt.back().first != decoded_frame_number) { |
| 347 frame_cnt.push_back(std::make_pair(decoded_frame_number, 1)); |
| 348 } else { |
| 349 ++frame_cnt.back().second; |
| 350 } |
| 351 } |
| 352 return frame_cnt; |
| 353 } |
| 354 } // namespace |
| 321 | 355 |
| 322 // Calculate how many frames a cluster of repeated frames contains. | 356 void PrintMaxRepeatedAndSkippedFrames(FILE* output, |
| 323 if (decoded_frame_number == previous_frame_number) { | 357 const std::string& label, |
| 324 ++repeated_frames; | 358 const std::string& stats_file_ref_name, |
| 325 if (repeated_frames > max_repeated_frames) { | 359 const std::string& stats_file_test_name) { |
| 326 max_repeated_frames = repeated_frames; | 360 FILE* stats_file_ref = fopen(stats_file_ref_name.c_str(), "r"); |
| 327 } | 361 FILE* stats_file_test = fopen(stats_file_test_name.c_str(), "r"); |
| 328 } else { | 362 if (stats_file_ref == NULL) { |
| 329 repeated_frames = 1; | 363 fprintf(stderr, "Couldn't open reference stats file for reading: %s\n", |
| 364 stats_file_ref_name.c_str()); |
| 365 return; |
| 366 } |
| 367 if (stats_file_test == NULL) { |
| 368 fprintf(stderr, "Couldn't open test stats file for reading: %s\n", |
| 369 stats_file_test_name.c_str()); |
| 370 fclose(stats_file_ref); |
| 371 return; |
| 372 } |
| 373 |
| 374 int max_repeated_frames = 1; |
| 375 int max_skipped_frames = 1; |
| 376 |
| 377 std::vector<std::pair<int, int> > frame_cnt_ref = |
| 378 CalculateFrameClusters(stats_file_ref); |
| 379 |
| 380 std::vector<std::pair<int, int> > frame_cnt_test = |
| 381 CalculateFrameClusters(stats_file_test); |
| 382 |
| 383 fclose(stats_file_ref); |
| 384 fclose(stats_file_test); |
| 385 |
| 386 auto it_ref = frame_cnt_ref.begin(); |
| 387 auto it_test = frame_cnt_test.begin(); |
| 388 auto end_ref = frame_cnt_ref.end(); |
| 389 auto end_test = frame_cnt_test.end(); |
| 390 |
| 391 if (it_test == end_test || it_ref == end_ref) { |
| 392 fprintf(stderr, "Either test or ref file is empty, nothing to print"); |
| 393 return; |
| 394 } |
| 395 |
| 396 // Find the first frame in the reference video that match the first frame in |
| 397 // the test video. |
| 398 while (it_ref != end_ref && it_ref->first != it_test->first) { |
| 399 ++it_ref; |
| 400 } |
| 401 if (it_ref == end_ref) { |
| 402 fprintf(stderr, |
| 403 "The barcode in the test videos first frame is not in the " |
| 404 "reference video."); |
| 405 return; |
| 406 } |
| 407 |
| 408 for (;;) { |
| 409 max_repeated_frames = |
| 410 std::max(max_repeated_frames, it_test->second - it_ref->second + 1); |
| 411 ++it_test; |
| 412 if (it_test == end_test) { |
| 413 break; |
| 330 } | 414 } |
| 415 int skipped_frames = 0; |
| 416 ++it_ref; |
| 417 while (it_ref != end_ref && it_ref->first != it_test->first) { |
| 418 skipped_frames += it_ref->second; |
| 419 ++it_ref; |
| 420 } |
| 421 if (it_ref == end_ref) { |
| 422 fprintf(stderr, |
| 423 "The barcode in the test video is not in the reference video."); |
| 424 return; |
| 425 } |
| 426 if (skipped_frames > max_skipped_frames) { |
| 427 max_skipped_frames = skipped_frames; |
| 428 } |
| 429 } |
| 331 | 430 |
| 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(), | 431 fprintf(output, "RESULT Max_repeated: %s= %d\n", label.c_str(), |
| 342 max_repeated_frames); | 432 max_repeated_frames); |
| 343 fprintf(output, "RESULT Max_skipped: %s= %d\n", label.c_str(), | 433 fprintf(output, "RESULT Max_skipped: %s= %d\n", label.c_str(), |
| 344 max_skipped_frames); | 434 max_skipped_frames); |
| 345 fclose(stats_file); | |
| 346 } | 435 } |
| 347 | 436 |
| 348 void PrintAnalysisResults(const std::string& label, ResultsContainer* results) { | 437 void PrintAnalysisResults(const std::string& label, ResultsContainer* results) { |
| 349 PrintAnalysisResults(stdout, label, results); | 438 PrintAnalysisResults(stdout, label, results); |
| 350 } | 439 } |
| 351 | 440 |
| 352 void PrintAnalysisResults(FILE* output, const std::string& label, | 441 void PrintAnalysisResults(FILE* output, const std::string& label, |
| 353 ResultsContainer* results) { | 442 ResultsContainer* results) { |
| 354 std::vector<AnalysisResult>::iterator iter; | 443 std::vector<AnalysisResult>::iterator iter; |
| 355 | 444 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 368 for (iter = results->frames.begin(); iter != results->frames.end() - 1; | 457 for (iter = results->frames.begin(); iter != results->frames.end() - 1; |
| 369 ++iter) { | 458 ++iter) { |
| 370 fprintf(output, "%f,", iter->ssim_value); | 459 fprintf(output, "%f,", iter->ssim_value); |
| 371 } | 460 } |
| 372 fprintf(output, "%f] score\n", iter->ssim_value); | 461 fprintf(output, "%f] score\n", iter->ssim_value); |
| 373 } | 462 } |
| 374 } | 463 } |
| 375 | 464 |
| 376 } // namespace test | 465 } // namespace test |
| 377 } // namespace webrtc | 466 } // namespace webrtc |
| OLD | NEW |