opencv_size.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. # coding=utf-8
  2. # 导入一些后续需要使用到的python包
  3. # 可能需要 pip install imutils
  4. from scipy.spatial import distance as dist
  5. from imutils import perspective
  6. from imutils import contours
  7. import numpy as np
  8. import argparse
  9. import imutils
  10. import cv2
  11. # 定义一个中点函数,后面会用到
  12. def midpoint(ptA, ptB):
  13. return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)
  14. # 设置一些需要改变的参数
  15. ap = argparse.ArgumentParser()
  16. ap.add_argument("-i", "--image", required=True,
  17. help="path to the input image")
  18. ap.add_argument("-w", "--width", type=float, required=True,
  19. help="width of the left-most object in the image (in inches)")
  20. args = vars(ap.parse_args())
  21. # 读取输入图片
  22. image = cv2.imread(args["image"])
  23. # 输入图片灰度化
  24. gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  25. # 对灰度图片执行高斯滤波
  26. gray = cv2.GaussianBlur(gray, (7, 7), 0)
  27. # 对滤波结果做边缘检测获取目标
  28. edged = cv2.Canny(gray, 50, 100)
  29. # 使用膨胀和腐蚀操作进行闭合对象边缘之间的间隙
  30. edged = cv2.dilate(edged, None, iterations=1)
  31. edged = cv2.erode(edged, None, iterations=1)
  32. # 在边缘图像中寻找物体轮廓(即物体)
  33. cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
  34. cv2.CHAIN_APPROX_SIMPLE)
  35. cnts = imutils.grab_contours(cnts)
  36. # 对轮廓按照从左到右进行排序处理
  37. (cnts, _) = contours.sort_contours(cnts)
  38. # 初始化 'pixels per metric'
  39. pixelsPerMetric = None
  40. # 循环遍历每一个轮廓
  41. for c in cnts:
  42. # 如果当前轮廓的面积太少,认为可能是噪声,直接忽略掉
  43. if cv2.contourArea(c) < 100:
  44. continue
  45. # 根据物体轮廓计算出外切矩形框
  46. orig = image.copy()
  47. box = cv2.minAreaRect(c)
  48. box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
  49. box = np.array(box, dtype="int")
  50. # 按照top-left, top-right, bottom-right, bottom-left的顺序对轮廓点进行排序,并绘制外切的BB,用绿色的线来表示
  51. box = perspective.order_points(box)
  52. cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2)
  53. # 绘制BB的4个顶点,用红色的小圆圈来表示
  54. for (x, y) in box:
  55. cv2.circle(orig, (int(x), int(y)), 5, (0, 0, 255), -1)
  56. # 分别计算top-left 和top-right的中心点和bottom-left 和bottom-right的中心点坐标
  57. (tl, tr, br, bl) = box
  58. (tltrX, tltrY) = midpoint(tl, tr)
  59. (blbrX, blbrY) = midpoint(bl, br)
  60. # 分别计算top-left和top-right的中心点和top-righ和bottom-right的中心点坐标
  61. (tlblX, tlblY) = midpoint(tl, bl)
  62. (trbrX, trbrY) = midpoint(tr, br)
  63. # 绘制BB的4条边的中心点,用蓝色的小圆圈来表示
  64. cv2.circle(orig, (int(tltrX), int(tltrY)), 5, (255, 0, 0), -1)
  65. cv2.circle(orig, (int(blbrX), int(blbrY)), 5, (255, 0, 0), -1)
  66. cv2.circle(orig, (int(tlblX), int(tlblY)), 5, (255, 0, 0), -1)
  67. cv2.circle(orig, (int(trbrX), int(trbrY)), 5, (255, 0, 0), -1)
  68. # 在中心点之间绘制直线,用紫红色的线来表示
  69. cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)),
  70. (255, 0, 255), 2)
  71. cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)),
  72. (255, 0, 255), 2)
  73. # 计算两个中心点之间的欧氏距离,即图片距离
  74. dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY))
  75. dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))
  76. # 初始化测量指标值,参考物体在图片中的宽度已经通过欧氏距离计算得到,参考物体的实际大小已知
  77. if pixelsPerMetric is None:
  78. pixelsPerMetric = dB / args["width"]
  79. # 计算目标的实际大小(宽和高),用英尺来表示
  80. dimA = dA / pixelsPerMetric
  81. dimB = dB / pixelsPerMetric
  82. # 在图片中绘制结果
  83. cv2.putText(orig, "{:.1f}in".format(dimB),
  84. (int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX,
  85. 0.65, (255, 255, 255), 2)
  86. cv2.putText(orig, "{:.1f}in".format(dimA),
  87. (int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX,
  88. 0.65, (255, 255, 255), 2)
  89. # 显示结果
  90. cv2.imshow("Image", orig)
  91. cv2.waitKey(0)