def Detect(self, request, context): """ real time detection task! """ t1 = time.perf_counter() anypb = Any() height = request.height width = request.width image = np.frombuffer(request.image, dtype=np.uint8) #if image.ndim == 1: # image = image.reshape([height,width]) if not request.isRaw: image = cv2.imdecode(image, cv2.IMREAD_GRAYSCALE) height, width = image.shape image = image.reshape([height, width]) # check image size, single-channel gray image if 0 == image.size or image.size != height * width: logging.error( f'image shape is {image.shape}, but height = {height}, width = {width}' ) return thyroidrpc_pb2.ProtoResponse( code=WRONG_IMAGE_SHAPE, msg=ErrorDict[WRONG_IMAGE_SHAPE], data=anypb) print(image.shape) if image.ndim == 1: image = image.reshape([height, width]) res = pymrcnn.ThyroidAIDoInference(image, height, width) nodule_nums = len(res) logging.info(f'nodules_nums: {nodule_nums}') if 0 == nodule_nums: nodules = thyroidrpc_pb2.Nodules(nums=0, nodule=[]) else: nodules_list = [] for i in range(nodule_nums): nodules_list.append( thyroidrpc_pb2.NoduleWithNum(n=i + 1, m=0, x=res[i][0], y=res[i][1], w=res[i][2], h=res[i][3], s=0, pos=0)) nodules = thyroidrpc_pb2.Nodules(nums=nodule_nums, nodule=nodules_list) print(nodules) # packing results to any anypb.Pack(nodules) t2 = time.perf_counter() logging.info('tensorrt: {:.2f}'.format((t2 - t1) * 1000)) return thyroidrpc_pb2.ProtoResponse(code=0, msg='', data=anypb)
def run(): # NOTE(gRPC Python Team): .close() is possible on a channel and should be # used in circumstances in which the with statement does not fit the needs # of the code. if (':' in args.server): ip = args.server else: ip = args.server + ':' + args.port print(f'connecting server: {ip}') with grpc.insecure_channel(ip) as channel: stub = thyroidrpc_pb2_grpc.ThyroidaiGrpcStub(channel) ################################################ ### 1. test first stage real time detection ### ################################################ isRaw = True if isRaw: img = cv2.imread('./images/190528152340059.png', 0) h, w = img.shape img = img.tostring() else: with open('./images/190528152340059.png', 'rb') as f: img = f.read() h, w = cv2.imread('./images/190528152340059.png', 0).shape response = stub.Detect( thyroidrpc_pb2.DetectRequest(isRaw=isRaw, image=img, height=h, width=w)) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: anypb = Any() anypb.CopyFrom(response.data) nodules = thyroidrpc_pb2.Nodules() anypb.Unpack(nodules) print(f'nodule number is: {nodules.nums}') for node in nodules.nodule: print( f'nudule {node.n}: ({node.x}, {node.y}, {node.w}, {node.h})' ) ########################################### ### 2. test second stage inference ### ########################################### print('----------------------') print('Second stage detect') print('----------------------') print('') user_id = '123456' scan_id = int(time.time() * 10) image_id = 1 list_nodule = [] cut = 1 for node in nodules.nodule: nx, ny, nw, nh = (node.x, node.y, node.w, node.h) list_nodule = [ thyroidrpc_pb2.NoduleWithNum(n=1, m=0, x=nx, y=ny, w=nw, h=nh, s=cut, pos=5) ] if cut == 1: cut = 2 elif cut == 2: cut = 1 else: pass response = stub.ClassifyAndTirads( thyroidrpc_pb2.CTRequest(uid=user_id, scanID=scan_id, imageID=image_id, isRaw=isRaw, image=img, height=h, width=w, ppc=160, nodule=list_nodule)) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: anypb = Any() anypb.CopyFrom(response.data) ctRes = thyroidrpc_pb2.CTResponse() anypb.Unpack(ctRes) print(f'total nodule number: {ctRes.nums}') print() for ds, nodule, bp, tirads in zip(ctRes.ds, ctRes.nodule, ctRes.bp, ctRes.tirads): print(f'nodule index: {ds.n}:') if ds.status != 0: print('nodule {} not detect !'.format(ds.n)) else: print('{:>25}: ({}, {}, {}, {})'.format( 'nodule x:', nodule.x, nodule.y, nodule.w, nodule.h)) print('{:>25}: {}'.format('benign', bp.benign)) print('{:>25}: {}'.format('corresponding prob', bp.prob)) print('{:>25}: {}'.format('constitute', tirads.constitute)) print('{:>25}: {}'.format('Comet tail quantity', tirads.comet)) print('{:>25}: {}'.format('Shape', tirads.shape)) print('{:>25}: {}'.format('Aspect ratio', tirads.ratio)) print('{:>25}: {}'.format('Horizontal axis(cm)', tirads.hxlen)) print('{:>25}: {}'.format('Vertical axis(cm)', tirads.vxlen)) print('{:>25}: {}'.format('echo level', tirads.echo_level)) print('{:>25}: {}'.format('border_clear', tirads.border_clear)) print('{:>25}: {}'.format('calcification', tirads.calcification[0])) print('{:>25}: {}'.format('calcification', tirads.calcification[1])) print('{:>25}: {}'.format('calcification', tirads.calcification[2])) print() print('----------------------') print('generating report') print('----------------------') print('') response = stub.GenerateReport( thyroidrpc_pb2.UserID(uid=user_id, scanID=scan_id)) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: anypb = Any() anypb.CopyFrom(response.data) ctRes = thyroidrpc_pb2.CTResponse() anypb.Unpack(ctRes) print(f'total nodule number: {ctRes.nums}') print() for ds, nodule, bp, tirads in zip(ctRes.ds, ctRes.nodule, ctRes.bp, ctRes.tirads): print(f'nodule index: {ds.n}:') if ds.status != 0: print('nodule {} not detect !'.format(ds.n)) else: print('{:>25}: ({}, {}, {}, {})'.format( 'nodule x:', nodule.x, nodule.y, nodule.w, nodule.h)) print('{:>25}: {}'.format('benign', bp.benign)) print('{:>25}: {}'.format('corresponding prob', bp.prob)) print('{:>25}: {}'.format('constitute', tirads.constitute)) print('{:>25}: {}'.format('Comet tail quantity', tirads.comet)) print('{:>25}: {}'.format('Shape', tirads.shape)) print('{:>25}: {}'.format('Aspect ratio', tirads.ratio)) #print('{:>25}: {}'.format('frond-end (cm)', tirads.hxlen[0])) #print('{:>25}: {}'.format('left-right(cm)', tirads.hxlen[1])) print('{:>25}: {}'.format('Horizontal axis(cm)', tirads.hxlen)) print('{:>25}: {}'.format('Vertical axis(cm)', tirads.vxlen)) print('{:>25}: {}'.format('pos_extend', nodule.pos)) print('{:>25}: {}'.format('echo level', tirads.echo_level)) print('{:>25}: {}'.format('border_clear', tirads.border_clear)) print('{:>25}: {}'.format('calcification', tirads.calcification[0])) print('{:>25}: {}'.format('calcification', tirads.calcification[1])) print('{:>25}: {}'.format('calcification', tirads.calcification[2])) print()
def run(): # NOTE(gRPC Python Team): .close() is possible on a channel and should be # used in circumstances in which the with statement does not fit the needs # of the code. if (':' in args.server): ip = args.server else: ip = args.server + ':' + args.port print(f'connecting server: {ip}') with grpc.insecure_channel(ip) as channel: stub = thyroidrpc_pb2_grpc.ThyroidaiGrpcStub(channel) ################################################ ### 1. test first stage real time detection ### ################################################ isRaw = True img_path = './images/03.jpg' if isRaw: img = cv2.imread(img_path, 0) cv2.imwrite('input.png', img) h, w = img.shape img = img.tostring() else: with open(img_path, 'rb') as f: img = f.read() h, w = cv2.imread(img_path, 0).shape response = stub.Detect( thyroidrpc_pb2.DetectRequest(isRaw=isRaw, image=img, height=h, width=w)) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: anypb = Any() anypb.CopyFrom(response.data) nodules = thyroidrpc_pb2.Nodules() anypb.Unpack(nodules) print(f'nodule number is: {nodules.nums}') for node in nodules.nodule: print( f'nudule {node.n}: ({node.x}, {node.y}, {node.w}, {node.h})' ) ########################################### # ##### test image interface ######## ############################################ # isRaw = True # request = thyroidrpc_pb2.DetectRequest(isRaw=isRaw, image=img, height=h, width=w) # response = stub.ImageOutput(thyroidrpc_pb2.DetectRequestNew(isRaw=isRaw, image=img, height=h, width=w, ppc=80)) # if (response.code !=0 ): # print('error code: {}'.format(response.code)) # print('error message: {}'.format(response.msg)) # else: # anypb = Any() # anypb.CopyFrom(response.data) # ImageRequest = thyroidrpc_pb2.ImageRequest() # anypb.Unpack(ImageRequest) # new_img = np.frombuffer(ImageRequest.image, dtype=np.uint8) # new_img = new_img.reshape([h, w]) # return_msgs = ImageRequest.msg # for return_msg in return_msgs: # print(return_msg.decode(encoding="utf-8")) # # cv2.imwrite('output.png',new_img) # # cv2.imshow('input', cv2.imread(img_path, 0)) # # cv2.imshow('output', new_img) # # cv2.waitKey(0) ########################################### ### 2. test second stage inference ### ########################################### print('----------------------') print('Second stage detect') print('----------------------') print('') user_id = '123456' scan_id = int(time.time() * 10) image_id = 1 list_nodule = [] cut = 1 for node in nodules.nodule: """ # extending bounding box to 1.5 nx = max(0, int(node.x - 0.25 * node.w)) ny = max(0, int(node.y - 0.25 * node.h)) nw = min(w, int(node.w * 1.5)) nh = min(h, int(node.h * 1.5)) """ nx, ny, nw, nh = (node.x, node.y, node.w, node.h) list_nodule.append( thyroidrpc_pb2.NoduleWithNum(n=node.n, m=0, x=nx, y=ny, w=nw, h=nh, s=cut, pos=5)) if cut == 1: cut = 2 elif cut == 2: cut = 1 else: pass response = stub.ClassifyAndTirads( thyroidrpc_pb2.CTRequest(uid=user_id, scanID=scan_id, imageID=image_id, isRaw=isRaw, image=img, height=h, width=w, ppc=160, nodule=list_nodule)) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: anypb = Any() anypb.CopyFrom(response.data) ctRes = thyroidrpc_pb2.CTResponse() anypb.Unpack(ctRes) print(f'total nodule number: {ctRes.nums}') print() for ds, nodule, bp, tirads in zip(ctRes.ds, ctRes.nodule, ctRes.bp, ctRes.tirads): print(f'nodule index: {ds.n}:') if ds.status != 0: print('nodule {} not detect !'.format(ds.n)) else: print('{:>25}: ({}, {}, {}, {})'.format( 'nodule x:', nodule.x, nodule.y, nodule.w, nodule.h)) print('{:>25}: {}'.format('benign', bp.benign)) print('{:>25}: {}'.format('corresponding prob', bp.prob)) print('{:>25}: {}'.format('constitute', tirads.constitute)) print('{:>25}: {}'.format('Comet tail quantity', tirads.comet)) print('{:>25}: {}'.format('Shape', tirads.shape)) print('{:>25}: {}'.format('Aspect ratio', tirads.ratio)) print('{:>25}: {}'.format('Horizontal axis(cm)', tirads.hxlen)) print('{:>25}: {}'.format('Vertical axis(cm)', tirads.vxlen)) print('{:>25}: {}'.format('echo level', tirads.echo_level)) print('{:>25}: {}'.format('border_clear', tirads.border_clear)) print('{:>25}: {}'.format('calcification', tirads.calcification[0])) print('{:>25}: {}'.format('calcification', tirads.calcification[1])) print('{:>25}: {}'.format('calcification', tirads.calcification[2])) print() print('------------------------') print('Second stage re-request') print('------------------------') print('') list_nodule = [] cut = 1 for node in nodules.nodule: # extending bounding box to 1.5 nx = max(0, int(node.x - 0.25 * node.w)) ny = max(0, int(node.y - 0.25 * node.h)) nw = min(w, int(node.w * 1.5)) nh = min(h, int(node.h * 1.5)) if node.n == 1: list_nodule.append( thyroidrpc_pb2.NoduleWithNum(n=node.n, m=0, x=nx, y=ny, w=nw, h=nh, s=cut, pos=5)) else: list_nodule.append( thyroidrpc_pb2.NoduleWithNum(n=node.n + 1, m=node.n, x=nx, y=ny, w=nw, h=nh, s=cut, pos=5)) if cut == 1: cut = 2 elif cut == 2: cut = 1 else: pass response = stub.ClassifyAndTirads( thyroidrpc_pb2.CTRequest(uid=user_id, scanID=scan_id, imageID=image_id, isRaw=isRaw, image=img, height=h, width=w, ppc=160, nodule=list_nodule)) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: anypb = Any() anypb.CopyFrom(response.data) ctRes = thyroidrpc_pb2.CTResponse() anypb.Unpack(ctRes) print(f'total nodule number: {ctRes.nums}') print() for ds, nodule, bp, tirads in zip(ctRes.ds, ctRes.nodule, ctRes.bp, ctRes.tirads): print(f'nodule index: {ds.n}:') if ds.status != 0: print('nodule {} not detect !'.format(ds.n)) else: print('{:>25}: ({}, {}, {}, {})'.format( 'nodule x:', nodule.x, nodule.y, nodule.w, nodule.h)) print('{:>25}: {}'.format('benign', bp.benign)) print('{:>25}: {}'.format('corresponding prob', bp.prob)) print('{:>25}: {}'.format('constitute', tirads.constitute)) print('{:>25}: {}'.format('Comet tail quantity', tirads.comet)) print('{:>25}: {}'.format('Shape', tirads.shape)) print('{:>25}: {}'.format('Aspect ratio', tirads.ratio)) print('{:>25}: {}'.format('Horizontal axis(cm)', tirads.hxlen)) print('{:>25}: {}'.format('Vertical axis(cm)', tirads.vxlen)) print('{:>25}: {}'.format('echo level', tirads.echo_level)) print('{:>25}: {}'.format('border_clear', tirads.border_clear)) print('{:>25}: {}'.format('calcification', tirads.calcification[0])) print('{:>25}: {}'.format('calcification', tirads.calcification[1])) print('{:>25}: {}'.format('calcification', tirads.calcification[2])) print() print('----------------------') print('deleting nodule') print('----------------------') print('') response = stub.DeleteNodule( thyroidrpc_pb2.DeleteNoduleRequest(uid=user_id, scanID=scan_id, imageID=image_id, noduleID=[3])) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: print('deleting ok!') print('----------------------') print('generating report') print('----------------------') print('') response = stub.GenerateReport( thyroidrpc_pb2.UserID(uid=user_id, scanID=scan_id)) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: anypb = Any() anypb.CopyFrom(response.data) ctRes = thyroidrpc_pb2.CTResponse() anypb.Unpack(ctRes) print(f'total nodule number: {ctRes.nums}') print() for ds, nodule, bp, tirads in zip(ctRes.ds, ctRes.nodule, ctRes.bp, ctRes.tirads): print(f'nodule index: {ds.n}:') if ds.status != 0: print('nodule {} not detect !'.format(ds.n)) else: print('{:>25}: ({}, {}, {}, {})'.format( 'nodule x:', nodule.x, nodule.y, nodule.w, nodule.h)) print('{:>25}: {}'.format('benign', bp.benign)) print('{:>25}: {}'.format('corresponding prob', bp.prob)) print('{:>25}: {}'.format('constitute', tirads.constitute)) print('{:>25}: {}'.format('Comet tail quantity', tirads.comet)) print('{:>25}: {}'.format('Shape', tirads.shape)) print('{:>25}: {}'.format('Aspect ratio', tirads.ratio)) print('{:>25}: {}'.format('frond-end (cm)', tirads.hxlen[0])) print('{:>25}: {}'.format('left-right(cm)', tirads.hxlen[1])) print('{:>25}: {}'.format('up-down (cm)', tirads.vxlen)) print('{:>25}: {}'.format('pos_extend', nodule.pos)) print('{:>25}: {}'.format('echo level', tirads.echo_level)) print('{:>25}: {}'.format('border_clear', tirads.border_clear)) print('{:>25}: {}'.format('calcification', tirads.calcification[0])) print('{:>25}: {}'.format('calcification', tirads.calcification[1])) print('{:>25}: {}'.format('calcification', tirads.calcification[2])) print() print('----------------------') print('modify report') print('----------------------') print('') response = stub.ModifyReport( thyroidrpc_pb2.ModifyCT(uid=user_id, scanID=scan_id, nindex=0, benign=[2], prob=[0.91], constitute=[2], shape=[0], echo_level=[3], border_clear=[0])) if (response.code != 0): print('error code: {}'.format(response.code)) response = stub.ModifyReport( thyroidrpc_pb2.ModifyCT(uid=user_id, scanID=scan_id, nindex=1, benign=[2], prob=[0.92], constitute=[3], shape=[0], echo_level=[4], border_clear=[0])) if (response.code != 0): print('error code: {}'.format(response.code)) response = stub.ReadReport( thyroidrpc_pb2.UserID(uid=user_id, scanID=scan_id)) if (response.code != 0): print('error code: {}'.format(response.code)) print('error message: {}'.format(response.msg)) else: anypb = Any() anypb.CopyFrom(response.data) ctRes = thyroidrpc_pb2.CTResponse() anypb.Unpack(ctRes) print(f'total nodule number: {ctRes.nums}') print() for ds, nodule, bp, tirads in zip(ctRes.ds, ctRes.nodule, ctRes.bp, ctRes.tirads): print(f'nodule index: {ds.n}:') if ds.status != 0: print('nodule {} not detect !'.format(ds.n)) else: print('{:>25}: ({}, {}, {}, {})'.format( 'nodule x:', nodule.x, nodule.y, nodule.w, nodule.h)) print('{:>25}: {}'.format('benign', bp.benign)) print('{:>25}: {}'.format('corresponding prob', bp.prob)) print('{:>25}: {}'.format('constitute', tirads.constitute)) print('{:>25}: {}'.format('Comet tail quantity', tirads.comet)) print('{:>25}: {}'.format('Shape', tirads.shape)) print('{:>25}: {}'.format('Aspect ratio', tirads.ratio)) print('{:>25}: {}'.format('frond-end (cm)', tirads.hxlen[0])) print('{:>25}: {}'.format('left-right(cm)', tirads.hxlen[1])) print('{:>25}: {}'.format('up-down (cm)', tirads.vxlen)) print('{:>25}: {}'.format('pos_extend', nodule.pos)) print('{:>25}: {}'.format('echo level', tirads.echo_level)) print('{:>25}: {}'.format('border_clear', tirads.border_clear)) print('{:>25}: {}'.format('calcification', tirads.calcification[0])) print('{:>25}: {}'.format('calcification', tirads.calcification[1])) print('{:>25}: {}'.format('calcification', tirads.calcification[2])) print()
def ReadReport(self, request, context): """ read final report """ logging.info('---------------------------------------') logging.info('ReadReport') t1 = time.perf_counter() # output list list_ds = [] list_nodule = [] list_benign_prob = [] list_tirads = [] modify_flag = [] # return mongodb report for nodule_report in userdb.report.find({ 'uid': request.uid, 'scanID': request.scanID }): modify_flag.append(True) #nodule index nindex = nodule_report['nodule'] # status list_ds.append(thyroidrpc_pb2.DetectionStatus(n=nindex, status=0)) # position x, y, w, h, = nodule_report['pos'] s = nodule_report['cut'] pos_extend = nodule_report['pos_extend'] list_nodule.append( thyroidrpc_pb2.NoduleWithNum(n=nindex, m=0, x=x, y=y, w=w, h=h, s=s, pos=pos_extend)) # classification benign = nodule_report['benign'] prob = nodule_report['prob'] list_benign_prob.append( thyroidrpc_pb2.BenignAndProb(benign=benign, prob=prob)) # tirads constitute = nodule_report['constitute'] comet = nodule_report['comet'] shape = nodule_report['shape'] border_clear = nodule_report['border_clear'] echo_level = nodule_report['echo_level'] ratio = nodule_report['ratio'] hxlen = nodule_report['hxlen'] vxlen = nodule_report['vxlen'] calcification = nodule_report['calcification'] list_tirads.append( thyroidrpc_pb2.OneTiradsRes(constitute=constitute, comet=comet, shape=shape, ratio=ratio, hxlen=hxlen, vxlen=vxlen, echo_level=echo_level, border_clear=border_clear, calcification=calcification)) nums = userdb.report.count_documents({ 'uid': request.uid, 'scanID': request.scanID }) anypb = Any() anypb.Pack( thyroidrpc_pb2.CTResponse(nums=nums, ds=list_ds, nodule=list_nodule, bp=list_benign_prob, tirads=list_tirads, modify_flag=modify_flag)) t2 = time.perf_counter() logging.info('read report: {:.2f}'.format((t2 - t1) * 1000)) return thyroidrpc_pb2.ProtoResponse(code=0, msg='', data=anypb)
def GenerateReport(self, request, context): """generating report function """ logging.info('---------------------------------------') logging.info('GenerateReport') t1 = time.perf_counter() logging.info(f'uid: {request.uid}, scanID: {request.scanID}') # we should only process the un-generate report records # record has flag *flag_report* # at first count module appearing times in all images nodule_dict = {} # key(nodule index): value(appear times) for user in userdb.collection.find({ 'uid': request.uid, 'scanID': request.scanID, 'flag_report': False }): key = user['nodule'] nodule_dict[key] = nodule_dict.get(key, 0) + 1 # merge results to one record # loop over nodule index for index, value in nodule_dict.items(): logging.info(f'nodule index: {index}, un-gerated records: {value}') if value > 1: # merge results one_record = self._merge_records(request.uid, request.scanID, index, value) else: # set *flag_report* True, means this record has been used in final report! one_record = userdb.collection.find_one_and_update( { 'uid': request.uid, 'scanID': request.scanID, 'nodule': index }, {'$set': { 'flag_report': True }}, return_document=pymongo.ReturnDocument.AFTER) del one_record['_id'] del one_record['imageID'] del one_record['flag_report'] one_record[ 'flag_modified'] = False # means the doctor does not modify the report! # merge one_record and report report = userdb.report.find_one({ 'uid': request.uid, 'scanID': request.scanID, 'nodule': index }) # if no report, just insert one! if report is None: userdb.report.insert_one(one_record) else: self._merge_record_and_report(one_record, report) # output list list_ds = [] list_nodule = [] list_benign_prob = [] list_tirads = [] modify_flag = [] # return mongodb report for nodule_report in userdb.report.find({ 'uid': request.uid, 'scanID': request.scanID }): # modify_flag modify_flag.append(False) #nodule index nindex = nodule_report['nodule'] # status list_ds.append(thyroidrpc_pb2.DetectionStatus(n=nindex, status=0)) # position x, y, w, h, = nodule_report['pos'] s = nodule_report['cut'] pos_extend = nodule_report['pos_extend'] list_nodule.append( thyroidrpc_pb2.NoduleWithNum(n=nindex, m=0, x=x, y=y, w=w, h=h, s=s, pos=pos_extend)) # classification benign = nodule_report['benign'] prob = nodule_report['prob'] list_benign_prob.append( thyroidrpc_pb2.BenignAndProb(benign=benign, prob=prob)) # tirads constitute = nodule_report['constitute'] comet = nodule_report['comet'] shape = nodule_report['shape'] border_clear = nodule_report['border_clear'] echo_level = nodule_report['echo_level'] ratio = nodule_report['ratio'] hxlen = nodule_report['hxlen'] vxlen = nodule_report['vxlen'] calcification = nodule_report['calcification'] list_tirads.append( thyroidrpc_pb2.OneTiradsRes(constitute=constitute, comet=comet, shape=shape, ratio=ratio, hxlen=hxlen, vxlen=vxlen, echo_level=echo_level, border_clear=border_clear, calcification=calcification)) nums = userdb.report.count_documents({ 'uid': request.uid, 'scanID': request.scanID }) anypb = Any() anypb.Pack( thyroidrpc_pb2.CTResponse(nums=nums, ds=list_ds, nodule=list_nodule, bp=list_benign_prob, tirads=list_tirads, modify_flag=modify_flag)) t2 = time.perf_counter() logging.info('generate report: {:.2f}'.format((t2 - t1) * 1000)) return thyroidrpc_pb2.ProtoResponse(code=0, msg='', data=anypb)
def ClassifyAndTirads(self, request, context): """ classification, tirads """ logging.info('---------------------------------------') logging.info('ClassifyAndTirads') logging.info( f'uid: {request.uid}, scanID: {request.scanID}, imageID: {request.imageID}' ) anypb = Any() image_gray = np.frombuffer(request.image, dtype=np.uint8) height = request.height width = request.width if not request.isRaw: image_gray = cv2.imdecode(image_gray, cv2.IMREAD_GRAYSCALE) height, width = image_gray.shape # check image size if 0 == image_gray.size or image_gray.size != height * width: logging.error( f'image shape is {image_gray.shape}, but height = {height}, width = {width}' ) return thyroidrpc_pb2.ProtoResponse( code=WRONG_IMAGE_SHAPE, msg=ErrorDict[WRONG_IMAGE_SHAPE], data=anypb) if image_gray.ndim == 1: image_gray = image_gray.reshape([height, width]) logging.info(f'image shape is height = {height}, width = {width}') # gray to rgb image = cv2.cvtColor(image_gray, cv2.COLOR_GRAY2BGR) # check input nodule number if len(request.nodule) <= 0: logging.error( f'input nodule numbers should large than zero, but your input is: {len(request.nodule)}' ) return thyroidrpc_pb2.ProtoResponse( code=WRONG_INPUT_NODELES, msg=ErrorDict[WRONG_INPUT_NODELES], data=anypb) """ what's the logic? 1. loop over request bounding box, use second stage maskrcnn to generate new mask 2. for each new mask/nodule, do classification task 3. for each new mask/nodule, do tirads task 4. save the results for final report """ # output list list_ds = [] list_nodule = [] list_benign_prob = [] list_tirads = [] modify_flag = [] # loop over all the nodules for nodule in request.nodule: modify_flag.append(False) t1 = time.perf_counter() logging.info( f'nodule index: {nodule.n}, pos = ({nodule.x}, {nodule.y}, {nodule.w}, {nodule.h})' ) # 1. do second stage segmentation bbox_input = (nodule.x, nodule.y, nodule.x + nodule.w, nodule.y + nodule.h) try: mask, bbox = maskrcnn2seg.do_inference(image_gray, bbox_input) print(bbox) except: logging.error('second stage detection exception') return thyroidrpc_pb2.ProtoResponse( code=SECOND_DETECTION_FAIL, msg=ErrorDict[SECOND_DETECTION_FAIL], data=anypb) if mask.size == 0: logging.error( f'{nodule.n}: second stage not detect any nodule!') list_ds.append( thyroidrpc_pb2.DetectionStatus(n=nodule.n, status=1)) list_nodule.append([]) list_benign_prob.append([]) list_tirads.append([]) continue else: list_nodule.append( thyroidrpc_pb2.NoduleWithNum(n=nodule.n, m=nodule.m, x=bbox[0], y=bbox[1], w=bbox[2] - bbox[0], h=bbox[3] - bbox[1], s=nodule.s, pos=nodule.pos)) bbox_pos = [ bbox[0].item(), bbox[1].item(), (bbox[2] - bbox[0]).item(), (bbox[3] - bbox[1]).item() ] t2 = time.perf_counter() logging.info('nodule {}, second detection time: {:.2f}'.format( nodule.n, (t2 - t1) * 1000)) # 2. do classification t1 = time.perf_counter() try: benign, prob = rescnn.do_inference(image_gray, mask) print(benign, prob) except: logging.error('classification fail!') return thyroidrpc_pb2.ProtoResponse( code=CLASSIFICATION_FAIL, msg=ErrorDict[CLASSIFICATION_FAIL], data=anypb) # 1: benign; 2: not benign benign += 1 list_benign_prob.append( thyroidrpc_pb2.BenignAndProb(benign=benign, prob=prob)) t2 = time.perf_counter() logging.info('nodule {}, classification time: {:.2f}'.format( nodule.n, (t2 - t1) * 1000)) # 3. do tirads """ constitute 构成 0: 实性 1: 实性为主 2: 囊性为主 3: 囊性 comet 彗星尾数量, 不过不要显示数量,直接大于1时显示有彗星尾就行 shape 0: 形状规则 1: 形状不规则 ar # 纵横比 hx_len # 水平轴 如果是横切,它就是左右径,如果是纵切,它就是上下径 vx_len # 垂直轴 它只有可能是前后径 echo_code # 回声水平 0: 无回声 2: 低回声 3: 等回声 4: 高回声 border_clear # 边界清晰模糊 0: 模糊 1: 清晰 calcification[0] # 0:无微钙化 1: 有微钙化 calcification[1] # 0:无粗钙化 1: 有粗钙化 calcification[2] # 0:无环钙化 1: 有环钙化 """ # 3.0 diffuse, c_s try: anno, c_s = pymrcnn_part.ThyroidAIDoInference( image_gray, height, width) print(anno, c_s) except: logging.error('part detection fail!') return thyroidrpc_pb2.ProtoResponse( code=TIRADS_FAIL, msg=ErrorDict[CLASSIFICATION_FAIL], data=anypb) try: if nodule.w * nodule.h < 100000: diffusion = diffuse.inference(image_gray, anno) else: diffusion = 2 except: logging.error('diffuse fail!') return thyroidrpc_pb2.ProtoResponse( code=TIRADS_FAIL, msg=ErrorDict[CLASSIFICATION_FAIL], data=anypb) t1 = time.perf_counter() tirads = TiradsRecognition(image_gray, mask, is_debug=False) # 3.1 constitute constitute, cys_mask = tirads.find_or_report_constitute("report") # 3.2 comet if constitute <= 1: comet = 0 else: comet = tirads.find_comet_calcify("report", cys_mask) # 3.3 ratio and hx_len, vx_len ar, vx, hx = tirads.estimate_aspect_ratio() hx_len, vx_len = tirads.get_axis_len(hx), tirads.get_axis_len(vx) pixels_one_cm = request.ppc hx_len /= pixels_one_cm vx_len /= pixels_one_cm hx_len = round(hx_len, 8) vx_len = round(vx_len, 8) if nodule.s == 1: hx_list = [hx_len, 0] else: hx_list = [0, hx_len] # 3.4 shape shape = tirads.classify_shape() # TODO: echo lever # 3.5 echo level #echo_code = tirads.compute_nodule_echo(mask_cyst=cys_mask) echo_code = 2 # 3.6 border border_clear = tirads.border_clear_fuzzy() # 3.7 calcification 钙化 if comet > 0: calcification = tirads.find_or_report_calcification( pixels_one_cm) else: calcification = [0, 0, 0] # 加入弥漫性结果 calcification.append(diffusion) list_tirads.append( thyroidrpc_pb2.OneTiradsRes(constitute=constitute, comet=comet, shape=shape, ratio=ar, hxlen=hx_list, vxlen=vx_len, echo_level=echo_code, border_clear=border_clear, calcification=calcification)) # at last we set status ok list_ds.append(thyroidrpc_pb2.DetectionStatus(n=nodule.n, status=0)) t2 = time.perf_counter() logging.info('nodule {}, tirads time: {:.2f}'.format( nodule.n, (t2 - t1) * 1000)) # save results to mongodb # if need update, replace old result if nodule.m > 0: old_index = nodule.m logging.info(f'rename nodule index: {nodule.m} -> {nodule.n}') else: old_index = nodule.n logging.info(f'and/replace nodule: {nodule.n}') try: userdb.collection.find_one_and_replace( { 'uid': request.uid, 'scanID': request.scanID, 'imageID': request.imageID, 'nodule': old_index }, { 'uid': request.uid, 'scanID': request.scanID, 'imageID': request.imageID, 'nodule': nodule.n, 'flag_report': False, # means not used by report! 'pos': bbox_pos, 'pos_extend': nodule.pos, 'benign': benign, 'prob': prob, 'comet': comet, 'constitute': constitute, 'shape': shape, 'echo_level': echo_code, 'border_clear': border_clear, 'cut': nodule.s, 'ratio': ar, 'hxlen': hx_list, 'vxlen': vx_len, 'calcification': calcification }, upsert=True # if not find, just insert ) except: logging.error('userdb.collection.find_one_and_replace fail!') return thyroidrpc_pb2.ProtoResponse( code=DB_REPLACE_FAIL, msg=ErrorDict[DB_REPLACE_FAIL], data=anypb) # pack the proto result anypb.Pack( thyroidrpc_pb2.CTResponse(nums=len(request.nodule), ds=list_ds, nodule=list_nodule, bp=list_benign_prob, tirads=list_tirads, modify_flag=modify_flag)) return thyroidrpc_pb2.ProtoResponse(code=0, msg='', data=anypb)