├── .gitignore ├── CalcHistogram.cpp ├── CalcHistogram.h ├── README.md ├── glcm.cpp ├── glcm.h ├── input.cpp ├── input.h ├── makefile ├── scene.cpp ├── scene.h ├── tools.cpp ├── tools.h ├── v-sfp.cpp ├── v-sfp.vcxproj ├── v-sfp.vcxproj.filters └── v-sfp.vcxproj.user /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /CalcHistogram.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverlight/scene-split/0982b7e19171645cf489c9a4ecd4daafb99cc3bc/CalcHistogram.cpp -------------------------------------------------------------------------------- /CalcHistogram.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverlight/scene-split/0982b7e19171645cf489c9a4ecd4daafb99cc3bc/CalcHistogram.h -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scene-split 2 | 视频场景切分算法 3 | 核心的特征是 颜色直方图,然后通过余弦距离来判断是否切分 4 | 有2个核心阈值,一个是相邻帧的余弦距离,另一个是当前帧和场景起始帧的余弦距离 5 | 项目中使用的时候,这2个距离可以自己调整 6 | -------------------------------------------------------------------------------- /glcm.cpp: -------------------------------------------------------------------------------- 1 | /*================================================================= 2 | * Calculate GLCM(Gray-level Co-occurrence Matrix) By OpenCV. 3 | * 4 | * Copyright (C) 2017 Chandler Geng. All rights reserved. 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as published 8 | * by the Free Software Foundation; either version 2 of the License, or (at 9 | * your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 | * more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 59 18 | * Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | =================================================================== 20 | */ 21 | 22 | #include "glcm.h" 23 | 24 | /*=================================================================== 25 | * 函数名:getOneChannel 26 | * 说明:从彩色通道中提取一个通道; 27 | * 参数: 28 | * Mat src: 源图像 29 | * Mat& dstChannel: 源图像中的单一通道,并输出为灰度图 30 | * RGBChannel channel: RGB通道中的指定通道 31 | * 返回值:void 32 | *------------------------------------------------------------------ 33 | * Function: getOneChannel 34 | * 35 | * Summary: 36 | * Extract a channel from RGB Image; 37 | * 38 | * Arguments: 39 | * Mat src - source image 40 | * Mat& dstChannel - a channel from RGB source image 41 | * RGBChannel channel - Point out which channel will be extracted 42 | * 43 | * Returns: 44 | * void 45 | ===================================================================== 46 | */ 47 | void GLCM::getOneChannel(Mat src, Mat& dstChannel, RGBChannel channel) 48 | { 49 | // 若输入图像已经是灰度图,则直接输出 50 | if(src.channels() == 1) 51 | dstChannel = src; 52 | 53 | vector bgr; 54 | // 分离图像 55 | split(src, bgr); 56 | 57 | switch(channel) 58 | { 59 | case CHANNEL_B: dstChannel = bgr[0]; break; 60 | case CHANNEL_G: dstChannel = bgr[1]; break; 61 | case CHANNEL_R: dstChannel = bgr[2]; break; 62 | default: 63 | cout<<"ERROR in getOneChannel(): No Such Channel."<(j); 106 | uchar* output = dst.ptr(j); 107 | 108 | for(int i = 0; i < tmp.cols; i++) 109 | { 110 | switch(level) 111 | { 112 | case GRAY_4: 113 | output[i] = cv::saturate_cast(current[i] / 64); 114 | break; 115 | case GRAY_8: 116 | output[i] = cv::saturate_cast(current[i] / 32); 117 | break; 118 | case GRAY_16: 119 | output[i] = cv::saturate_cast(current[i] / 16); 120 | break; 121 | default: 122 | cout<<"ERROR in GrayMagnitude(): No Such GrayLevel."< src.rows 187 | || src_j + (size/2) + 1 > src.cols 188 | || src_i < (size/2) 189 | || src_j < (size/2)) 190 | { 191 | size = 3; 192 | if(src_i <= size/2) 193 | { 194 | if(src_j <= size/2) 195 | srcCut = Mat(src, Range(0, 3), Range(0, 3)); 196 | else if(src_j + (size/2) + 1 > src.cols) 197 | srcCut = Mat(src, Range(0, 3), Range(src.cols - 3, src.cols)); 198 | else 199 | srcCut = Mat(src, Range(0, 3), Range(src_j - size/2, src_j + size/2 + 1)); 200 | } 201 | else if(src_i >= src.rows - size/2) 202 | { 203 | if(src_j <= size/2) 204 | srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(0, 3)); 205 | else if(src_j + (size/2) + 1 > src.cols) 206 | srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(src.cols - 3, src.cols)); 207 | else 208 | srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(src_j - size/2, src_j + size/2 + 1)); 209 | } 210 | else if(src_j <= size/2) 211 | { 212 | if(src_i <= size/2) 213 | srcCut = Mat(src, Range(0, 3), Range(0, 3)); 214 | else if(src_i + (size/2) + 1 > src.rows) 215 | srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(0, 3)); 216 | else 217 | srcCut = Mat(src, Range(src_i - size/2, src_i + size/2 + 1), Range(0, 3)); 218 | } 219 | else if(src_j >= src.cols - size/2) 220 | { 221 | if(src_i <= size/2) 222 | srcCut = Mat(src, Range(0, 3), Range(src.cols - 3, src.cols)); 223 | else if(src_i + (size/2) + 1 > src.rows) 224 | srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(src.cols - 3, src.cols)); 225 | else 226 | srcCut = Mat(src, Range(src_i - size/2, src_i + size/2 + 1), Range(src.cols - 3, src.cols)); 227 | } 228 | else 229 | srcCut = Mat(src, Range(src_i - size/2, src_i + size/2 + 1), Range(src_j - size/2, src_j + size/2 + 1)); 230 | } 231 | else 232 | srcCut = Mat(src, Range(src_i - size/2, src_i + size/2 + 1), Range(src_j - size/2, src_j + size/2 + 1)); 233 | 234 | // 根据灰度等级初始化灰度共生矩阵 235 | // Initialize GLCM according Gray Level 236 | switch(level) 237 | { 238 | case GRAY_4: 239 | { 240 | glcm = Mat_(4, 4); 241 | for(int i = 0; i < 4; i++) 242 | for(int j = 0; j < 4; j++) 243 | glcm.at(j, i) = 0; 244 | break; 245 | } 246 | case GRAY_8: 247 | { 248 | glcm = Mat_(8, 8); 249 | for(int i = 0; i < 8; i++) 250 | for(int j = 0; j < 8; j++) 251 | glcm.at(j, i) = 0; 252 | break; 253 | } 254 | case GRAY_16: 255 | { 256 | glcm = Mat_(16, 16); 257 | for(int i = 0; i < 16; i++) 258 | for(int j = 0; j < 16; j++) 259 | glcm.at(j, i) = 0; 260 | break; 261 | } 262 | default: 263 | cout<<"ERROR in CalcuOneGLCM(): No Such Gray Level."<(srcCut.at(j, i), srcCut.at(j+1, i))++; 275 | break; 276 | case DIR_45: 277 | for(int i = 0; i < srcCut.rows - 1; i++) 278 | for(int j = 0; j < srcCut.cols - 1; j++) 279 | glcm.at(srcCut.at(j, i), srcCut.at(j+1, i+1))++; 280 | break; 281 | case DIR_90: 282 | for(int i = 0; i < srcCut.rows - 1; i++) 283 | for(int j = 0; j < srcCut.cols; j++) 284 | glcm.at(srcCut.at(j, i), srcCut.at(j, i+1))++; 285 | break; 286 | case DIR_135: 287 | for(int i = 1; i < srcCut.rows; i++) 288 | for(int j = 0; j < srcCut.cols - 1; j++) 289 | glcm.at(srcCut.at(j, i), srcCut.at(j+1, i-1))++; 290 | break; 291 | default: 292 | cout<<"ERROR in CalcuOneGLCM(): No such Direct."<(j, i); 334 | if(sum == 0) sum = 1; 335 | 336 | for(int i = 0; i < tmp.rows; i++) 337 | for(int j = 0; j < tmp.cols; j++) 338 | tmp.at(j, i) /= sum; 339 | 340 | tmp.copyTo(dst); 341 | } 342 | 343 | /*=================================================================== 344 | * 函数名:CalcuOneTextureEValue 345 | * 说明:计算单个窗口矩阵的图像纹理特征值,包括能量、对比度、相关度、熵 346 | * 参数: 347 | * Mat src: 源矩阵,窗口矩阵 348 | * TextureEValues& EValue: 纹理特征值变量 349 | * bool ToCheckMat: 检查输入矩阵是否为概率矩阵 350 | * 返回值:void 351 | *------------------------------------------------------------------ 352 | * Function: CalcuOneTextureEValue 353 | * 354 | * Summary: 355 | * Calculate Texture Eigenvalues of the Window Mat, which is including 356 | * Energy, Contrast, Homogenity, Entropy. 357 | * 358 | * Arguments: 359 | * Mat src - source Matrix (Window Mat) 360 | * TextureEValues& EValue - Texture Eigenvalues 361 | * bool ToCheckMat - to check input Mat is Probability Mat or not 362 | * 363 | * Returns: 364 | * void 365 | ===================================================================== 366 | */ 367 | void GLCM::CalcuOneTextureEValue(Mat src, TextureEValues& EValue, bool ToCheckMat) 368 | { 369 | if(ToCheckMat) 370 | { 371 | float sum = 0; 372 | for(int i = 0; i < src.rows; i++) 373 | for(int j = 0; j < src.cols; j++) 374 | sum += src.at(j, i); 375 | if(sum < 0.99 || sum > 1.01) 376 | { 377 | cout<<"ERROR in CalcuOneTextureEValue(): Sum of the Mat is not equal to 1.00."<(j, i), 2); 391 | EValue.contrast += (powf((i - j), 2) * src.at(j, i) ); 392 | EValue.homogenity += (src.at(j, i) / (1 + fabs((float)(i - j))) ); 393 | if(src.at(j, i) != 0) 394 | EValue.entropy -= (src.at(j, i) * log10(src.at(j, i)) ); 395 | } 396 | } 397 | 398 | /*=================================================================== 399 | * 函数名:CalcuTextureEValue 400 | * 说明:计算全图的图像纹理特征值,包括能量、对比度、相关度、熵 401 | * 参数: 402 | * Mat src: 源矩阵,窗口矩阵 403 | * TextureEValues& EValue: 输出目标,全图的纹理特征值变量 404 | * int size: 窗口尺寸(仅支持5*5, 7*7) 405 | * GrayLevel level: 灰度等级 406 | * 返回值:void 407 | *------------------------------------------------------------------ 408 | * Function: CalcuOneTextureEValue 409 | * 410 | * Summary: 411 | * Calculate Texture Eigenvalues of One Window Mat, which is including 412 | * Energy, Contrast, Homogenity, Entropy. 413 | * 414 | * Arguments: 415 | * Mat src - source Matrix (Window Mat) 416 | * TextureEValues& EValue - Output Dst: Texture Eigenvalues of the Whole Image 417 | * int size - size of Mat Window (only support 5*5, 7*7) 418 | * GrayLevel level - Destination image's Gray Level (choose in 4/8/16) 419 | * 420 | * Returns: 421 | * void 422 | ===================================================================== 423 | */ 424 | void GLCM::CalcuTextureEValue(Mat src, TextureEValues& EValue, int size, GrayLevel level) 425 | { 426 | // 原图像的灰度图 427 | // Gray Image of the Source Image 428 | Mat imgGray; 429 | 430 | // 窗口矩阵 431 | // Window Matrix 432 | Mat glcm_win; 433 | 434 | // 归一化后的概率矩阵 435 | // Probability Matrix after Normalizing 436 | Mat glcm_norm; 437 | 438 | // 纹理特征值缓存变量 439 | // Texture Eigenvalues temp variable 440 | TextureEValues EValue_temp; 441 | 442 | // 初始化目标纹理特征值 443 | // Init Dst Texture Eigenvalues 444 | EValue.contrast = 0; EValue.energy = 0; EValue.entropy = 0; EValue.homogenity = 0; 445 | 446 | // 检查输入图像是否为单通道图像,如果不是,则转换其格式 447 | // Check if Input Image is Single Channel Image or not, IF it's Single Channel Image, then Convert its Format to Gray Image. 448 | if(src.channels() != 1) 449 | cvtColor(src, imgGray, CV_BGR2GRAY); 450 | else 451 | src.copyTo(imgGray); 452 | 453 | for(int i = 0; i < imgGray.rows; i++) 454 | { 455 | for(int j = 0; j < imgGray.cols; j++) 456 | { 457 | // 计算所有统计方向的灰度共生矩阵与对应的特征值,并累加至缓存变量中 458 | // Calculate All Statistical Direction's GLCM and Eigenvalues, then accumulate into temp variables 459 | float energy, contrast, homogenity, entropy; 460 | energy = contrast = homogenity = entropy = 0; 461 | 462 | CalcuOneGLCM(imgGray, glcm_win, i, j, size, level, DIR_0); 463 | NormalizeMat(glcm_win, glcm_norm); 464 | CalcuOneTextureEValue(glcm_norm, EValue_temp, false); 465 | energy += EValue_temp.energy; contrast += EValue_temp.contrast; 466 | homogenity += EValue_temp.homogenity; entropy += EValue_temp.entropy; 467 | 468 | CalcuOneGLCM(imgGray, glcm_win, i, j, size, level, DIR_45); 469 | NormalizeMat(glcm_win, glcm_norm); 470 | CalcuOneTextureEValue(glcm_norm, EValue_temp, false); 471 | energy += EValue_temp.energy; contrast += EValue_temp.contrast; 472 | homogenity += EValue_temp.homogenity; entropy += EValue_temp.entropy; 473 | 474 | CalcuOneGLCM(imgGray, glcm_win, i, j, size, level, DIR_90); 475 | NormalizeMat(glcm_win, glcm_norm); 476 | CalcuOneTextureEValue(glcm_norm, EValue_temp, false); 477 | energy += EValue_temp.energy; contrast += EValue_temp.contrast; 478 | homogenity += EValue_temp.homogenity; entropy += EValue_temp.entropy; 479 | 480 | CalcuOneGLCM(imgGray, glcm_win, i, j, size, level, DIR_135); 481 | NormalizeMat(glcm_win, glcm_norm); 482 | CalcuOneTextureEValue(glcm_norm, EValue_temp, false); 483 | energy += EValue_temp.energy; contrast += EValue_temp.contrast; 484 | homogenity += EValue_temp.homogenity; entropy += EValue_temp.entropy; 485 | 486 | // 将所有方向计算得到的特征值平均化,得到的值即可消除统计方向影响 487 | // average Eigenvalues of all Statistical Directions, then the average value has eliminated the effect of Statistical Directions 488 | energy /= 4; contrast /= 4; 489 | homogenity /= 4; entropy /= 4; 490 | 491 | // 累加当前单个窗口的纹理特征值,作为整个图像的纹理特征值 492 | // Accumulate Texture Eigenvalues of Current Window, then make the Sum as Texture Eigenvalues of the Whole Image 493 | //cout << contrast << " " << energy << " " << entropy << " " << homogenity << endl; 494 | EValue.contrast += contrast; 495 | EValue.energy += energy; 496 | EValue.entropy += entropy; 497 | EValue.homogenity += homogenity; 498 | } 499 | } 500 | } 501 | 502 | /*=================================================================== 503 | * 函数名:CalcuTextureImages 504 | * 说明:计算整幅图像的纹理特征,并将结果输出到相应矩阵中 505 | * 参数: 506 | * Mat src: 原图像 507 | * Mat& imgEnergy: 目标能量矩阵 508 | * Mat& imgContrast: 目标对比度矩阵 509 | * Mat& imgHomogenity: 目标相关度矩阵 510 | * Mat& imgEntropy: 目标熵矩阵 511 | * int size: 窗口尺寸(仅支持5*5, 7*7) 512 | * GrayLevel level: 灰度等级 513 | * bool ToAdjustImg: 是否调整输出的纹理特征图像 514 | * 返回值:void 515 | *------------------------------------------------------------------ 516 | * Function: CalcuTextureImages 517 | * 518 | * Summary: 519 | * Calculate Texture Features of the whole Image, and output the result 520 | * into Martixs. 521 | * 522 | * Arguments: 523 | * Mat src - source Image 524 | * Mat& imgEnergy - Destination Mat, Energy Matrix 525 | * Mat& imgContrast - Destination Mat, Contrast Matrix 526 | * Mat& imgHomogenity - Destination Mat, Homogenity Matrix 527 | * Mat& imgEntropy - Destination Mat, Entropy Matrix 528 | * int size - size of Mat Window (only support 5*5, 7*7) 529 | * GrayLevel level - Destination image's Gray Level (choose in 4/8/16) 530 | * bool ToAdjustImg: to Adjust output Texture Feature Images or not 531 | * 532 | * Returns: 533 | * void 534 | ===================================================================== 535 | */ 536 | void GLCM::CalcuTextureImages(Mat src, Mat& imgEnergy, Mat& imgContrast, Mat& imgHomogenity, Mat& imgEntropy, 537 | int size, GrayLevel level, bool ToAdjustImg) 538 | { 539 | // 窗口矩阵 540 | // Window Matrix 541 | Mat glcm_win; 542 | 543 | // 归一化后的概率矩阵 544 | // Probability Matrix after Normalizing 545 | Mat glcm_norm; 546 | 547 | // 纹理特征值缓存变量 548 | // Texture Eigenvalues temp varialbe 549 | TextureEValues EValue; 550 | 551 | imgEnergy.create(src.size(), CV_32FC1); 552 | imgContrast.create(src.size(), CV_32FC1); 553 | imgHomogenity.create(src.size(), CV_32FC1); 554 | imgEntropy.create(src.size(), CV_32FC1); 555 | 556 | for(int i = 0; i < src.rows; i++) 557 | { 558 | float* energyData = imgEnergy.ptr(i); 559 | float* contrastData = imgContrast.ptr(i); 560 | float* homogenityData = imgHomogenity.ptr(i); 561 | float* entropyData = imgEntropy.ptr(i); 562 | 563 | for(int j = 0; j < src.cols; j++) 564 | { 565 | // 计算所有统计方向的灰度共生矩阵与对应的特征值,并累加至缓存变量中 566 | // Calculate All Statistical Direction's GLCM and Eigenvalues, then accumulate into temp variables 567 | float energy, contrast, homogenity, entropy; 568 | energy = contrast = homogenity = entropy = 0; 569 | 570 | CalcuOneGLCM(src, glcm_win, i, j, size, level, DIR_0); 571 | NormalizeMat(glcm_win, glcm_norm); 572 | CalcuOneTextureEValue(glcm_norm, EValue, false); 573 | energy += EValue.energy; contrast += EValue.contrast; 574 | homogenity += EValue.homogenity; entropy += EValue.entropy; 575 | 576 | CalcuOneGLCM(src, glcm_win, i, j, size, level, DIR_45); 577 | NormalizeMat(glcm_win, glcm_norm); 578 | CalcuOneTextureEValue(glcm_norm, EValue, false); 579 | energy += EValue.energy; contrast += EValue.contrast; 580 | homogenity += EValue.homogenity; entropy += EValue.entropy; 581 | 582 | CalcuOneGLCM(src, glcm_win, i, j, size, level, DIR_90); 583 | NormalizeMat(glcm_win, glcm_norm); 584 | CalcuOneTextureEValue(glcm_norm, EValue, false); 585 | energy += EValue.energy; contrast += EValue.contrast; 586 | homogenity += EValue.homogenity; entropy += EValue.entropy; 587 | 588 | CalcuOneGLCM(src, glcm_win, i, j, size, level, DIR_135); 589 | NormalizeMat(glcm_win, glcm_norm); 590 | CalcuOneTextureEValue(glcm_norm, EValue, false); 591 | energy += EValue.energy; contrast += EValue.contrast; 592 | homogenity += EValue.homogenity; entropy += EValue.entropy; 593 | 594 | // 将所有方向计算得到的特征值平均化,得到的值即可消除统计方向影响 595 | // average Eigenvalues of all Statistical Directions, then the average value has eliminated the effect of Statistical Directions 596 | energy /= 4; contrast /= 4; 597 | homogenity /= 4; entropy /= 4; 598 | 599 | energyData[j] = energy; 600 | contrastData[j] = contrast; 601 | homogenityData[j] = homogenity; 602 | entropyData[j] = entropy; 603 | } 604 | } 605 | 606 | // 调整输出特征图像,类型由CV_32FC1改为CV_8UC1,取值范围0--255 607 | // Adjust output Texture Feature Images, Change its type from CV_32FC1 to CV_8UC1, Change its value range as 0--255 608 | if(ToAdjustImg) 609 | { 610 | cv::normalize(imgEnergy, imgEnergy, 0, 255, NORM_MINMAX); 611 | cv::normalize(imgContrast, imgContrast, 0, 255, NORM_MINMAX); 612 | cv::normalize(imgEntropy, imgEntropy, 0, 255, NORM_MINMAX); 613 | cv::normalize(imgHomogenity, imgHomogenity, 0, 255, NORM_MINMAX); 614 | imgEnergy.convertTo(imgEnergy, CV_8UC1); 615 | imgContrast.convertTo(imgContrast, CV_8UC1); 616 | imgEntropy.convertTo(imgEntropy, CV_8UC1); 617 | imgHomogenity.convertTo(imgHomogenity, CV_8UC1); 618 | } 619 | } 620 | -------------------------------------------------------------------------------- /glcm.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef GLCM_H 3 | #define GLCM_H 4 | #include 5 | #include "opencv2/core/core.hpp" 6 | #include "opencv2/imgproc/imgproc.hpp" 7 | #include "opencv2/highgui/highgui.hpp" 8 | #include 9 | 10 | 11 | using namespace std; 12 | using namespace cv; 13 | 14 | 15 | 16 | using namespace cv; 17 | using namespace std; 18 | 19 | // 灰度等级 20 | // Gray Level (Choose in 4/8/16) 21 | enum GrayLevel 22 | { 23 | GRAY_4, 24 | GRAY_8, 25 | GRAY_16 26 | }; 27 | 28 | // 灰度统计方向 29 | // Gray Value Statistical Direction 30 | // (Choose in 0°, 45°, 90°, 135°) 31 | enum GrayDirection 32 | { 33 | DIR_0, 34 | DIR_45, 35 | DIR_90, 36 | DIR_135 37 | }; 38 | 39 | // 彩色图中的指定通道 40 | // Point out R, G, B Channel of a Image 41 | enum RGBChannel 42 | { 43 | CHANNEL_R, 44 | CHANNEL_G, 45 | CHANNEL_B 46 | }; 47 | 48 | // 纹理特征值结构体 49 | // struct including Texture Eigenvalues 50 | struct TextureEValues 51 | { 52 | // 能量 53 | float energy; 54 | // 对比度 55 | float contrast; 56 | // 相关度 57 | float homogenity; 58 | // 熵 59 | float entropy; 60 | }; 61 | 62 | class GLCM 63 | { 64 | public: 65 | // 从彩色通道中提取一个通道 66 | // Extract a channel from RGB Image 67 | void getOneChannel(Mat src, Mat& dstChannel, RGBChannel channel = CHANNEL_R); 68 | 69 | // 将灰度图中的所有像素值量级化,可以被量化为4/8/16个等级 70 | // Magnitude all pixels of Gray Image, and Magnitude Level can be chosen in 4/8/16; 71 | void GrayMagnitude(Mat src, Mat& dst, GrayLevel level = GRAY_8); 72 | 73 | // 计算一个矩阵窗口中,按照某个方向统计的灰度共生矩阵 74 | // Calculate the GLCM of one Mat Window according to one Statistical Direction. 75 | void CalcuOneGLCM(Mat src, Mat &dst, int src_i, int src_j, int size, GrayLevel level = GRAY_8, GrayDirection direct = DIR_0); 76 | 77 | // 矩阵的归一化,将矩阵所有元素与矩阵中所有元素之和作除运算,得到概率矩阵 78 | // Normalize the Martix, make all pixels of Mat divided by the sum of all pixels of Mat, then get Probability Matrix. 79 | void NormalizeMat(Mat src, Mat& dst); 80 | 81 | // 计算单个窗口矩阵的图像纹理特征值,包括能量、对比度、相关度、熵 82 | // Calculate Texture Eigenvalues of One Window Mat, which is including Energy, Contrast, Homogenity, Entropy. 83 | void CalcuOneTextureEValue(Mat src, TextureEValues& EValue, bool ToCheckMat = false); 84 | 85 | // 计算全图的图像纹理特征值,包括能量、对比度、相关度、熵 86 | // Calculate Texture Eigenvalues of One Window Mat, which is including Energy, Contrast, Homogenity, Entropy. 87 | void CalcuTextureEValue(Mat src, TextureEValues& EValue, 88 | int size = 5, GrayLevel level = GRAY_8); 89 | 90 | // 计算整幅图像的纹理特征 91 | void CalcuTextureImages(Mat src, Mat& imgEnergy, Mat& imgContrast, Mat& imgHomogenity, Mat& imgEntropy, 92 | int size = 5, GrayLevel level = GRAY_8, bool ToAdjustImg = false); 93 | }; 94 | 95 | #endif // GLCM_H 96 | -------------------------------------------------------------------------------- /input.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "input.h" 3 | 4 | 5 | CInput::CInput() 6 | { 7 | _url = NULL; 8 | _timestamp_ms = -1; 9 | _width = 0, _height = 0; 10 | 11 | } 12 | 13 | CInput::~CInput() 14 | { 15 | 16 | } 17 | 18 | 19 | CInputVideo::CInputVideo() 20 | { 21 | _timestamp_ms = -1; 22 | _bLoops = 0; 23 | _nDuration_ms = -1; 24 | } 25 | 26 | CInputVideo::~CInputVideo() 27 | { 28 | } 29 | 30 | int CInputVideo::Open(char *url) 31 | { 32 | _url = url; 33 | if (_url == NULL) 34 | return -1; 35 | 36 | _cap.open(_url); 37 | if (!_cap.isOpened()) 38 | { 39 | cout << " video file [" << _url << "] open failed! " << endl; 40 | return -1; 41 | } 42 | 43 | float fps = _cap.get(CV_CAP_PROP_FPS); 44 | float framenum = _cap.get(CV_CAP_PROP_FRAME_COUNT); 45 | if (fps <= 0) 46 | return -1; 47 | _nDuration_ms = (framenum / fps) * 1000; 48 | //cout << "video duration : " << _nDuration_ms << endl; 49 | _nBaseTimestamp_ms = 0; 50 | 51 | _width = int(_cap.get(CV_CAP_PROP_FRAME_WIDTH)); 52 | _height = int(_cap.get(CV_CAP_PROP_FRAME_HEIGHT)); 53 | 54 | return 0; 55 | } 56 | 57 | int CInputVideo::Open(char *url, int bLoops) 58 | { 59 | _bLoops = bLoops; 60 | return Open(url); 61 | } 62 | 63 | int CInputVideo::Get_FirstFrame(Mat &firstImg) 64 | { 65 | if (!_matFirst.empty()) 66 | { 67 | firstImg = _matFirst; 68 | return 0; 69 | } 70 | 71 | VideoCapture cap(_url); 72 | if (!cap.isOpened()) 73 | return -1; 74 | cap >> _matFirst; 75 | firstImg = _matFirst; 76 | 77 | return 0; 78 | } 79 | 80 | int CInputVideo::Get_Duration() 81 | { 82 | return _nDuration_ms; 83 | } 84 | 85 | float CInputVideo::Get_FrameRate() 86 | { 87 | return _cap.get(CV_CAP_PROP_FPS); 88 | } 89 | 90 | int CInputVideo::Close() 91 | { 92 | return 0; 93 | } 94 | 95 | int CInputVideo::Get_CurrentMat(Mat &img, int startTime, int endTime, int currentTime) 96 | { 97 | int hopeTimestamp_ms = currentTime - startTime - _nBaseTimestamp_ms; 98 | if (hopeTimestamp_ms<0) 99 | return -1; 100 | 101 | while (1) 102 | { 103 | if (_timestamp_ms >= hopeTimestamp_ms) 104 | { 105 | if (_matCurrent.empty()) 106 | { 107 | cout << "video _matCurrent is emtpy " << endl; 108 | return -1; 109 | } 110 | img = _matCurrent; 111 | //cout << _timestamp_ms << endl; 112 | break; 113 | } 114 | else 115 | { 116 | _timestamp_ms = _cap.get(CV_CAP_PROP_POS_MSEC); 117 | _cap >> _matCurrent; 118 | if (_matCurrent.empty()) 119 | { 120 | if (_timestamp_ms < 0 || _bLoops == 0) 121 | return -1; 122 | 123 | // loops == 1 124 | _nBaseTimestamp_ms += (_timestamp_ms + 1); 125 | hopeTimestamp_ms = currentTime - startTime - _nBaseTimestamp_ms; 126 | _timestamp_ms = -1; 127 | //_cap.open(_url); 128 | _cap.set(CV_CAP_PROP_POS_MSEC, 0); 129 | _timestamp_ms = _cap.get(CV_CAP_PROP_POS_MSEC); 130 | _cap >> _matCurrent; 131 | continue; 132 | } 133 | } 134 | } 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /input.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverlight/scene-split/0982b7e19171645cf489c9a4ecd4daafb99cc3bc/input.h -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | CC=g++ 3 | 4 | SRC := CalcHistogram.cpp glcm.cpp scene.cpp tools.cpp v-sfp.cpp input.cpp 5 | OBJ := $(subst .cpp,.o,$(SRC)) 6 | 7 | target := v-sfp 8 | 9 | CFLAGS := -std=c++11 -O3 -I$(OPENCV2_INCLUDE) -L$(OPENCV2_LIB) -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml -lopencv_contrib -lopencv_superres -lopencv_legacy -lopencv_stitching -lopencv_ocl -lopencv_photo -lopencv_ocl -lopencv_objdetect 10 | 11 | %.o: %.cpp 12 | $(CC) -c $(CFLAGS) $< -o $@ 13 | 14 | all: $(target) 15 | 16 | 17 | $(target): $(OBJ) 18 | $(CC) -o $@ $(OBJ) $(CFLAGS) 19 | 20 | install: 21 | cp $(target) /usr/local/bin 22 | 23 | uninstall: 24 | rm -f /usr/local/bin/$(target) 25 | 26 | clean: 27 | rm -f *.o 28 | rm -f $(target) 29 | 30 | -------------------------------------------------------------------------------- /scene.cpp: -------------------------------------------------------------------------------- 1 | #include "CalcHistogram.h" 2 | #include "glcm.h" 3 | #include "scene.h" 4 | 5 | static CalcHistogram g_ch_handle; 6 | static GLCM g_glcm; 7 | 8 | bool comp_bypos(const FrameInfo &a, const FrameInfo &b) 9 | { 10 | return a._pos < b._pos; 11 | } 12 | 13 | bool comp_byfn(const SceneInfo &a, const SceneInfo &b) 14 | { 15 | return a._framenum > b._framenum; 16 | } 17 | 18 | bool comp_by_starttime(const SceneInfo &a, const SceneInfo &b) 19 | { 20 | return a._starttime_ms < b._starttime_ms; 21 | } 22 | 23 | void saveSI(string &basename, SceneInfo &si, int id, vector &fis) 24 | { 25 | string name, strid, str_s, str_e; 26 | int2string(strid, id); 27 | int2string(str_s, si._start); 28 | int2string(str_e, si._end); 29 | 30 | name = basename + "-" + strid + "-" + str_s + "-" + str_e + ".jpg"; 31 | //imwrite( name, fis[si._start]._m256); 32 | imwrite(name, fis[si._end]._m256); 33 | } 34 | 35 | void calcFeature_grayH(SceneInfo &si, vector &fis) 36 | { 37 | g_ch_handle.mat2RGBHist(fis[si._end]._m256, si._rgbHist); 38 | } 39 | 40 | void calcFeature_structfp(SceneInfo &si, vector &fis) 41 | { 42 | Mat_2_structfp(fis[si._end]._m256, si._struct_fp); 43 | } 44 | 45 | void calcFeature_mhash(SceneInfo &si, vector &fis) 46 | { 47 | #if 1 48 | Mat m_sum(256, 256, CV_32FC3, Scalar::all(0)); 49 | for (int j = si._start; j < si._end + 1; j++) 50 | { 51 | addMat(m_sum, fis[j]._m256); 52 | } 53 | //m_sum.convertTo() 54 | m_sum /= si._framenum; 55 | cvtMat(fis[si._end]._m256, m_sum); 56 | #else 57 | sis[i]._m256_mean = fis[(sis[i]._start + sis[i]._end) / 2]._m256.clone(); 58 | #endif 59 | //mat2fp(si._m256_mean, si._mhash); 60 | } 61 | -------------------------------------------------------------------------------- /scene.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENE_H 2 | #define SCENE_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "tools.h" 10 | 11 | using namespace cv; 12 | using namespace std; 13 | 14 | #define SI_NUM_THRESHOLD 10 15 | 16 | typedef struct FrameInfo_s 17 | { 18 | Mat _m256; 19 | int _pos; 20 | int _rgbHist[64]; 21 | int _ts_ms; 22 | } FrameInfo; 23 | 24 | typedef struct SceneInfo_s 25 | { 26 | int _start; 27 | int _end; 28 | int _starttime_ms, _endtime_ms; 29 | int _framenum; 30 | int _mh_score; 31 | //Mat _m256_mean; 32 | //int _mhash[64]; 33 | int _rgbHist[64]; 34 | int _struct_fp[48 * 48]; 35 | float _cosSimi_mean; 36 | } SceneInfo; 37 | 38 | bool comp_byscore(const FrameInfo &a, const FrameInfo &b); 39 | bool comp_bypos(const FrameInfo &a, const FrameInfo &b); 40 | bool comp_byfn(const SceneInfo &a, const SceneInfo &b); 41 | bool comp_by_starttime(const SceneInfo &a, const SceneInfo &b); 42 | void saveSI(string &basename, SceneInfo &si, int id, vector &fis); 43 | 44 | void calcFeature_grayH(SceneInfo &si, vector &fis); 45 | void calcFeature_structfp(SceneInfo &si, vector &fis); 46 | void calcFeature_mhash(SceneInfo &si, vector &fis); 47 | 48 | #endif // SCENE_H 49 | -------------------------------------------------------------------------------- /tools.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverlight/scene-split/0982b7e19171645cf489c9a4ecd4daafb99cc3bc/tools.cpp -------------------------------------------------------------------------------- /tools.h: -------------------------------------------------------------------------------- 1 | #ifndef TOOLS_H 2 | #define TOOLS_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace cv; 11 | using namespace std; 12 | 13 | void Mat_2_structfp(Mat &m, int structfp[48 * 48]); 14 | void mat2vfp(Mat s0, float m, int *vfp); 15 | void mat2fp(Mat img, int *fp); 16 | int calcScore(int *v0, int *v1); 17 | void addMat(Mat &m_sum, Mat &m); 18 | void cvtMat(Mat &m_8u3c, Mat &m_32f3c); 19 | void int2string(string &str, int n); 20 | int calcSAD(Mat m0, Mat m1); 21 | void preprocessMat(Mat &src, Mat &dst); 22 | void preprocessMat2(Mat &src, Mat &dst); 23 | 24 | void getThumbnailBaseName(string &basename, char *vfile, char *tb_path); 25 | 26 | 27 | #endif // TOOLS_H 28 | -------------------------------------------------------------------------------- /v-sfp.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riverlight/scene-split/0982b7e19171645cf489c9a4ecd4daafb99cc3bc/v-sfp.cpp -------------------------------------------------------------------------------- /v-sfp.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {8460E52D-E86B-45B0-9178-10D66CEFA566} 23 | Win32Proj 24 | vsfp 25 | 26 | 27 | 28 | Application 29 | true 30 | v120 31 | Unicode 32 | 33 | 34 | Application 35 | true 36 | v120 37 | Unicode 38 | 39 | 40 | Application 41 | false 42 | v120 43 | true 44 | Unicode 45 | 46 | 47 | Application 48 | false 49 | v120 50 | true 51 | Unicode 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | 72 | 73 | true 74 | $(VC_IncludePath);$(WindowsSDK_IncludePath);$(opencv2_include) 75 | $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(opencv2_lib) 76 | 77 | 78 | false 79 | 80 | 81 | false 82 | $(VC_IncludePath);$(WindowsSDK_IncludePath);$(opencv2_include) 83 | $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(opencv2_lib) 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | 102 | 103 | Level3 104 | Disabled 105 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | opencv_core2412d.lib;opencv_highgui2412d.lib;opencv_imgproc2412d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 112 | 113 | 114 | 115 | 116 | Level3 117 | 118 | 119 | MaxSpeed 120 | true 121 | true 122 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 123 | true 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 141 | true 142 | 143 | 144 | Console 145 | true 146 | true 147 | true 148 | opencv_core2412.lib;opencv_highgui2412.lib;opencv_imgproc2412.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /v-sfp.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 源文件 20 | 21 | 22 | 源文件 23 | 24 | 25 | 源文件 26 | 27 | 28 | 源文件 29 | 30 | 31 | 源文件 32 | 33 | 34 | 源文件 35 | 36 | 37 | 38 | 39 | 头文件 40 | 41 | 42 | 头文件 43 | 44 | 45 | 头文件 46 | 47 | 48 | 头文件 49 | 50 | 51 | 头文件 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /v-sfp.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | D:\\workroom\\dataset\\duopai-video\\dup\\ 5 | WindowsLocalDebugger 6 | dup_346_0.mp4 7 | 8 | 9 | D:\\workroom\\testroom 10 | WindowsLocalDebugger 11 | d:\\ld.mp4 d:\\workroom\\testroom\111.csv d:\\workroom\\testroom 12 | 13 | --------------------------------------------------------------------------------