347 lines
15 KiB
Python
347 lines
15 KiB
Python
from kafka import KafkaProducer, KafkaConsumer
|
||
from kafka.errors import kafka_errors
|
||
import traceback
|
||
import json, base64,os
|
||
import numpy as np
|
||
from multiprocessing import Process,Queue
|
||
import time,cv2,string,random
|
||
import subprocess as sp
|
||
|
||
import matplotlib.pyplot as plt
|
||
from utils.datasets import LoadStreams, LoadImages
|
||
from models.experimental import attempt_load
|
||
from utils.general import check_img_size, check_requirements, check_imshow, non_max_suppression, apply_classifier, scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path
|
||
import torch,sys
|
||
#from segutils.segmodel import SegModel,get_largest_contours
|
||
#sys.path.extend(['../yolov5/segutils'])
|
||
|
||
from segutils.segWaterBuilding import SegModel,get_largest_contours,illBuildings
|
||
|
||
#from segutils.core.models.bisenet import BiSeNet
|
||
from segutils.core.models.bisenet import BiSeNet_MultiOutput
|
||
|
||
from utils.plots import plot_one_box,plot_one_box_PIL,draw_painting_joint,get_label_arrays,get_websource
|
||
from collections import Counter
|
||
#import matplotlib
|
||
|
||
import matplotlib.pyplot as plt
|
||
# get_labelnames,get_label_arrays,post_process_,save_problem_images,time_str
|
||
#FP_DEBUG=open('debut.txt','w')
|
||
def bsJpgCode(image_ori):
|
||
jpgCode = cv2.imencode('.jpg',image_ori)[-1]###np.array,(4502009,1)
|
||
bsCode = str(base64.b64encode(jpgCode))[2:-1] ###str,长6002680
|
||
return bsCode
|
||
def bsJpgDecode(bsCode):
|
||
bsDecode = base64.b64decode(bsCode)###types,长4502009
|
||
npString = np.frombuffer(bsDecode,np.uint8)###np.array,(长4502009,)
|
||
jpgDecode = cv2.imdecode(npString,cv2.IMREAD_COLOR)###np.array,(3000,4000,3)
|
||
return jpgDecode
|
||
def get_ms(time0,time1):
|
||
str_time ='%.2f ms'%((time1-time0)*1000)
|
||
return str_time
|
||
rainbows=[
|
||
(0,0,255),(0,255,0),(255,0,0),(255,0,255),(255,255,0),(255,127,0),(255,0,127),
|
||
(127,255,0),(0,255,127),(0,127,255),(127,0,255),(255,127,255),(255,255,127),
|
||
(127,255,255),(0,255,255),(255,127,255),(127,255,255),
|
||
(0,127,0),(0,0,127),(0,255,255)
|
||
]
|
||
|
||
|
||
def get_labelnames(labelnames):
|
||
with open(labelnames,'r') as fp:
|
||
namesjson=json.load(fp)
|
||
names_fromfile=namesjson['labelnames']
|
||
names = names_fromfile
|
||
return names
|
||
|
||
def check_stream(stream):
|
||
cap = cv2.VideoCapture(stream)
|
||
if cap.isOpened():
|
||
return True
|
||
else:
|
||
return False
|
||
#####
|
||
def drawWater(pred,image_array0,river={'color':(0,255,255),'line_width':3,'segRegionCnt':2}):####pred是模型的输出,只有水分割的任务
|
||
##画出水体区域
|
||
contours, hierarchy = cv2.findContours(pred,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
|
||
water = pred.copy(); water[:,:] = 0
|
||
|
||
if len(contours)==0:
|
||
return image_array0,water
|
||
max_ids = get_largest_contours(contours,river['segRegionCnt']);
|
||
for max_id in max_ids:
|
||
cv2.fillPoly(water, [contours[max_id][:,0,:]], 1)
|
||
cv2.drawContours(image_array0,contours,max_id,river['color'],river['line_width'] )
|
||
return image_array0,water
|
||
|
||
|
||
def scale_back(boxes,padInfos):
|
||
top, left,r = padInfos[0:3]
|
||
|
||
boxes[:,0] = (boxes[:,0] - left) * r
|
||
|
||
boxes[:,2] = (boxes[:,2] - left) * r
|
||
boxes[:,1] = (boxes[:,1] - top) * r
|
||
boxes[:,3] = (boxes[:,3] - top) * r
|
||
return boxes
|
||
|
||
def img_pad(img, size, pad_value=[114,114,114]):
|
||
###填充成固定尺寸
|
||
H,W,_ = img.shape
|
||
r = max(H/size[0], W/size[1])
|
||
img_r = cv2.resize(img, (int(W/r), int(H/r)))
|
||
tb = size[0] - img_r.shape[0]
|
||
lr = size[1] - img_r.shape[1]
|
||
top = int(tb/2)
|
||
bottom = tb - top
|
||
left = int(lr/2)
|
||
right = lr - left
|
||
pad_image = cv2.copyMakeBorder(img_r, top, bottom, left, right, cv2.BORDER_CONSTANT,value=pad_value)
|
||
return pad_image,(top, left,r)
|
||
|
||
def post_process_(datas,conf_thres, iou_thres,names,label_arraylist,rainbows,iframe,ObjectPar={ 'object_config':[0,1,2,3,4], 'slopeIndex':[5,6,7] ,'segmodel':True,'segRegionCnt':1 },font={ 'line_thickness':None, 'fontSize':None,'boxLine_thickness':None,'waterLineColor':(0,255,255),'waterLineWidth':3},padInfos=None ):
|
||
object_config,slopeIndex,segmodel,segRegionCnt=ObjectPar['object_config'],ObjectPar['slopeIndex'],ObjectPar['segmodel'],ObjectPar['segRegionCnt']
|
||
##输入dataset genereate 生成的数据,model预测的结果pred,nms参数
|
||
##主要操作NMS ---> 坐标转换 ---> 画图
|
||
##输出原图、AI处理后的图、检测结果
|
||
time0=time.time()
|
||
path, img, im0s, vid_cap ,pred,seg_pred= datas[0:6];
|
||
#segmodel=True
|
||
pred = non_max_suppression(pred, conf_thres, iou_thres, classes=None, agnostic=False)
|
||
time1=time.time()
|
||
i=0;det=pred[0]###一次检测一张图片
|
||
time1_1 = time.time()
|
||
#p, s, im0 = path[i], '%g: ' % i, im0s[i].copy()
|
||
p, s, im0 = path[i], '%g: ' % i, im0s[i]
|
||
time1_2 = time.time()
|
||
gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh
|
||
time1_3 = time.time()
|
||
det_xywh=[];
|
||
#im0_brg=cv2.cvtColor(im0,cv2.COLOR_RGB2BGR);
|
||
if segmodel:
|
||
if len(seg_pred)==2:
|
||
im0,water = illBuildings(seg_pred,im0)
|
||
else:
|
||
river={ 'color':font['waterLineColor'],'line_width':font['waterLineWidth'],'segRegionCnt':segRegionCnt }
|
||
im0,water = drawWater(seg_pred,im0,river)
|
||
time2=time.time()
|
||
#plt.imshow(im0);plt.show()
|
||
if len(det)>0:
|
||
# Rescale boxes from img_size to im0 size
|
||
if not padInfos:
|
||
det[:, :4] = scale_coords(img.shape[2:], det[:, :4],im0.shape).round()
|
||
else:
|
||
#print('####line131:',det[:, :])
|
||
det[:, :4] = scale_back( det[:, :4],padInfos).round()
|
||
#print('####line133:',det[:, :])
|
||
#用seg模型,确定有效检测匡及河道轮廓线
|
||
if segmodel:
|
||
cls_indexs = det[:, 5].clone().cpu().numpy().astype(np.int32)
|
||
##判断哪些目标属于岸坡的
|
||
slope_flag = np.array([x in slopeIndex for x in cls_indexs ] )
|
||
|
||
det_c = det.clone(); det_c=det_c.cpu().numpy()
|
||
try:
|
||
area_factors = np.array([np.sum(water[int(x[1]):int(x[3]), int(x[0]):int(x[2])] )*1.0/(1.0*(x[2]-x[0])*(x[3]-x[1])+0.00001) for x in det_c] )
|
||
except:
|
||
print('*****************************line143: error:',det_c)
|
||
water_flag = np.array(area_factors>0.1)
|
||
det = det[water_flag|slope_flag]##如果是水上目标,则需要与水的iou超过0.1;如果是岸坡目标,则直接保留。
|
||
#对检测匡绘图
|
||
for *xyxy, conf, cls in reversed(det):
|
||
xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh
|
||
cls_c = cls.cpu().numpy()
|
||
if int(cls_c) not in object_config: ###如果不是所需要的目标,则不显示
|
||
continue
|
||
conf_c = conf.cpu().numpy()
|
||
line = [float(cls_c), *xywh, float(conf_c)] # label format
|
||
det_xywh.append(line)
|
||
label = f'{names[int(cls)]} {conf:.2f}'
|
||
|
||
im0 = draw_painting_joint(xyxy,im0,label_arraylist[int(cls)],score=conf,color=rainbows[int(cls)%20],font=font)
|
||
time3=time.time()
|
||
strout='nms:%s drawWater:%s,copy:%s,toTensor:%s,detDraw:%s '%(get_ms(time0,time1),get_ms(time1,time2),get_ms(time1_1,time1_2),get_ms(time1_2,time1_3), get_ms(time2,time3) )
|
||
return [im0s[0],im0,det_xywh,iframe],strout
|
||
|
||
|
||
|
||
def preprocess(par):
|
||
print('#####process:',par['name'])
|
||
##负责读取视频,生成原图及供检测的使用图,numpy格式
|
||
#source='rtmp://liveplay.yunhengzhizao.cn/live/demo_HD5M'
|
||
#img_size=640; stride=32
|
||
while True:
|
||
cap = cv2.VideoCapture(par['source'])
|
||
iframe = 0
|
||
if cap.isOpened():
|
||
print( '#### read %s success!'%(par['source']))
|
||
try:
|
||
dataset = LoadStreams(par['source'], img_size=640, stride=32)
|
||
for path, img, im0s, vid_cap in dataset:
|
||
datas=[path, img, im0s, vid_cap,iframe]
|
||
par['queOut'].put(datas)
|
||
iframe +=1
|
||
except Exception as e:
|
||
print('###read error:%s '%(par['source']))
|
||
time.sleep(10)
|
||
iframe = 0
|
||
|
||
else:
|
||
print('###read error:%s '%(par['source'] ))
|
||
time.sleep(10)
|
||
iframe = 0
|
||
|
||
def gpu_process(par):
|
||
print('#####process:',par['name'])
|
||
half=True
|
||
##gpu运算,检测模型
|
||
weights = par['weights']
|
||
device = par['device']
|
||
print('###line127:',par['device'])
|
||
model = attempt_load(par['weights'], map_location=par['device']) # load FP32 model
|
||
if half:
|
||
model.half()
|
||
|
||
##gpu运算,分割模型
|
||
seg_nclass = par['seg_nclass']
|
||
seg_weights = par['seg_weights']
|
||
|
||
#segmodel = SegModel(nclass=seg_nclass,weights=seg_weights,device=device)
|
||
|
||
|
||
nclass = [2,2]
|
||
Segmodel = BiSeNet_MultiOutput(nclass)
|
||
weights='weights/segmentation/WaterBuilding.pth'
|
||
segmodel = SegModel(model=Segmodel,nclass=nclass,weights=weights,device='cuda:0',multiOutput=True)
|
||
while True:
|
||
if not par['queIn'].empty():
|
||
time0=time.time()
|
||
datas = par['queIn'].get()
|
||
path, img, im0s, vid_cap,iframe = datas[0:5]
|
||
time1=time.time()
|
||
img = torch.from_numpy(img).to(device)
|
||
img = img.half() if half else img.float() # uint8 to fp16/32
|
||
img /= 255.0 # 0 - 255 to 0.0 - 1.0
|
||
time2 = time.time()
|
||
pred = model(img,augment=False)[0]
|
||
time3 = time.time()
|
||
seg_pred = segmodel.eval(im0s[0],outsize=None,smooth_kernel=20)
|
||
time4 = time.time()
|
||
fpStr= 'process:%s ,iframe:%d,getdata:%s,copygpu:%s,dettime:%s,segtime:%s , time:%s, queLen:%d '%( par['name'],iframe,get_ms(time0,time1) ,get_ms(time1,time2) ,get_ms(time2,time3) ,get_ms(time3,time4),get_ms(time0,time4) ,par['queIn'].qsize() )
|
||
#FP_DEBUG.write( fpStr+'\n' )
|
||
datasOut = [path, img, im0s, vid_cap,pred,seg_pred,iframe]
|
||
par['queOut'].put(datasOut)
|
||
if par['debug']:
|
||
print('#####process:',par['name'],' line107')
|
||
else:
|
||
time.sleep(1/300)
|
||
def get_cls(array):
|
||
dcs = Counter(array)
|
||
keys = list(dcs.keys())
|
||
values = list(dcs.values())
|
||
max_index = values.index(max(values))
|
||
cls = int(keys[max_index])
|
||
return cls
|
||
def save_problem_images(post_results,iimage_cnt,names,streamName='live-THSAHD5M',outImaDir='problems/images_tmp',imageTxtFile=False):
|
||
## [cls, x,y,w,h, conf]
|
||
problem_image=[[] for i in range(6)]
|
||
|
||
|
||
dets_list = [x[2] for x in post_results]
|
||
|
||
mean_scores=[ np.array(x)[:,5].mean() for x in dets_list ] ###mean conf
|
||
|
||
best_index = mean_scores.index(max(mean_scores)) ##获取该批图片里,问题图片的index
|
||
best_frame = post_results[ best_index][3] ##获取绝对帧号
|
||
img_send = post_results[best_index][1]##AI处理后的图
|
||
img_bak = post_results[best_index][0]##原图
|
||
cls_max = get_cls( x[0] for x in dets_list[best_index] )
|
||
|
||
|
||
time_str = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
|
||
uid=''.join(random.sample(string.ascii_letters + string.digits, 16))
|
||
#ori_name = '2022-01-20-15-57-36_frame-368-720_type-漂浮物_qVh4zI08ZlwJN9on_s-live-THSAHD5M_OR.jpg'
|
||
#2022-01-13-15-07-57_frame-9999-9999_type-结束_9999999999999999_s-off-XJRW20220110115904_AI.jpg
|
||
outnameOR= '%s/%s_frame-%d-%d_type-%s_%s_s-%s_AI.jpg'%(outImaDir,time_str,best_frame,iimage_cnt,names[cls_max],uid,streamName)
|
||
outnameAR= '%s/%s_frame-%d-%d_type-%s_%s_s-%s_OR.jpg'%(outImaDir,time_str,best_frame,iimage_cnt,names[cls_max],uid,streamName)
|
||
|
||
cv2.imwrite(outnameOR,img_send)
|
||
cv2.imwrite(outnameAR,img_bak)
|
||
if imageTxtFile:
|
||
outnameOR_txt = outnameOR.replace('.jpg','.txt')
|
||
fp=open(outnameOR_txt,'w');fp.write(outnameOR+'\n');fp.close()
|
||
outnameAI_txt = outnameAR.replace('.jpg','.txt')
|
||
fp=open(outnameAI_txt,'w');fp.write(outnameAR+'\n');fp.close()
|
||
|
||
parOut = {}; parOut['imgOR'] = img_send; parOut['imgAR'] = img_send; parOut['uid']=uid
|
||
parOut['imgORname']=os.path.basename(outnameOR);parOut['imgARname']=os.path.basename(outnameAR);
|
||
parOut['time_str'] = time_str;parOut['type'] = names[cls_max]
|
||
return parOut
|
||
|
||
|
||
|
||
|
||
def post_process(par):
|
||
|
||
print('#####process:',par['name'])
|
||
###post-process参数
|
||
conf_thres,iou_thres,classes=par['conf_thres'],par['iou_thres'],par['classes']
|
||
labelnames=par['labelnames']
|
||
rainbows=par['rainbows']
|
||
fpsample = par['fpsample']
|
||
names=get_labelnames(labelnames)
|
||
label_arraylist = get_label_arrays(names,rainbows,outfontsize=40)
|
||
iimage_cnt = 0
|
||
post_results=[]
|
||
while True:
|
||
if not par['queIn'].empty():
|
||
time0=time.time()
|
||
datas = par['queIn'].get()
|
||
iframe = datas[6]
|
||
if par['debug']:
|
||
print('#####process:',par['name'],' line129')
|
||
p_result,timeOut = post_process_(datas,conf_thres, iou_thres,names,label_arraylist,rainbows,iframe)
|
||
par['queOut'].put(p_result)
|
||
##输出结果
|
||
|
||
|
||
|
||
##每隔 fpsample帧处理一次,如果有问题就保存图片
|
||
if (iframe % fpsample == 0) and (len(post_results)>0) :
|
||
#print('####line204:',iframe,post_results)
|
||
save_problem_images(post_results,iframe,names)
|
||
post_results=[]
|
||
|
||
if len(p_result[2] )>0: ##
|
||
#post_list = p_result.append(iframe)
|
||
post_results.append(p_result)
|
||
#print('####line201:',type(p_result))
|
||
|
||
time1=time.time()
|
||
outstr='process:%s ,iframe:%d,%s , time:%s, queLen:%d '%( par['name'],iframe,timeOut,get_ms(time0,time1) ,par['queIn'].qsize() )
|
||
#FP_DEBUG.write(outstr +'\n')
|
||
#print( 'process:%s ,iframe:%d,%s , time:%s, queLen:%d '%( par['name'],iframe,timeOut,get_ms(time0,time1) ,par['queIn'].qsize() ) )
|
||
else:
|
||
time.sleep(1/300)
|
||
|
||
|
||
def save_logfile(name,txt):
|
||
if os.path.exists(name):
|
||
fp=open(name,'r+')
|
||
else:
|
||
fp=open(name,'w')
|
||
|
||
fp.write('%s %s \n'%(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),txt))
|
||
fp.close()
|
||
def time_str():
|
||
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||
|
||
|
||
|
||
if __name__=='__main__':
|
||
jsonfile='config/queRiver.json'
|
||
#image_encode_decode()
|
||
work_stream(jsonfile)
|
||
#par={'name':'preprocess'}
|
||
#preprocess(par)
|