本文是将俩张图片进行比对,取上面的差异,系数根据需求自己设定
因为是比对,俩张差异值有问题的就是标注出来
本文用mqtt和前端对接,将对比的图片放到oss中
import datetime
import math
import random
import urllib
import uuid
import cv2
import numpy as np
import oss2
import paho.mqtt.client as mqtt
import json
# 订阅主题
subTopic ='task/image/disobey/handle'
# 发布通知主题
pushNoticeTopic ='task/image/disobey/confirm'
# 发布主题
pushTopic ='task/image/disobey/original'
access_key = "xxxx"
secret_key = "xxxxx"
endpoint = "xxxx"
bucket_name = "xxxx"
auths = oss2.Auth(access_key, secret_key)
bucket = oss2.Bucket(auths, endpoint, bucket_name)
def MeanShift(input, r):
classification = []
startNum = 60 # 起始点数量
radium = r # 窗口半径
num = len(input) # 样本数量
sample = np.int32([[0, 0, 0] for m in range(num)]) # 添加分类信息 0为未分类
for i in range(num):
sample[i][0] = input[i][0]
sample[i][1] = input[i][1]
# 随机选择一个起始点
for i in range(startNum):
# 范围
ptr = random.randint(0, num - 1)
# 记录分类中心点
center = [0, 0]
center[0] = sample[ptr][0]
center[1] = sample[ptr][1]
Flag = 0
# 判断终止条件
iteration = 0
while ((Flag == 0) & (iteration < 10)):
orientation = [0, 0] # 移动方向
# 找出窗口内的所有样本点
for j in range(num):
oX = sample[j][0] - center[0]
oY = sample[j][1] - center[1]
dist = math.sqrt(oX * oX + oY * oY)
# 该点在观察窗内
if dist <= radium:
orientation[0] = orientation[0] + oX / 20
orientation[1] = orientation[1] + oY / 20
# 开始漂移
center[0] = center[0] + orientation[0]
center[1] = center[1] + orientation[1]
# 中心点不再移动时
oX = orientation[0]
oY = orientation[1]
iteration = iteration + 1
if math.sqrt(oX * oX + oY * oY) < 3:
Flag = 1
# 添加不重复的新分类信息
Flag = 1
for i in range(len(classification)):
# 与当前存在的分类位置差别小于5
oX = classification[i][0] - center[0]
oY = classification[i][1] - center[1]
if math.sqrt(oX * oX + oY * oY) < math.sqrt(classification[i][2]) + 30:
Flag = 0
break
if Flag == 1:
temp = [center[0], center[1], 0]
classification.append(temp)
# 给所有样本点分类
for i in range(num):
Index = 0
minValue = 99999
# 找出最近的分类
for j in range(len(classification)):
xx = classification[j][0] - sample[i][0]
yy = classification[j][1] - sample[i][1]
distination = abs(xx * xx + yy * yy)
if distination <= minValue:
Index = j
minValue = distination
sample[i][2] = Index
classification[Index][2] = classification[Index][2] + 1
return classification
def compare_images(client,imge1, imge2,num,list,data):
try:
out = None
# 如果图片中包含细节丰富的物体或者纹理,则需要设置更高的 detectDensity 值,以保证对差异区域的检测和定位。一般来说,可以将 detectDensity 设为 30-50 左右。
# 如果图片中包含大面积连续的颜色或者背景,则可以适当降低 detectDensity 的值,以减少计算量和提高速度。一般来说,可以将 detectDensity 设为 10-20 左右。
# 如果需要检测的差异区域比较小,例如像素级别的差异,则需要设置更高的 detectDensity 值,并可能需要进一步调整参数。具体的取值范围需要根据实际应用情况进行调试和优化。
detectDensity = 2
# 分辨率
# resolution=(1079, 890)
# 窗口半径
windowSize = 40
# 最大值
shreshood = 350
a = 1 # 显示比例
# 加载原始图片和目标图片
# 读取互联网图片需要先加载网络图片,然后将其转换为numpy数组
resp1 = urllib.request.urlopen(imge1)
image1 = np.asarray(bytearray(resp1.read()), dtype="uint8")
img1 = cv2.imdecode(image1, cv2.IMREAD_COLOR)
resp2 = urllib.request.urlopen(imge2)
image2 = np.asarray(bytearray(resp2.read()), dtype="uint8")
img2 = cv2.imdecode(image2, cv2.IMREAD_COLOR)
# img1 = cv2.imread(imge1)
# img2 = cv2.imread(imge1)
# img1 = cv2.resize(img1, resolution)
# img2 = cv2.resize(img2, resolution)
# 调整分辨率,
if img1.shape[0] < img1.shape[1]:
img1 = cv2.resize(img1, dsize=(600, int(600 * img1.shape[0] / img1.shape[1])))
img2 = cv2.resize(img2, dsize=(600, int(600 * img1.shape[0] / img1.shape[1])))
else:
img1 = cv2.resize(img1, dsize=(int(600 * img1.shape[1] / img1.shape[0]), 600))
img2 = cv2.resize(img2, dsize=(int(600 * img1.shape[1] / img1.shape[0]), 600))
sift = cv2.SIFT_create()
# 检测关键点
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 这段代码定义了使用 Fast Library for Approximate Nearest Neighbors (FLANN) 模块的 k-d tree 算法进行近似最近邻搜索时的索引参数和搜索参数,
# 并将其保存在变量 index_params 和 search_params 中。
# 其中,FLANN_INDEX_KDTREE 指定了使用 k-d tree 算法进行索引,trees=6 表示使用 6 棵树来构建索引。
# 这里的 k-d tree 算法是一种基于二叉树的数据结构,能够以 O(log n) 的复杂度对 N 个点进行搜索和插入,适用于高维度的数据集。
# 而搜索参数 checks=10 则指定了搜索过程中需要检查的次数,值越大则结果越准确但速度越慢。在实际使用中,可以根据需要调整该参数以达到合适的平衡
# 关键点匹配
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=6)
search_params = dict(checks=20)
# 这段代码使用 OpenCV 提供的 FlannBasedMatcher 类进行基于 FLANN 算法的特征匹配,
# 其中 index_params 和 search_params 分别为使用 k-d tree 算法进行索引和搜索时所需的参数。
# 具体来说,cv2.FlannBasedMatcher() 函数可以传入索引参数和搜索参数,返回一个 Matcher 对象。
# 该对象可以使用 matcher.knnMatch() 函数进行特征匹配,其中 des1 和 des2 分别为待匹配图像的特征描述符。
# 在进行特征匹配时,函数会查找与每个特征描述符最相似的一组匹配,并返回这些匹配对应的特征点对。参数 k 表示对于每个特征描述符需要返回多少个最佳匹配以供选择,默认为 2
# 需要注意的是,由于 FLANN 算法是基于近似最近邻搜索的,因此在进行特征匹配时可能存在误匹配的情况。
# 如果需要进一步提高特征匹配的准确率,可以考虑使用其他算法和技术,例如 RANSAC、Lowe's ratio test 等。
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
good = []
# m是最佳匹配,n是次佳匹配。如果最佳匹配与次佳匹配之间的距离相差很大(即m.distance远小于n.distance)
# ,则说明它们是比较容易区分的,这个最佳匹配就可能是正确的匹配,否则可能是误匹配。
# 所以这里将阈值设置为0.7,若满足阈值要求,则将最佳匹配加入good列表中。这种策略可以有效消除误匹配,提高匹配准确率。
for m, n in matches:
if m.distance < 0.7 * n.distance:
good.append(m)
# 把good中的左右点分别提出来找单应性变换
pts_src = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
pts_dst = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
# 单应性变换
M, mask = cv2.findHomography(pts_src, pts_dst, cv2.RANSAC, 5.0)
# M矩阵中xy方向的偏移量
dX = M[0][2] # x方向 负为左比右小
dY = M[1][2] # y方向 负为左比右小
# 图像 img1 的长、宽和通道数,并将长和宽保存在变量 height 和 width 中。
# 接着根据图像的宽度设定了关键点的尺度,其中乘以了0.01,这个值一般根据实际应用而定,可以适当调整。需要注意的是,这里计算出来的 size 是一个整数,其单位是像素
# 图像的长宽
height, width, channel = img1.shape
# 设定关键点的尺度
size = int(width * 0.01)
# 自动选择采样点的位置范围
xMinLeft = width
xMaxLeft = 0
yMinLeft = height
yMaxLeft = 0
xMinRight = width
xMaxRight = 0
yMinRight = height
yMaxRight = 0
# pts_src 是源图像中的关键点集,mask 对应每个关键点的掩膜值,将匹配成功的关键点的掩膜值设为 1。
# xMinLeft、yMinLeft、xMaxLeft 和 yMaxLeft 分别表示目标区域左上角和右下角的坐标限制,这些值是在后续的代码中使用的。
# 这里需要注意的是,变量名中的 Left 可能表示匹配成功的关键点集所属的图片是左图,而不是表示方向
# 用当前匹配成功的点集分析合适的检测范围
for i in range(len(pts_src)):
if mask[i] == 1:
if pts_src[i][0][1] < yMinLeft:
yMinLeft = pts_src[i][0][1]
if pts_src[i][0][1] > yMaxLeft:
yMaxLeft = pts_src[i][0][1]
if pts_src[i][0][0] < xMinLeft:
xMinLeft = pts_src[i][0][0]
if pts_src[i][0][0] > xMaxLeft:
xMaxLeft = pts_src[i][0][0]
for i in range(len(pts_dst)):
if mask[i] == 1:
if pts_dst[i][0][1] < yMinRight:
yMinRight = pts_dst[i][0][1]
if pts_dst[i][0][1] > yMaxRight:
yMaxRight = pts_dst[i][0][1]
if pts_dst[i][0][0] < xMinRight:
xMinRight = pts_dst[i][0][0]
if pts_dst[i][0][0] > xMaxRight:
xMaxRight = pts_dst[i][0][0]
xMinLeft = xMinLeft + 2 * size
yMinLeft = yMinLeft + 3 * size
# detectDensity 表示密度,size 表示尺寸,二者相乘得到的 interval 表示监测点之间的间隔。在这个场景下,监测点是指在目标区域内均匀分布的像素点,监测点之间的距离越近,检测的精度就越高。
# 具体来说,如果 detectDensity 是一个整数,那么 interval 就等于每个像素边长为 size 的正方形内,
# 沿水平和垂直方向上均匀分布的 detectDensity * detectDensity 个监测点之间的距离。
# 如果 detectDensity 是一个小数,则最终的监测点数量可能会稍微偏离 detectDensity * detectDensity,但基本上也是均匀分布的。
# 检测范围确定
interval = detectDensity * size # 监测点间隔
# 这段代码主要是用于生成一个搜索区域的坐标点集合 demo_src,其大小和数量由 searchWidth 和 searchHeight 参数决定。
# 首先根据 xMinLeft、 xMaxLeft、 yMinLeft、 yMaxLeft 等边界信息计算出需要搜索的区域大小,然后按照指定的间隔 interval 在该区域内生成一组坐标点。
# 具体来说:
# 1) searchWidth = int((xMaxLeft - xMinLeft) / interval - 2) 表示计算左侧目标物体宽度方向上的搜索格子数目,并取整。
# 其中 (xMaxLeft - xMinLeft) / interval - 2 表示根据左侧目标物体的坐标范围以及指定的搜索间隔,计算出总共需要多少个搜索格子,而取整操作则是为了确保搜索区域边界内的所有格子都被包括在内。
# 2) searchHeight = int((yMaxLeft - yMinLeft) / interval - 2) 同理,表示计算左侧目标物体高度方向上的搜索格子数目,并取整。
# 3) searchNum = searchWidth * searchHeight 表示计算总共需要进行多少次搜索,即搜索点的数量。
# 4) demo_src = np.float32([[0] * 2] * searchNum * 1).reshape(-1, 1, 2) 表示生成一个大小为 searchNum 的二维坐标数组 demo_src,用于存储搜索点的位置信息。
# 其中 [[0] * 2] * searchNum * 1 表示重复 [[0] * 2] 这个二元数组 searchNum * 1 次,然后将其扁平化为一维数组并转化为浮点数类型,最后调整为 (searchNum, 1, 2) 的三维数组形式。
# 然后在双重循环中,枚举所有的搜索格子,计算每一个格子对应的坐标点位置,并将其保存到 demo_src 数组中。
# 这段代码的目的是为了生成一个规则的搜索区域坐标集合,方便进行后续的图像处理和目标物体检测操作。需要根据具体需求和场景来定制参数值和精细化调整算法逻辑,以获得较好的检测效果。
searchWidth = int((xMaxLeft - xMinLeft) / interval - 2)
searchHeight = int((yMaxLeft - yMinLeft) / interval - 2)
searchNum = searchWidth * searchHeight
demo_src = np.float32([[0] * 2] * searchNum * 1).reshape(-1, 1, 2)
for i in range(searchWidth):
for j in range(searchHeight):
demo_src[i + j * searchWidth][0][0] = xMinLeft + i * interval + size
demo_src[i + j * searchWidth][0][1] = yMinLeft + j * interval + size
# 对搜索区域内的所有坐标点进行单应性变换,并将结果保存到 demo_dst 变量中。其中 M 表示单应性矩阵,用于描述左图和右图之间的几何转换关系,也就是把左图映射到右图中的操作。
# 在使用 cv2.perspectiveTransform() 函数时,需要传入两个参数:
# demo_src:输入的二维浮点数数组,表示需要进行单应性变换的源坐标点集合。
# M:输出的二维浮点数数组,表示变换矩阵,用于描述源坐标点集合与目标坐标点集合之间的映射关系。
# 该函数在执行时,会根据输入的变换矩阵 M,将源坐标点集合中的每一个坐标点,通过单应性变换映射到目标坐标点集合中,最后将映射后的目标坐标点集合保存到 demo_dst 数组中。
# 单应性变换 左图映射到右图的位置
demo_dst = cv2.perspectiveTransform(demo_src, M)
# 这段代码主要是为了实现将两张图像进行拼接的操作。
# 其中:
# . heightO = max(img1.shape[0], img2.shape[0]) 表示计算输出图像的高度,选择两张图像中的最大高度作为输出图像的高度。
# . widthO = img1.shape[1] + img2.shape[1] 表示计算输出图像的宽度,选择两张图像的宽度之和作为输出图像的宽度。
# . output = np.zeros((heightO, widthO, 3), dtype=np.uint8) 表示生成一个大小为 (heightO, widthO, 3) 的三维数组 output,用于存储输出图像的像素数据。
# 该数组的元素类型为 np.uint8,即 8 位无符号整数类型。
# . output[0:img1.shape[0], 0:img1.shape[1]] = img1 表示将第一张图像 img1 复制到输出图像 output 的左侧部分,
# 即从 (0, 0) 到 (img1.shape[0], img1.shape[1]) 区域。
# . output[0:img2.shape[0], img2.shape[1]:] = img2[:] 表示将第二张图像 img2 复制到输出图像 output 的右侧部分,
# 即从 (0, img1.shape[1]) 到 (img2.shape[0], img1.shape[1]+img2.shape[1]) 区域。
# 需要注意的是,这里通过 img2[:] 的方式进行了复制操作,可以保证 img2 复制后的数据类型和 output 一致。
# 把差异点画出来
# output2
output2 = output=img1
# 这段代码主要是将之前获得的坐标点数组 demo_src 和 demo_dst 转换成了 OpenCV 库中的 KeyPoint 类型。
# 这个类型通常用于描述图像中的关键点(即具有特殊性质的像素点),可以在很多计算机视觉领域的算法中使用,如图像分类、对象检测、相似性匹配等。
# 在转换成 KeyPoint 类型时,该段代码使用了一个列表推导式,分别遍历了 demo_src 和 demo_dst 中的每一个坐标点,
# 然后将其封装成了一个 cv2.KeyPoint 对象,其中 size 参数表示关键点的大小,可以根据实际需求进行设置。
# 需要注意的是,在使用 cv2.KeyPoint() 函数时,需要传入三个参数:
# . x:关键点的 x 坐标;
# . y:关键点的 y 坐标;
# . size:关键点的大小或半径,通常使用正整数表示。
# 此外,还可以通过设置其他可选参数,如 angle 表示关键点方向、response 表示响应值等。转换成 KeyPoint 类型后,这些像素点就可以用于接下来的特征提取和匹配操作
# 转换成KeyPoint类型
kp_src = [cv2.KeyPoint(demo_src[i][0][0], demo_src[i][0][1], size)
for i in range(demo_src.shape[0])]
kp_dst = [cv2.KeyPoint(demo_dst[i][0][0], demo_dst[i][0][1], size)
for i in range(demo_dst.shape[0])]
# 具体来说,sift.compute() 函数接受两个参数:
# img:要计算描述子的图像(一个灰度图像或颜色图像);
# keypoints:一个关键点列表,由 cv2.KeyPoint 类型的对象组成。
# 经过计算后,该函数返回两个值:
# keypoints:一个更新的关键点列表,其中每个关键点都包含了新的描述子信息(即被替换为了 SIFT 描述子);
# descriptors:一个 N×128 的数组,其中 N 表示关键点的数量,128 表示每个描述子的维度。
# 计算这些关键点的SIFT描述子
keypoints_image1, descriptors_image1 = sift.compute(img1, kp_src)
keypoints_image2, descriptors_image2 = sift.compute(img2, kp_dst)
# 差异点
diffLeft = []
diffRight = []
# 这段代码主要是对左右两幅图像中的 SIFT 描述子进行比较,并筛选出差异较小的关键点对。
# 具体来说,该代码使用了一个双重循环,将所有关键点对进行遍历。
# 在每个关键点对比较时,将它们的描述子看作一个 128 维的向量,计算它们之间的欧氏距离,得到它们的差异值 difference。
# 然后根据设定的阈值 shreshood 对 difference 进行判断,如果 difference 小于等于 shreshood 则认为两个关键点相似,否则认为不相似。
# 对于相似的关键点对(即 difference 小于等于 shreshood 的),可以根据需求选择绘制它们的连接线或保存它们的坐标。
# 需要注意的是,在绘制连接线时,代码使用了一些计算将右图中的关键点坐标转换成了与左图同一坐标系下的坐标,以便更好地展示匹配结果。
# 最后,值得注意的是,该代码中对差异值的计算方法可能并不是最优的,因为它只是简单地完成了元素之间的差值计算和平方求和,而没有考虑特征向量之间的相关性。
# 因此,对于更高质量和更准确的匹配结果,可以使用其他更复杂和更精细的特征描述子或匹配算法
# 分析差异
for i in range(searchNum):
nowShreshood = shreshood
difference = 0
for j in range(128):
d = abs(descriptors_image1[i][j] - descriptors_image2[i][j])
difference = difference + d * d
difference = math.sqrt(difference)
# 右图关键点位置不超出范围
if (demo_dst[i][0][1] >= 0) & (demo_dst[i][0][0] >= 0):
if difference > nowShreshood:
# cv2.circle(output, (int(demo_src[i][0][0]),int(demo_src[i][0][1])),1, (0, 0, 255), 2)
# cv2.circle(output, (int(demo_dst[i][0][0]+width),int(demo_dst[i][0][1])),1, (0, 0, 255), 2)
diffLeft.append([demo_src[i][0][0], demo_src[i][0][1]])
diffRight.append([demo_dst[i][0][0], demo_dst[i][0][1]])
if diffLeft:
outLeft = MeanShift(diffLeft, windowSize)
# 实现了对一组二维坐标点坐标值的透视变换。
# 首先,将左图中得到的一组二维坐标点 outLeft 转换为 left 矩阵。其中 left 为大小为 num x 1 x 2 的 np.float32 类型的空数组。
# 然后,通过一个循环将 outLeft 中的 X 和 Y 坐标值依次赋给 left 中对应坐标位置,生成一个大小为 num x 1 x 2 的矩阵。
# 接着,利用 OpenCV 的 cv2.perspectiveTransform() 函数,将 left 矩阵通过透视变换转换为右图中对应的二维坐标点 outRight。
# 其中 M 是透视变换矩阵,通过调用 cv2.getPerspectiveTransform() 函数得到。函数返回的 M 矩阵可以将左图中的坐标点转换成右图中的坐标点。
# 最后,通过一个循环,将 right 矩阵中的 X 和 Y 坐标值依次赋给 outRight 中的对应坐标位置,
# 并将 outLeft 矩阵中对应坐标位置上的值 outLeft[i][2] 赋给 outRight 中对应坐标位置上的值 outRight[i][2]。
# 这样便实现了左图中所有的坐标点在透视变换后得到了右图中对应的坐标点。
# 需要注意的是,在循环体中对 right 和 outRight 进行赋值操作时,需使用不同的下标变量来遍历二维坐标点,避免将错误的值赋给相应的坐标位置。
# 总之,这段代码可以实现图像处理中的基础变换操作,常常用于目标检测、特征匹配等领域中。
left = np.float32([[0] * 2] * len(outLeft) * 1).reshape(-1, 1, 2)
for i in range(len(outLeft)):
left[i][0][0] = outLeft[i][0]
left[i][0][1] = outLeft[i][1]
right = cv2.perspectiveTransform(left, M)
outRight = [[0 for x in range(3)] for y in range(len(outLeft))]
for i in range(len(outLeft)):
outRight[i][0] = right[i][0][0]
outRight[i][1] = right[i][0][1]
outRight[i][2] = outLeft[i][2]
# 这段代码主要是在透视变换后的图像上绘制圆圈,并标记出目标区域。
# 首先,在第一个 for 循环中,遍历左图中的每一个二维坐标点 outLeft[i]。
# 如果该坐标点对应的权值(即 outLeft[i][2])大于 weight,就在 output2 图像上绘制一个圆圈,并标记出该点所在的目标区域。
# 其中 output2 为指定大小的 np.uint8 类型的灰度图像。该圆圈的半径是通过 np.sqrt(outLeft[i][2]) * weight 计算得到,颜色为 (0, 0, 255)。
# 此外,int() 函数把点坐标和半径强制转为整数。
# 接着,在第二个 for 循环中,遍历右图中的每一个二维坐标点 outRight[i]。类似于左图,也是通过判断权值是否大于 weight,来决定在 output2 图像中绘制圆圈并标记出目标区域。
# 不同之处在于,此时需要加上一个偏移量 width,把圆心的横坐标加上 width,使圆圈能够显示在右图的对应位置上。另外,圆圈的颜色为 (255, 255, 0)。
# 将点数大于10的类画出来 点数不足10认为是错误导致的
weight = 5
for i in range(len(outLeft)):
if outLeft[i][2] > weight:
cv2.circle(output2, (int(outLeft[i][0]), int(outLeft[i][1])), int(np.sqrt(outLeft[i][2])) * weight,
(0, 0, 255),
2)
for i in range(len(outRight)):
if outRight[i][2] > weight:
cv2.circle(output2, (int(outRight[i][0]) + width, int(outRight[i][1])),
int(np.sqrt(outRight[i][2])) * weight,
(255, 255, 0), 2)
# 输出结果
out = cv2.resize(output2, (int(output.shape[1] * a), int(output.shape[0] * a)),
interpolation=cv2.INTER_CUBIC)
# cv2.imshow('show', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
return out
except Exception as e:
print(str(e))
num += 1
print("数据:" + str(data[num:]))
for re in data[num:]:
image1 = re["sourceImgUrl"]
image2 = re["mateSourceImgUrl"]
out = compare_images(client,image1, image2, num, list, data)
if out is None: # 检查图像是否为空
print("Error: Failed to load image(" + str(out) + "),未识别出违建物")
else:
# OpenCV图像对象转换为字节流格式。
_, buffer = cv2.imencode('.jpg', out)
img_bytes = buffer.tobytes()
if len(img_bytes) != 0:
# 如果数组不为空,执行其他操作
# 文件名称
unique_id = str(uuid.uuid4())
imgName = 'python/' + unique_id + "结果图.jpg"
# 将图片上传到阿里云OSS
url = upload_image_to_oss(imgName, img_bytes)
id = re["id"]
taskId = re["taskId"]
map = {"id": id, "discernImgUrl": url, "taskId": taskId}
list.append(map)
else:
print("未违建")
pass
print("输出结果==" + str(list))
on_publish(client, pushTopic, json.dumps(list), qos=2)
def upload_image_to_oss(imgeName,data):
# imgeName 文件名 列:python/img.jpg
bucket.put_object(imgeName, data)
# 将对象权限设置为公共读,以便通过URL访问上传的图像
bucket.put_object_acl(imgeName, oss2.OBJECT_ACL_PUBLIC_READ)
# 获取对象URL
url = bucket.sign_url("GET", imgeName, 3600) # URL有效期为1小时
return url
# 连接回调函数
def on_connect(client, userdata, flags, rc):
"""一旦连接成功, 回调此方法"""
rc_status = ["连接成功", "协议版本错误", "无效的客户端标识", "服务器无法使用", "用户名或密码错误", "无授权"]
print("connect:", rc_status[rc])
# 订阅主题
client.subscribe(subTopic)
def on_message(client, userdata, msg):
try:
"""一旦订阅到消息, 回调此方法"""
print("主题:" + msg.topic + " 消息:" + str(msg.payload.decode('utf-8'))) # 客户端返回的消息,使用gb2312编码中文不会报错
# 将收到的消息转换为JSON格式
data = json.loads(msg.payload)
# 获取图片路径、图片名称和MQTT消息的时间戳
list = []
num = 0
if data:
for re in data:
if "sourceImgUrl" in re and "mateSourceImgUrl" in re:
image1 = re["sourceImgUrl"]
image2 = re["mateSourceImgUrl"]
# 如果image1和image2都不为空,执行相关操作
# 指向算法比对,获取图片
out = compare_images(client,image1, image2,num,list,data)
num+=1
if out is None: # 检查图像是否为空
print("Error: Failed to load image("+str(out)+"),未识别出违建物")
else:
# OpenCV图像对象转换为字节流格式。
_, buffer = cv2.imencode('.jpg', out)
img_bytes = buffer.tobytes()
if len(img_bytes) != 0:
# 如果数组不为空,执行其他操作
# 文件名称
unique_id = str(uuid.uuid4())
imgName = 'python/' + unique_id + "结果图.jpg"
# 将图片上传到阿里云OSS
url = upload_image_to_oss(imgName, img_bytes)
id=re["id"]
taskId = re["taskId"]
dict={"id":id,"discernImgUrl":url,"taskId":taskId}
list.append(dict)
else:
print("未违建")
pass
# 发布消息
print("输出结果=="+str(list))
on_publish(client,pushTopic, json.dumps(list),qos=2)
except Exception as e:
print(str(e))
run()
def on_publish(client,topic, payload, qos):
re=client.publish(topic, payload, qos)
print("发布图片比对结果返回值::"+str(re)+"时间:"+str(datetime.datetime.now()))
def run():
# 创建客户端对象
client = mqtt.Client()
client.username_pw_set("adminRQ", "yongqiang666")
# 设置连接回调函数
client.on_connect = on_connect
# 设置接收消息回调函数
client.on_message = on_message
# 连接到MQTT代理
client.connect("106.13.50.125", 1883, 60)
# 保持连接并接收消息
client.loop_forever()
if __name__ == '__main__':
run()
2