希望疫情笼罩的日子尽早过去
因为疫情来的猛,公司内部为了监控员工健康码状态,要求系统自动识别,并且将情况通知到对应的人员进行后续跟踪。
运行环境和使用到的技术:ubuntu20(服务器)、python3.9、opencv4.5、java8
基本思路:通过健康码图片色HSV彩分析技术得出结果
1.图片大小规整
2.拾取指定颜色值的范围截取图片
3.图片处理(灰度、二值、平滑、膨胀)
4.最大轮廓寻找
5.计算最大轮廓面积以及码形状的面积比得出结果
6.因为处理结果需要在java端执行,因此json格式化输出结果
一,Python图片处理脚本主要实现核心的图片解析以及结果返回
Part1:
#颜色范围值 _color_dic=[{'color':'red','lower_hsv': [0, 43, 46], 'upper_hsv': [10, 255, 255]},{'color':'red','lower_hsv': [156, 43, 46], 'upper_hsv': [180, 255, 255]}, {'color':'yellow','lower_hsv': [26, 43, 46], 'upper_hsv': [34, 255, 255]},{'color':'yellow','lower_hsv': [11, 43, 46], 'upper_hsv': [25, 255, 255]}] #命中规则【整体轮廓面积>4w ,宽高差<50 ,图块白色像素>黑色像素点】 def isOkContours(range_mask): # 只提取整体外部轮廓 contours, hierarchy = cv2.findContours(range_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if (len(contours) <= 0): return False for cnt in contours: x, y, w, h = cv2.boundingRect(cnt) crop=range_mask[y:(y + h),x:(x+ w)] crop = cv2.dilate(crop, None, iterations=2) blackSum=np.sum([crop==0]) whiteSum = np.sum([crop == 255]) area = cv2.contourArea(cnt) c = abs(w - h) if (area >= 40000 and c <= 100 and whiteSum>blackSum): return True return False # get all color def whatColor(img_hsv): _checkFlag=False for color in _color_dic: lower_hsv = np.array(color['lower_hsv']) red_upper_hsv = np.array(color['upper_hsv']) target_red_mask = cv2.inRange(img_hsv, lowerb=lower_hsv, upperb=red_upper_hsv) _checkFlag=isOkContours(target_red_mask) if _checkFlag==False: continue _checkFlag=True print(json.dumps({"code": 0, "color":color['color']}, sort_keys=True)) break if _checkFlag==False: print(json.dumps({"code": 0, "color": "other"}, sort_keys=True)) def getColorRange(img_hsv,color_lower_hsv,color_upper_hsv): lower_hsv = np.array(color_lower_hsv) red_upper_hsv = np.array(color_upper_hsv) target_red_mask = cv2.inRange(img_hsv, lowerb=lower_hsv, upperb=red_upper_hsv) return target_red_mask # get single color def isRedCode(img_hsv): red_list = [item for item in _color_dic if item['color']=='red'] for color in red_list: _range = getColorRange(img_hsv, color['lower_hsv'], color['upper_hsv']) _checkFlag = isOkContours(_range) if _checkFlag == True: print(json.dumps({"code": 0, "color": color['color']}, sort_keys=True)) break if _checkFlag ==False: return False # get single color def isYellowCode(img_hsv): yellow_list = [item for item in _color_dic if item['color'] == 'yellow'] _checkFlag = False for color in yellow_list: _range = getColorRange(img_hsv, color['lower_hsv'],color['upper_hsv']) _checkFlag = isOkContours(_range) if _checkFlag == True: print(json.dumps({"code": 0, "color": color['color']}, sort_keys=True)) break if _checkFlag ==False: return False #识别码 def regRedCode(url): org_rbg_img = cv2.imread(url) org_rbg_img=cv2.resize(org_rbg_img,(590,1278)) # cv2.namedWindow("org_rbg_img", cv2.WINDOW_NORMAL) # cv2.imshow("org_rbg_img", org_rbg_img) barcodes = pyzbar.decode(org_rbg_img) codes=[] for barcode in barcodes: _rect=barcode.rect codeImage=org_rbg_img[_rect.top:(_rect.top + _rect.height),_rect.left:(_rect.width + _rect.left)] codes.append(codeImage) mergeImage=org_rbg_img if len(codes)>0: mergeImage = np.concatenate((codes), axis=2) img_hsv = cv2.cvtColor(mergeImage, cv2.COLOR_BGR2HSV) whatColor(img_hsv) # isRed=isRedCode(img_hsv) # if isRed==True: # print(json.dumps({"code": 0, "color": "red"}, sort_keys=True)) # return # yellow=isYellowCode(img_hsv) # if yellow==True: # print(json.dumps({"code": 0, "color": "yellow"}, sort_keys=True)) # return # print(json.dumps({"code": 0, "color": "other"}, sort_keys=True)) return #脚本入口,接受其他平台调用传参 if __name__ == '__main__': a = [] for i in range(1, len(sys.argv)): a.append(sys.argv[i]) if len(a)<0: print(json.dumps({"code": -1, "color": ""}, sort_keys=True)) regRedCode(a[0])
二,Java端主要是传递图片地址并调用脚本得到结果,通知通过钉钉指定人
Part2
//参数分别是 脚本的绝对位置,图片地址 String _regResult = CmdProcessUtils.invokePython("xxx/HeathCodeReg.py", localPath);
Part3 (calss CmdProcessUtils)
public synchronized static String invokePython(String pythonFile,String url){ if(org.apache.commons.lang3.StringUtils.isEmpty(url)){ return null; } try { String pythonPath=pythonFile; String[] args = new String[] { "python3", pythonPath, url}; Process proc = Runtime.getRuntime().exec(args);// 执行py文件 BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); String line = null; StringBuffer buffer=new StringBuffer(); while ((line = in.readLine()) != null) { buffer.append(line); } in.close(); proc.waitFor(); return buffer.toString(); } catch (Exception e) { e.printStackTrace(); } return null; }
经过生产环境测试,服务器4核8G识别速度可行!
常见疑问一:项目使用的环境是否必须为Python3.9,OpenCV4.5?
答:Python最好是3系列版本,因为2与3版本语法相差较大,OpenCV版本如果不使用4.5,那需要自行寻找相匹配的版本,识别脚本中相关的方法可能有差异需要调整。
常见疑问二:是否可以直接识别网络图片?
答:本项目中将图片下载到了本地,如果需要直接识别网络图,可以再脚本“regRedCode”方法中判断下,对于网络图片可以使用request先下载下来,然后将流转为byte给到opencv即可。
愿病毒早日消亡!!!