You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
462 lines
17 KiB
462 lines
17 KiB
import datetime
|
|
import math
|
|
import os
|
|
import signal
|
|
import sys
|
|
import threading
|
|
import re
|
|
import uuid
|
|
import tempfile
|
|
import pyexiv2
|
|
import numpy as np
|
|
|
|
import requests
|
|
from PIL import Image
|
|
|
|
from numpy import arctan
|
|
import cv2
|
|
from math import tan, cos, sin
|
|
import oss2
|
|
import paho.mqtt.client as mqtt
|
|
import concurrent.futures
|
|
import json
|
|
|
|
|
|
|
|
# 客户端id
|
|
client_id='python_gengbao'
|
|
# 订阅主题
|
|
longandlat_mqtt ='task/image/gps/custon'
|
|
# 订阅主题
|
|
appTwo ='task/image/disobey/handle'
|
|
# 订阅主题
|
|
weijian ='task/image/pixel/custon'
|
|
|
|
# 发布通知主题
|
|
pushNoticeTopic ='task/image/disobey/confirm'
|
|
|
|
|
|
access_key = "LTAI5tMDFvYZRvrJfK7HeuEm"
|
|
secret_key = "kP7ueRlhLbM9mvkaLtHfcje3GxgawH"
|
|
endpoint = "oss-cn-hangzhou.aliyuncs.com"
|
|
bucket_name = "yq-dajiang"
|
|
|
|
|
|
|
|
auths = oss2.Auth(access_key, secret_key)
|
|
bucket = oss2.Bucket(auths, endpoint, bucket_name)
|
|
# 根据像素坐标生成图片和经纬度,放入集合中,返回到前端
|
|
|
|
|
|
client = mqtt.Client(client_id=client_id)
|
|
#截图
|
|
def crop_image(input_image,center_x,center_y):
|
|
print(center_x,center_y)
|
|
#固定截图尺寸 1000*1000
|
|
l_x = 0
|
|
l_y=0
|
|
r_x=1000
|
|
r_y=1000
|
|
if center_x>500:
|
|
l_x=center_x-500
|
|
r_x=center_x+500
|
|
if r_x>=4000:
|
|
r_x=4000
|
|
l_x=3000
|
|
|
|
if center_y>500:
|
|
l_y=center_y-500
|
|
r_y=center_y+500
|
|
if r_y>=3000:
|
|
r_y=3000
|
|
l_y=2000
|
|
print((l_x,l_y),(r_x,r_y))
|
|
# 截取指定区域图像
|
|
cropped_image = input_image[l_y:r_y, l_x:r_x]
|
|
return np.array(cropped_image)
|
|
#计算两个经纬度之间的距离,用于计算长宽
|
|
def dimension(jingduA, weiduA,jingduB, weiduB):
|
|
R = 6371.393
|
|
Pi = math.pi
|
|
|
|
a = (math.sin(math.radians(weiduA / 2 - weiduB / 2))) ** 2
|
|
b = math.cos(weiduA * Pi / 180) * math.cos(weiduB * Pi / 180) * (
|
|
math.sin((jingduA / 2 - jingduB / 2) * Pi / 180)) ** 2
|
|
dimension = 2 * R * math.asin((a + b) ** 0.5) * 1000
|
|
return dimension
|
|
|
|
def sum(focalLength,x,altitude):
|
|
width=4000
|
|
#靶面宽
|
|
ba=6.2
|
|
#实际长度
|
|
wr=(x*altitude*ba)/(focalLength*width)
|
|
return wr
|
|
|
|
def longandlat(pixel_x,pixel_y,imageMap):
|
|
|
|
# 焦距
|
|
focalLength =float(imageMap["focalLength"])
|
|
# 经纬度
|
|
longitude_str=imageMap["longitude"]
|
|
latitude_str = imageMap["latitude"]
|
|
# 使用正则表达式提取度、分和秒的数值
|
|
pattern = r"(\d+)° (\d+)' ([\d.]+)\""
|
|
longitude_match = re.match(pattern, longitude_str)
|
|
latitude_match = re.match(pattern, latitude_str)
|
|
print(longitude_match)
|
|
print(type(longitude_match))
|
|
|
|
# 度
|
|
lg_du =int(longitude_match.group(1))
|
|
# 分
|
|
lg_fen =int(longitude_match.group(2))
|
|
# 秒
|
|
lg_seconds = float(longitude_match.group(3))
|
|
|
|
# 度
|
|
la_du = int(latitude_match.group(1))
|
|
# 分
|
|
la_fen = int(latitude_match.group(2))
|
|
# 秒
|
|
la_seconds =float(latitude_match.group(3))
|
|
|
|
|
|
# 海拔
|
|
altitude = float(imageMap["altitude"])
|
|
# 图片宽高
|
|
width =float(imageMap["width"])
|
|
height =float(imageMap["height"])
|
|
|
|
|
|
# 中点坐标
|
|
x, y = width // 2, height // 2
|
|
# 通过勾股定理可以计算出 1/2.3 英寸对角线的长度大约为 4.54 毫米。
|
|
|
|
D = 2 * tan(arctan(9.826 / 2 / focalLength) / 2) * altitude
|
|
|
|
# 因此,每个像素所代表的实际距离为(米):
|
|
# p =(9.826*altitude)/(width*focalLength)
|
|
|
|
p = D / width
|
|
# 经度每秒代表的距离 m
|
|
# 纬度每秒代表的距离 m
|
|
lat_p = 23.6
|
|
long_p = 30.9
|
|
|
|
pie=float(imageMap["xmpProperties"])
|
|
|
|
if pie < 0:
|
|
pie = -180 + abs(pie)
|
|
else:
|
|
pie = 180 - pie
|
|
# z转换已中心点为原点的坐标体系
|
|
px = (pixel_x - x)
|
|
py = (y - pixel_y)
|
|
|
|
a = math.radians(pie)
|
|
|
|
# 坐标体系旋转后所得到的坐标
|
|
c_a_x = cos(a) * px - sin(a) * py
|
|
c_a_y = sin(a) * px + cos(a) * py
|
|
|
|
ys = c_a_y * p / lat_p
|
|
xs = c_a_x * p / long_p
|
|
|
|
la_seconds += ys
|
|
lg_seconds += xs
|
|
|
|
longitude_x = lg_du + lg_fen / 60.0 + lg_seconds / 3600.0
|
|
|
|
latitude_y = la_du + la_fen / 60.0 + la_seconds / 3600.0
|
|
return longitude_x ,latitude_y,focalLength,altitude
|
|
|
|
|
|
|
|
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.split('?')[0]
|
|
|
|
# 连接回调函数
|
|
def on_connect(client, userdata, flags, rc):
|
|
"""一旦连接成功, 回调此方法"""
|
|
rc_status = ["连接成功", "协议版本错误", "无效的客户端标识", "服务器无法使用", "用户名或密码错误", "无授权"]
|
|
print("connect:", rc_status[rc])
|
|
topics = [str(weijian), str(longandlat_mqtt), str(appTwo)]
|
|
# 要订阅的主题列表
|
|
for topic in topics:
|
|
# 订阅主题
|
|
print("订阅主题:", topic)
|
|
client.subscribe(topic)
|
|
def compute(re):
|
|
if "sourceImgUrl" in re and "pixelCoordinate" in re:
|
|
sourceImgUrl = re["sourceImgUrl"]
|
|
mateSourceImgUrl = re["mateSourceImgUrl"]
|
|
pixelCoordinate = re["pixelCoordinate"]
|
|
imageMap = re["imageMap"]
|
|
maplist=[]
|
|
# 如果image1和image2都不为空,执行相关操作
|
|
# 指向算法比对,获取图片z
|
|
if pixelCoordinate:
|
|
coordinateList = json.loads(pixelCoordinate)
|
|
# 指向算法比对,获取图片
|
|
if len(coordinateList) != 0:
|
|
# 从URL加载图像
|
|
resp = requests.get(sourceImgUrl)
|
|
# 创建临时文件
|
|
# 创建临时文件
|
|
temp_file = tempfile.NamedTemporaryFile(delete=False)
|
|
temp_file.write(resp.content)
|
|
temp_file.close()
|
|
|
|
|
|
# 前端缩放了,比例是:scale=4
|
|
for item in coordinateList:
|
|
lef_x = item["lef_x"]
|
|
lef_y = item["lef_y"]
|
|
right_x = item["right_x"]
|
|
right_y = item["right_y"]
|
|
# 将比例还原
|
|
scale = 4
|
|
lef_x = int(lef_x * scale)
|
|
lef_y = int(lef_y * scale)
|
|
right_x = int(right_x * scale)
|
|
right_y = int(right_y * scale)
|
|
x=right_x-lef_x
|
|
y=right_y-lef_y
|
|
# 中心坐标
|
|
# 计算矩形框的中心点坐标
|
|
center_x = int((lef_x + right_x) / 2)
|
|
center_y = int((lef_y + right_y) / 2)
|
|
# 经纬度转换
|
|
long, lat ,focalLength,altitude= longandlat(center_x, center_y, imageMap)
|
|
img_array = np.frombuffer(resp.content, np.uint8)
|
|
# 将矩形框绘制到图片上
|
|
img_copy = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
|
|
# 在图像上绘制矩形框
|
|
cv2.rectangle(img_copy, (lef_x,lef_y), (right_x,right_y), (0, 255, 255), 10)
|
|
# OpenCV图像对象转换为字节流格式。
|
|
_, buffer = cv2.imencode('.jpg', img_copy)
|
|
img_bytes = buffer.tobytes()
|
|
# 文件名称
|
|
unique_id = str(uuid.uuid4())
|
|
imgName = 'python/' + unique_id + "违建图.jpg"
|
|
# 将图片上传到阿里云OSS
|
|
url = upload_image_to_oss(imgName, img_bytes)
|
|
|
|
# 获取长
|
|
w = sum(focalLength,x,altitude)
|
|
# 获取宽
|
|
h = sum(focalLength,y,altitude)
|
|
# 计算矩形面积并保留两位小数
|
|
area = round(w * h, 2)
|
|
|
|
#截图 对比图放大
|
|
cropped_image1=crop_image(img_copy, center_x,center_y)
|
|
# OpenCV图像对象转换为字节流格式。
|
|
_, buffer1 = cv2.imencode('.jpg',cropped_image1)
|
|
img_bytes1 = buffer1.tobytes()
|
|
# 文件名称
|
|
unique_id1 = str(uuid.uuid4())
|
|
imgNameOne = 'python/' + unique_id1 + "对比图违建放大图.jpg"
|
|
# 将图片上传到阿里云OSS
|
|
maxImages = upload_image_to_oss(imgNameOne, img_bytes1)
|
|
# 从URL加载图像
|
|
resp1 = requests.get(mateSourceImgUrl)
|
|
# 创建临时文件
|
|
temp_file1 = tempfile.NamedTemporaryFile(delete=False)
|
|
temp_file1.write(resp1.content)
|
|
temp_file1.close()
|
|
# 模板图画框
|
|
img_array2 = np.frombuffer(resp1.content, np.uint8)
|
|
# 将矩形框绘制到图片上
|
|
img_copy2 = cv2.imdecode(img_array2, cv2.IMREAD_COLOR)
|
|
# 在图像上绘制矩形框
|
|
cv2.rectangle(img_copy2, (lef_x, lef_y), (right_x, right_y), (255, 255, 0), 10)
|
|
# OpenCV图像对象转换为字节流格式。
|
|
_, buffer2 = cv2.imencode('.jpg', img_copy2)
|
|
img_bytes2 = buffer2.tobytes()
|
|
# 文件名称
|
|
unique_id3 = str(uuid.uuid4())
|
|
imgName3 = 'python/' + unique_id3 + "违建图.jpg"
|
|
# 将图片上传到阿里云OSS
|
|
url3 = upload_image_to_oss(imgName3, img_bytes2)
|
|
cropped_image2 = crop_image(img_copy2,center_x,center_y) # OpenCV图像对象转换为字节流格式。
|
|
_, buffer1 = cv2.imencode('.jpg',cropped_image2)
|
|
img_bytes2 = buffer1.tobytes()
|
|
# 文件名称
|
|
unique_id2 = str(uuid.uuid4())
|
|
imgNameTwo = 'python/' + unique_id2 + "放大模板图.jpg"
|
|
# 将图片上传到阿里云OSS
|
|
maxMateSourceImgUrl = upload_image_to_oss(imgNameTwo, img_bytes2)
|
|
|
|
id = re["id"]
|
|
taskId = re["taskId"]
|
|
map = {"lng": long, "lat": lat, "url": url, "taskImageId": id, "taskId": taskId, "images": url,
|
|
"handleType": 1,"areaL":w,"areaW":h,"area":area,"maxMateSourceImgUrl":maxMateSourceImgUrl,
|
|
"maxImages":maxImages,"mateSourceImgUrl":url3}
|
|
maplist.append(map)
|
|
|
|
return maplist
|
|
# 根据像素坐标生成图片和经纬度,放入集合中,返回到前端
|
|
def result(re,imgeurl,coordinateList):
|
|
if len(coordinateList)!=0:
|
|
print("读取")
|
|
# 从URL加载图像
|
|
imgeurl=str(imgeurl).replace("https", "http")
|
|
resp = requests.get(imgeurl,stream=True)
|
|
# 创建临时文件
|
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
|
for chunk in resp.iter_content(chunk_size=1024):
|
|
temp_file.write(chunk)
|
|
print("创建")
|
|
# 创建pyexiv2.Image对象
|
|
# 从临时文件读取图像数据
|
|
image = cv2.imread(temp_file.name)
|
|
if image is not None:
|
|
# 文件名称
|
|
unique_id = str(uuid.uuid4())
|
|
print("生成")
|
|
#前端缩放了,比例是:scale=4
|
|
for item in coordinateList:
|
|
print(type(item))
|
|
lef_x=item["lef_x"]
|
|
lef_y=item["lef_y"]
|
|
right_x = item["right_x"]
|
|
right_y = item["right_y"]
|
|
|
|
#将比例还原
|
|
scale=4
|
|
lef_x=lef_x*scale
|
|
lef_y=lef_y*scale
|
|
right_x=right_x*scale
|
|
right_y=right_y*scale
|
|
|
|
|
|
# # 中心坐标
|
|
# # 计算矩形框的中心点坐标
|
|
# center_x = int(lef_x+right_x / 2)
|
|
# center_y = int(lef_y+right_y / 2)
|
|
# # # 经纬度转换
|
|
# # long,lat=longandlat(image,center_x,center_y)
|
|
# # 将矩形框绘制到图片上
|
|
# nparr = np.frombuffer(resp.content, np.uint8)
|
|
# img_copy = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
|
# 在图像上绘制矩形框
|
|
cv2.rectangle(image, (int(lef_x),int(lef_y)), (int(right_x),int(right_y)), (0,0,255), 10)
|
|
# OpenCV图像对象转换为字节流格式。
|
|
_, buffer = cv2.imencode('.jpg', image)
|
|
img_bytes = buffer.tobytes()
|
|
imgName = 'python/' + unique_id + "违建图.jpg"
|
|
print("上传")
|
|
# 将图片上传到阿里云OSS
|
|
url = upload_image_to_oss(imgName, img_bytes)
|
|
id = re["id"]
|
|
taskId = re["taskId"]
|
|
map = {"id": id, "taskId": taskId,"discernImgUrl": url}
|
|
os.remove(temp_file.name)
|
|
return map
|
|
def messageData(msg):
|
|
topic = msg.topic
|
|
print("处理消息,时间:" + str(datetime.datetime.now()))
|
|
data = json.loads(msg.payload.decode())
|
|
# 获取图片路径、图片名称和MQTT消息的时间戳
|
|
if data:
|
|
if topic==appTwo:
|
|
print("通知")
|
|
if "id" in data:
|
|
print("数据情况",data)
|
|
id=data["id"]
|
|
notice = {"id":id }
|
|
on_publish(pushNoticeTopic, json.dumps(notice), 0)
|
|
if topic==weijian:
|
|
print("画违建矩形框")
|
|
if "sourceImgUrl" in data and "pixelCoordinate" in data:
|
|
image = data["sourceImgUrl"]
|
|
pixelCoordinate = data["pixelCoordinate"]
|
|
# 指向算法比对,获取图片
|
|
coordinateList = json.loads(pixelCoordinate)
|
|
print(type(coordinateList))
|
|
map = result(data, image, coordinateList)
|
|
if map is None:
|
|
print("生成图片出错,为null")
|
|
else:
|
|
url = "http://106.13.50.125:9006/ploughe-boot/wayline/waylineTaskImage/leakEdit"
|
|
# url = "http://192.168.1.20:9005/ploughe-boot/wayline/waylineTaskImage/leakEdit"
|
|
# 调取接口
|
|
response = requests.post(url, json=map)
|
|
message = response.text
|
|
print("接口返回值:", message)
|
|
print("结束时间:" + str(datetime.datetime.now()))
|
|
elif topic==longandlat_mqtt:
|
|
print("提交违建,生产预警")
|
|
# 创建线程池
|
|
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
|
if data and "waylineTaskImages" in data:
|
|
waylineImgList = data["waylineTaskImages"]
|
|
# 提交任务给线程池
|
|
futurelist = [executor.submit(compute, re) for re in waylineImgList]
|
|
# 等待所有任务完成
|
|
concurrent.futures.wait(futurelist)
|
|
# 获取每个任务的处理结果
|
|
final_results = [item for sublist in
|
|
[future.result() for future in futurelist if future.result() is not None] for item
|
|
in
|
|
sublist]
|
|
print("返回的结果", final_results)
|
|
print("返回的结果数量", len(final_results))
|
|
if len(final_results) != 0:
|
|
data['waylineTaskHandles'] = final_results
|
|
print("提交数据:", data)
|
|
url = "http://106.13.50.125:9006/ploughe-boot/wayline/waylineTaskWhee/leakEdit"
|
|
# url = "http://192.168.1.20:9005/ploughe-boot/wayline/waylineTaskWhee/leakEdit"
|
|
# 调取接口
|
|
# 将list作为JSON数据
|
|
# 发送POST请求,并将data作为请求体
|
|
response = requests.post(url, json=data, headers={'Content-Type': 'application/json'})
|
|
# 获取响应结果
|
|
body = response.json()
|
|
print("接口返回值:", body)
|
|
print("结束时间:" + str(datetime.datetime.now()))
|
|
# 关闭线程池
|
|
executor.shutdown()
|
|
|
|
|
|
def on_message(client, userdata, msg):
|
|
# """一旦订阅到消息, 回调此方法"""
|
|
print("主题:" + msg.topic + " 消息:" + msg.payload.decode('utf-8')) # 客户端返回的消息,使用gb2312编码中文不会报错
|
|
# 多线程执行
|
|
threading.Thread(target=messageData, args=((msg,))).start()
|
|
|
|
|
|
def signal_handler(signal, frame):
|
|
print('接收到键盘中断信号,程序将退出')
|
|
sys.exit(0)
|
|
def on_publish(topic, payload, qos):
|
|
re=client.publish(topic, payload, qos)
|
|
print("结果:"+str(re)+"时间:"+str(datetime.datetime.now()))
|
|
|
|
def run():
|
|
# 创建客户端对象
|
|
|
|
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)
|
|
# 注册信号处理程序
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
# 保持连接并接收消息
|
|
client.loop_forever()
|
|
|
|
if __name__ == '__main__':
|
|
run()
|
|
|
|
|