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 std::vector<std::pair<int, int> > GetBarcodeNrToFrequency(FILE* file) { |
302 FILE* stats_file = fopen(stats_file_name.c_str(), "r"); | 332 std::vector<std::pair<int, int> > frame_cnt; |
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 = |
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 || it_ref == end_ref) { |
| 382 fprintf(stderr, "Either test or ref file is empty, nothing to print"); |
| 383 return; |
| 384 } |
| 385 |
| 386 // Find the first frame in the reference video that match the first frame in |
| 387 // the test video. |
| 388 while (it_ref != end_ref && it_ref->first != it_test->first) { |
| 389 ++it_ref; |
| 390 } |
| 391 if (it_ref == end_ref) { |
| 392 fprintf(stderr, |
| 393 "The barcode in the test videos first frame is not in the " |
| 394 "reference video."); |
| 395 return; |
| 396 } |
| 397 |
| 398 for (;;) { |
| 399 max_repeated_frames = |
| 400 std::max(max_repeated_frames, it_test->second - it_ref->second + 1); |
| 401 ++it_test; |
| 402 if (it_test == end_test) { |
| 403 break; |
320 } | 404 } |
| 405 int skipped_frames = 0; |
| 406 ++it_ref; |
| 407 while (it_ref != end_ref && it_ref->first != it_test->first) { |
| 408 skipped_frames += it_ref->second; |
| 409 ++it_ref; |
| 410 } |
| 411 if (it_ref == end_ref) { |
| 412 fprintf(stderr, |
| 413 "The barcode in the test video is not in the reference video."); |
| 414 return; |
| 415 } |
| 416 if (skipped_frames > max_skipped_frames) { |
| 417 max_skipped_frames = skipped_frames; |
| 418 } |
| 419 } |
321 | 420 |
322 // Calculate how many frames a cluster of repeated frames contains. | |
323 if (decoded_frame_number == previous_frame_number) { | |
324 ++repeated_frames; | |
325 if (repeated_frames > max_repeated_frames) { | |
326 max_repeated_frames = repeated_frames; | |
327 } | |
328 } else { | |
329 repeated_frames = 1; | |
330 } | |
331 | |
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(), | 421 fprintf(output, "RESULT Max_repeated: %s= %d\n", label.c_str(), |
342 max_repeated_frames); | 422 max_repeated_frames); |
343 fprintf(output, "RESULT Max_skipped: %s= %d\n", label.c_str(), | 423 fprintf(output, "RESULT Max_skipped: %s= %d\n", label.c_str(), |
344 max_skipped_frames); | 424 max_skipped_frames); |
345 fclose(stats_file); | |
346 } | 425 } |
347 | 426 |
348 void PrintAnalysisResults(const std::string& label, ResultsContainer* results) { | 427 void PrintAnalysisResults(const std::string& label, ResultsContainer* results) { |
349 PrintAnalysisResults(stdout, label, results); | 428 PrintAnalysisResults(stdout, label, results); |
350 } | 429 } |
351 | 430 |
352 void PrintAnalysisResults(FILE* output, const std::string& label, | 431 void PrintAnalysisResults(FILE* output, const std::string& label, |
353 ResultsContainer* results) { | 432 ResultsContainer* results) { |
354 std::vector<AnalysisResult>::iterator iter; | 433 std::vector<AnalysisResult>::iterator iter; |
355 | 434 |
(...skipping 12 matching lines...) Expand all Loading... |
368 for (iter = results->frames.begin(); iter != results->frames.end() - 1; | 447 for (iter = results->frames.begin(); iter != results->frames.end() - 1; |
369 ++iter) { | 448 ++iter) { |
370 fprintf(output, "%f,", iter->ssim_value); | 449 fprintf(output, "%f,", iter->ssim_value); |
371 } | 450 } |
372 fprintf(output, "%f] score\n", iter->ssim_value); | 451 fprintf(output, "%f] score\n", iter->ssim_value); |
373 } | 452 } |
374 } | 453 } |
375 | 454 |
376 } // namespace test | 455 } // namespace test |
377 } // namespace webrtc | 456 } // namespace webrtc |
OLD | NEW |