当前位置:   article > 正文

Python / C++ OpenCV 前后两张截图去重 & 图像找不同_opencv 比较两个图片是否完全一致 c++

opencv 比较两个图片是否完全一致 c++

概述:

  • 场景与任务:判断相邻的两张微信聊天截图是否为同一张(传输压缩、格式转换过程中存在一定像素失真和边缘抖动,不可以直接相减)
  • 要求:使用数字图像处理的方法(仅作为预处理去重,不用深度学习方法);一组(两张)图片判断的时间要求在20ms以内;
  • 思路:
    • 转换到HSV空间下,先将聊天窗口通过颜色阈值单独分离出来;
    • 轮廓查找判断两张图的闭包矩形框的数目是否相同,不同则为不同截图;
    • 否则再利用ORB提取特征点(keypoints)和描述子(descriptors),计算两张图对应特征点的斜率,若60%的线条均值在0附近(前后各去掉最大最小的20%斜率的曲线),则为同一张图,否则为不同张;
    • Python 测试成功后转写成 C++ 版本以适应速度需求;

Python 代码:

  1. import numpy as np
  2. import pandas as pd
  3. import matplotlib.pyplot as plt
  4. import cv2
  5. import os, glob, time
  6. orb = cv2.ORB_create()
  7. def read_img(name1, name2):
  8. img1 = cv2.imread(name1, 1)
  9. img2 = cv2.imread(name2)
  10. h, w = min(img1.shape[0], img2.shape[0]), min(img1.shape[1], img2.shape[1])
  11. img1 = img1[:h, :w]
  12. img2 = img2[:h, :w]
  13. img1 = cv2.resize(img1, (800, 800 * img1.shape[0] // img1.shape[1]))
  14. img2 = cv2.resize(img2, (800, 800 * img2.shape[0] // img2.shape[1]))
  15. return img1, img2
  16. def color_enhance(img):
  17. hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
  18. lower_green = np.array([35, 43, 46])
  19. upper_green = np.array([77, 255, 255])
  20. mask_green = cv2.inRange(hsv, lowerb=lower_green, upperb=upper_green)
  21. lower_white = np.array([0, 0, 245])
  22. upper_white = np.array([180, 30, 255])
  23. mask_white = cv2.inRange(hsv, lowerb=lower_white, upperb=upper_white)
  24. dst_w = cv2.bitwise_and(img, img, mask=mask_white)
  25. dst_g = cv2.bitwise_and(img, img, mask=mask_green)
  26. dst = dst_w + dst_g
  27. return dst
  28. def count_box(img, show=False):
  29. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  30. thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 9, 2)
  31. contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  32. H, W = img.shape[:2]
  33. count = 0
  34. for contour in contours:
  35. if cv2.contourArea(contour) < H * W / 500 or H > W * 1.1:
  36. continue
  37. count += 1
  38. x, y, w, h = cv2.boundingRect(contour)
  39. cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
  40. if show:
  41. cv2.imshow('img', img)
  42. cv2.imshow('gray', gray)
  43. cv2.imshow('thresh', thresh)
  44. cv2.waitKey(0)
  45. return count
  46. def orb_match(img1, img2):
  47. kp1, des1 = orb.detectAndCompute(img1, None) # des是描述子
  48. kp2, des2 = orb.detectAndCompute(img2, None)
  49. return kp1, des1, kp2, des2
  50. def draw_keypoints(img, keypoints, color=(0, 255, 255)):
  51. for kp in keypoints:
  52. x, y = kp.pt
  53. cv2.circle(img, (int(x), int(y)), 2, color)
  54. return img
  55. def match_imgs(des1, des2):
  56. bf = cv2.BFMatcher()
  57. matches = bf.knnMatch(des1, des2, k=2)
  58. good = []
  59. for m, n in matches:
  60. if m.distance < 0.8 * n.distance:
  61. good.append([m])
  62. return good
  63. def compute_slope(src, dst):
  64. # slope = (y - y') / (x' - x + 800)
  65. return (src[1] - dst[1]) / (dst[0] - src[0] + 800)
  66. def judge(img1, img2, show=False):
  67. img3, img4 = color_enhance(img1), color_enhance(img2)
  68. n1 = count_box(img3)
  69. n2 = count_box(img4)
  70. if n1 != n2:
  71. print('n1, n2: ', n1, n2)
  72. return False
  73. kp1, des1, kp2, des2 = orb_match(img3, img4)
  74. good = match_imgs(des1, des2)
  75. src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 2)
  76. dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 2)
  77. all_slopes = []
  78. for i in range(len(src_pts)):
  79. all_slopes.append(compute_slope(src_pts[i], dst_pts[i]))
  80. all_slopes.sort()
  81. len_s = len(all_slopes) // 5
  82. filtered_slopes = all_slopes[len_s:-len_s]
  83. slopes = filtered_slopes if filtered_slopes else all_slopes
  84. if show:
  85. slopes = pd.Series(slopes)
  86. # print(slopes.describe())
  87. fig = plt.figure()
  88. ax = fig.add_subplot(111)
  89. ax.hist(slopes, bins=20, color='blue', alpha=0.8)
  90. plt.show()
  91. img5 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=2)
  92. thresh_merge = np.hstack((img3, img4))
  93. cv2.imshow("thresh_merge", thresh_merge)
  94. visual_1 = draw_keypoints(img1, kp1, color=(255, 0, 255))
  95. visual_2 = draw_keypoints(img2, kp2, color=(255, 0, 255))
  96. hmerge = np.hstack((visual_1, visual_2))
  97. cv2.imshow("point", hmerge)
  98. cv2.imshow("ORB", img5)
  99. cv2.waitKey(0)
  100. cv2.destroyAllWindows()
  101. slopes_mean = sum(slopes) / len(slopes)
  102. print('abs slope mean: ', abs(slopes_mean))
  103. if abs(slopes_mean) < 0.01:
  104. return True
  105. else:
  106. return False
  107. if __name__ == '__main__':
  108. name1, name2 = './1.png', './2.png'
  109. img1, img2 = read_img(name1, name2)
  110. if judge(img1, img2, show=True):
  111. print('Same screenshots.')
  112. else:
  113. print('Different screenshots.')

 

C++ 代码(去掉了颜色增强部分):

JudgeDuplicates.h

  1. #ifndef JUDGEDUPLICATES_H
  2. #define JUDGEDUPLICATES_H
  3. #include <cstdlib>
  4. #include <iostream>
  5. #include <vector>
  6. #include <opencv2/opencv.hpp>
  7. #include <opencv2/core/core.hpp>
  8. #include <opencv2/highgui/highgui.hpp>
  9. #include <opencv2/features2d/features2d.hpp>
  10. class JudgeDuplicates
  11. {
  12. public:
  13. JudgeDuplicates();
  14. void orb_match(cv::Mat, cv::Mat,
  15. std::vector<cv::KeyPoint>&,
  16. std::vector<cv::KeyPoint>&,
  17. std::vector<cv::DMatch>&);
  18. double compute_slope(cv::Point, cv::Point);
  19. bool judge(std::string, std::string);
  20. virtual ~JudgeDuplicates();
  21. protected:
  22. private:
  23. };
  24. #endif // JUDGEDUPLICATES_H

JudgeDuplicates.cpp

  1. #include "JudgeDuplicates.h"
  2. JudgeDuplicates::JudgeDuplicates()
  3. {
  4. //ctor
  5. }
  6. JudgeDuplicates::~JudgeDuplicates()
  7. {
  8. //dtor
  9. }
  10. void JudgeDuplicates::orb_match(cv::Mat img1, cv::Mat img2,
  11. std::vector<cv::KeyPoint>& kp1,
  12. std::vector<cv::KeyPoint>& kp2,
  13. std::vector<cv::DMatch>& goodmatches){
  14. int Hessian = 500;
  15. cv::Ptr<cv::ORB> detector = cv::ORB::create(Hessian);
  16. cv::Mat des1, des2;
  17. detector->detectAndCompute(img1, cv::Mat(), kp1, des1);
  18. detector->detectAndCompute(img2, cv::Mat(), kp2, des2);
  19. cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce");
  20. std::vector<std::vector<cv::DMatch> > matches_knn;
  21. matcher->knnMatch(des1, des2, matches_knn, 2);
  22. for (size_t i = 0; i < matches_knn.size(); ++i){
  23. if(matches_knn[i][0].distance < 0.8 * matches_knn[i][1].distance){
  24. goodmatches.push_back(matches_knn[i][0]);
  25. }
  26. }
  27. }
  28. double JudgeDuplicates::compute_slope(cv::Point src, cv::Point dst){
  29. // slope = (y - y') / (x' - x + 800)
  30. return double(src.y - dst.y) / double(dst.x - src.x + 800.0);
  31. }
  32. bool JudgeDuplicates::judge(std::string name1, std::string name2){
  33. cv::Mat img1 = cv::imread(name1, 1);
  34. cv::Mat img2 = cv::imread(name2, 1);
  35. int h1 = img1.rows;
  36. int w1 = img1.cols;
  37. cv::resize(img1, img1, cv::Size(800, int(800 * h1 / w1)));
  38. cv::resize(img2, img2, cv::Size(800, int(800 * h1 / w1)));
  39. std::vector<cv::KeyPoint> kp1, kp2;
  40. std::vector<cv::DMatch> good_matches;
  41. orb_match(img1, img2, kp1, kp2, good_matches);
  42. std::cout << good_matches.size() << std::endl;
  43. std::vector<cv::Point> src_pts, dst_pts;
  44. for(size_t i = 0; i < good_matches.size(); ++i){
  45. int x1 = kp1[good_matches[i].queryIdx].pt.x;
  46. int y1 = kp1[good_matches[i].queryIdx].pt.y;
  47. int x2 = kp2[good_matches[i].trainIdx].pt.x;
  48. int y2 = kp2[good_matches[i].trainIdx].pt.y;
  49. cv::Point src_pt = cv::Point(x1, y1);
  50. cv::Point dst_pt = cv::Point(x2, y2);
  51. src_pts.push_back(src_pt);
  52. dst_pts.push_back(dst_pt);
  53. }
  54. double slope, mean_slope = 0.0;
  55. std::vector<double> slopes;
  56. for(size_t i = 0; i < src_pts.size(); ++i){
  57. slope = compute_slope(src_pts[i], dst_pts[i]);
  58. slopes.push_back(slope);
  59. }
  60. sort(slopes.begin(), slopes.end());
  61. int line_cnt = 0;
  62. for(size_t i = 0; i < slopes.size(); ++i){
  63. if(i < slopes.size() * 0.2){
  64. continue;
  65. }
  66. if(i > slopes.size() * 0.8){
  67. break;
  68. }
  69. line_cnt += 1;
  70. mean_slope += fabs(slopes[i]);
  71. std::cout << slopes[i] << std::endl;
  72. }
  73. if(line_cnt != 0){
  74. mean_slope /= line_cnt;
  75. }
  76. else{
  77. mean_slope = 1000000;
  78. }
  79. std::cout << mean_slope << " line_cnt " << line_cnt << std::endl;
  80. if(mean_slope < 0.001)
  81. return true;
  82. else
  83. return false;
  84. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/103681
推荐阅读
相关标签
  

闽ICP备14008679号