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 |