def test_iodine(im_list, legacy, sheet): print(f"[INFO] IODINE TEST") loaded_images = [get_data(image)[0] for image in im_list] pix_dim = im_list[0].PixelSpacing images = loaded_images[:] images = [img.astype("uint32") for img in images] standard_circles = [(256, 256, 13), (256, 415, 13), (415, 256, 13), (95, 256, 13), (256, 95, 13)] tollerance = 5 cir = [] dis = [] for img in images: found = find_circles(img) final_circles = clean_circles(found, standard_circles) cir.append(final_circles) passed, results, df, means = check_iodine(loaded_images, cir) st.write(df) if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet}") leg = legacyWriter(legacy, sheet) leg.write_iodine_report(results)
def test_lowcontrast(dcm_images, legacy=None, sheet=None): print(f"[INFO] LOW CONTRAST TEST..") st.write(f"[INFO] LOW CONTRAST TEST..") #load dicom image #images_list=glob.glob(os.path.join(path,"*.DCM")) #dcm_images=[pydicom.dcmread(image) for image in images_list] images = [get_data(image)[0] for image in dcm_images] pix_dim = dcm_images[0].PixelSpacing dimensions_mm = [3, 4, 6, 9, 12, 18] computed_dim = [ int(round(0.886227 * dim_mm * 512 / 227, 0)) for dim_mm in dimensions_mm ] start_x, start_y, w, h = (148, 148, 216, 216) cropped = [crop_img(image, start_x, start_y, w, h) for image in images] means = compute_means(cropped, computed_dim) passed, mu_list, std_list, lcd, fig = check_contrast(means) st.write(fig) if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet} ...") st.write(f"[INFO] legacy mode: work on {legacy} in {sheet} ...") leg = legacyWriter(legacy, sheet) leg.write_lowcontrast_report(mu_list, std_list, lcd)
def test_linearity(lin_img, legacy=None, sheet=None, option="fbp"): """The idea is to preprocess the image to find some cirlce with hough gradients, find the others by simmetry and test the density vs ct number""" print(f"[INFO] LINEARITY TEST") st.markdown(f"[INFO] LINEARITY TEST") #lin_img = pydicom.dcmread(path) # load image for linear spacing img, pix_dim = get_data(lin_img) # rescale img roi_diameter = 10 circles, display = find_native_circles(img) # start finding circles points = find_circles_simmetry(circles, img, pix_dim) #complete circles search if len(points) != 8: print( f"{colored('[WARNING]','red')}[WARNING] Could not find all the circles. Using default list instead" ) st.markdown( f"[<font color='orange'>WARNING</font>] Could not find all the circles. Using default list instead", unsafe_allow_html=True) points = base #eventually manual changes are possible mean_r = int((roi_diameter // 2) / pix_dim[0]) checking = False l = 0 while checking: new, bw = check_roi(display, mean_r, points) if len(new) > l: points, l = modify_roi(points, new, bw) else: checking = False print(f"[INFO] ROis accepted, going to linearity test..") st.write(f"[INFO] ROis accepted, going to linearity test..") materials = [] materials.append({"name": "Air", "density": 0}) materials.append({"name": "PMP", "density": 0.83}) materials.append({"name": "LDPE", "density": 0.92}) materials.append({"name": "Polystyrene", "density": 1.05}) materials.append({"name": "Acrilyc", "density": 1.18}) materials.append({"name": "Delrin", "density": 1.41}) materials.append({"name": "Teflon", "density": 2.16}) dens_df = pd.DataFrame(materials) dens_df['ct'] = dens_df['density'].apply(density_2_ct) # generate the rois and measure the mean number in each one linearity_rois = [] for p in points.values(): res, lookup = select_roi(img, p[0], p[1], 10, pix_dim) linearity_rois.append(mask_values(res, lookup)) lin = get_hu(linearity_rois)[1:] check_linearity(lin, dens_df["density"].values, 0.99) if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet}") st.write(f"[INFO] legacy mode: work on {legacy} in {sheet}") leg = legacyWriter(legacy, sheet) leg.write_linearity_report(lin, option)
def test_uniformity(dcm_img,legacy=None,sheet=None,tipology="body"): #set limits if tipology=="body": max_difference=10 elif tipology=="head": max_difference = 3 elif tipology=="multislice_monoenergetic": max_difference = 3 print(f"[INFO] UNIFORMITY, NOISE AND CT NUMBER CHECK - {tipology.upper()} MODE") st.write(f"[INFO] UNIFORMITY, NOISE AND CT NUMBER CHECK - {tipology.upper()} MODE") """This function perform all the processing and the test needed for water ct, uniformity and noise test""" if save_images: os.makedirs("cq_images", exist_ok=True) #dcm_slice = pydicom.dcmread(path) img, pix_dim = get_data(dcm_img) #select the center roi res,lookup=select_roi(img,img.shape[0]//2,img.shape[0]//2,20,pix_dim,save=save_images,outname="center") roi=mask_values(res,lookup) border_rois, images = make_border_rois(img,pix_dim, 30, 20) print(f"[INFO] Starting default test for quality control for image {tipology}...") st.write(f"[INFO] Starting default test for quality control for image {tipology}...") results=[] # 2.Noise noise,std=check_noise(roi) # 3. Water CT water,mean=check_waterct(roi) # 4. Uniformity uniformity,means=check_uniformity(roi, border_rois,max_difference=max_difference) border_std=[check_noise(br)[1] for br in border_rois] res_dic = {'noise': std, 'water_ct': mean, 'uniformity_left': means[0], 'std_left':border_std[0], 'uniformity_up': means[1],'std_up':border_std[1], 'uniformity_right': means[2], 'std_right':border_std[2], 'uniformity_bottom': means[3], 'std_bottom':border_std[3], 'noise_test': noise, 'water_ct_test': water, 'uniformity_test': uniformity} results.append(res_dic) print("\n\n") df=pd.DataFrame(results) print(f"[INFO] printing out results for noise, water and uniformity test: \n {df}") st.write(f"[INFO] printing out results for noise, water and uniformity test:") st.write(df) if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet}") leg=legacyWriter(legacy,sheet) if tipology=="body": leg.write_uniformity_body_report(df) elif tipology=="head": leg.write_uniformity_head_report(df) elif tipology=="multislice_monoenergetic": leg.write_uniformity_monoenergetic_report(df)
def test_thickness_singleslice(path, legacy, sheet, function): print(f"[INFO] THICKNESS TEST - SINGLE SLICE MODE") st.write(f"[INFO] THICKNESS TEST - SINGLE SLICE MODE") #thk_img=pydicom.dcmread(path) img,pix_dim=get_data(path) center = crop_img(img, 170, 150, 180, 200) dst=np.zeros(img.shape) norma=cv2.normalize(center,dst,0,255,cv2.NORM_MINMAX) norma = norma.astype("uint8") out = band_filter(norma).astype("uint8") conts, hierarchy = cv2.findContours(out, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) contours_poly = [None]*len(conts) boundRect = [None]*len(conts) for i, c in enumerate(conts): contours_poly[i] = cv2.approxPolyDP(c, 3, True) boundRect[i] = cv2.boundingRect(contours_poly[i]) nord = [] sud = [] est = [] ovest = [] area_min = 18 for r in boundRect: if r[2] * r[3] > area_min: # if greater than area min compute if (r[0] < 130 and r[0] > 60): if (r[1] < 100): sud.append(r) else: nord.append(r) elif (r[1] < 130 and r[1] > 60): if (r[0] < 100): est.append(r) else: ovest.append(r) keptRect = [nord, sud, est, ovest] #compute the test passed,thick_mean=check_thickness_lines(keptRect, center, pix_dim) if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet} WARNING: This must be the semester dose file..." ) st.write(f"[INFO] legacy mode: work on {legacy} in {sheet} WARNING: This must be the semester dose file..." ) leg = legacyWriter(legacy, sheet) leg.write_singleslice_thickness_report(thick_mean)
def test_contrast_resolution_catphan(dcm_img, legacy=None, sheet=None, filter="std", debug=True): #ris_img = pydicom.dcmread(path) # load image for linear spacing img, pix_dim = get_data(dcm_img) # rescale img print(f"[INFO Running contrast and resolution test for Catphan phantom..]") st.write( f"[INFO Running contrast and resolution test for Catphan phantom..]") angles = [ -27.263, -50.386, -74.330, -93.947, -112.642, -131.928, -149.233, -166.894, -178.693, 167, 149.998, 136.196, 120.356 ] #angles= [-25,-50,-75,-95,-112,-130,-150,-165,-180,170,150,135,120] outer_R = 95 inner_R = 12 radis = [13, 11, 11, 8, 6, 7, 6, 5, 4, 4, 4, 4, 3] rois = [] for (i, alfa) in enumerate(angles): # compute the center x = int(round(outer_R * math.cos(alfa * math.pi / 180.), 0)) + img.shape[0] // 2 y = -int(round(outer_R * math.sin(alfa * math.pi / 180.), 0)) + img.shape[1] // 2 rois.append((x, y, radis[i])) metallic, background, noise = check_contrast_resolution_catphan( img, rois, option=filter) if debug: #show img with rois drawing = img.copy() for i in rois: x, y, inner_R = i cv2.circle(drawing, (x, y), inner_R, (255, 255, 255), 2) fig, ax = plt.subplots(1) ax.imshow(drawing, cmap="gray") st.write(fig) if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet}") st.write(f"[INFO] legacy mode: work on {legacy} in {sheet}") leg = legacyWriter(legacy, sheet) leg.write_resolution_catphan_report(metallic, background, noise, filter)
def test_cart(data, legacy, sheet): print(f"[INFO] Cart displacement test") st.write(f"[INFO] Cart displacement test") if abs(data[0] - data[1]) < 6: print( f"[INFO] Forward cart displacement test: {colored('[SUCCESS]','green')}" ) st.markdown( f"[INFO] Forward cart displacement test: <font color='green'>[SUCCESS]</font>", unsafe_allow_html=True) else: print( f"[WARNING] Forward cart displacement test: {colored('[FAILED]', 'red')}" ) st.markdown( f"[WARNING] Forward cart displacement test: <font color='red'>[FAILED]</font>", unsafe_allow_html=True) if abs(data[2] - data[3]) < 6: print( f"[INFO] Backward cart displacement test: {colored('[SUCCESS]', 'green')}" ) st.markdown( f"[INFO] Backward cart displacement test: <font color='green'>[SUCCESS]</font>", unsafe_allow_html=True) else: print( f"[WARNING] Backward cart displacement test: {colored('[FAILED]', 'red')}" ) st.markdown( f"[WARNING] Forward cart displacement test: <font color='red'>[FAILED]</font>", unsafe_allow_html=True) if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet}") leg = legacyWriter(legacy, sheet) leg.write_cart_report(data)
def unifomity_multislicetest(im_list, legacy=None, sheet=None, option="multislice"): print(f"[INFO] UNIFORMITY, NOISE AND CT NUMBER CHECK - MULTISLICE MODE") st.write(f"[INFO] UNIFORMITY, NOISE AND CT NUMBER CHECK - MULTISLICE MODE") """Handle mutlislice test""" if save_images: os.makedirs("cq_images", exist_ok=True) #im_list=glob.glob(os.path.join(path,"*.DCM")) results = [] for (i, image) in enumerate(im_list): #dcm_slice = pydicom.dcmread(image) img, pix_dim = get_data(image) #select the center roi res, lookup = select_roi(img, img.shape[0] // 2, img.shape[0] // 2, 20, pix_dim, save=save_images, outname="center") roi = mask_values(res, lookup) border_rois, images = make_border_rois(img, pix_dim, 30, 20) print( f"[INFO] Starting default test for quality control for image {i+1}/{len(im_list)}..." ) st.write( f"[INFO] Starting default test for quality control for image {i+1}/{len(im_list)}..." ) #LAVORARE QUA AGGIUNGERE UNA TABELLA O QUALCOSA PER TENERE CONTO DEI VALORI # 2.Noise noise, std = check_noise(roi) # 3. Water CT water, mean = check_waterct(roi) # 4. Uniformity uniformity, means = check_uniformity(roi, border_rois) border_std = [check_noise(br)[1] for br in border_rois] res_dic = { 'noise': std, 'water_ct': mean, 'uniformity_left': means[0], 'std_left': border_std[0], 'uniformity_up': means[1], 'std_up': border_std[1], 'uniformity_right': means[2], 'std_right': border_std[2], 'uniformity_bottom': means[3], 'std_bottom': border_std[3], 'noise_test': noise, 'water_ct_test': water, 'uniformity_test': uniformity } results.append(res_dic) print("\n\n") df = pd.DataFrame(results) print( f"[INFO] printing out results for noise, water and uniformity test: \n {df}" ) st.write( f"[INFO] printing out results for noise, water and uniformity test: ") st.write(df) if legacy is not None: #eventually add here option for monoenergetic if option == "multislice": print(f"[INFO] legacy mode: work on {legacy} in {sheet}") st.write(f"[INFO] legacy mode: work on {legacy} in {sheet}") leg = legacyWriter(legacy, sheet) leg.write_uniformity_multislice_report(df, option) elif option == "multislice_monoenergetic": print(f"[INFO] legacy mode: work on {legacy} in {sheet}") st.write(f"[INFO] legacy mode: work on {legacy} in {sheet}") leg = legacyWriter(legacy, sheet) leg.write_uniformity_multislice_report(df, option)
def test_contrast_resolution(dcm_img, legacy=None, sheet=None, filter="std", debug=True): #ris_img = pydicom.dcmread(path) # load image for linear spacing img, pix_dim = get_data(dcm_img) # rescale img print(f"[INFO] Normalizing image..") st.write(f"[INFO] Normalizing image..") gray = img.copy() # normalize img dst = np.zeros(gray.shape) # drop the background gray[gray < 0] = 0 cv2.normalize(gray, dst, 0, 255, cv2.NORM_MINMAX) print(f"[INFO] Looking for contours..") st.write(f"[INFO] Looking for contours..") dst = dst.astype("uint8") display = dst.copy() # to show at the end if filter == "bone": dst = cv2.GaussianBlur(dst, (5, 5), 1.2) ret, thresh = cv2.threshold(dst, 127, 255, cv2.THRESH_BINARY_INV) min_area = 100 elif filter == "std": dst = cv2.GaussianBlur(dst, (5, 5), 1.21) ret, thresh = cv2.threshold(dst, 150, 255, cv2.THRESH_BINARY_INV) min_area = 80 contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) areas = [cv2.contourArea(cnt) for cnt in contours] big_conts = [] for cnt, area in zip(contours, areas): if area > min_area: big_conts.append(cnt) # LAVORARE QUA SUI CONTORNI -> RENDERLI QUADRATI MAGARI RUOTATI, SCARTARE QUELLI ESTERNI comunque è buono print(f"[INFO] Generating miniminum enclosing rectangles..") st.write(f"[INFO] Generating miniminum enclosing rectangles..") minRect = [None] * len(big_conts) for i, c in enumerate(big_conts): minRect[i] = cv2.minAreaRect(c) squares, circle = clean_freq_rois(minRect, align=True) wc, sc = prepare_checkcontrast_rois(circle, img, pix_dim) #here do contrast test passed, p, w, std_p, std_w = check_contrast(wc, sc, img, pix_dim) ##HERE START THE RESOLUTION TEST #roi 6 is w, roi 7 is pmm print( f"[INFO] Proceeding with resolution test, found {len(squares)} boxes.." ) st.write( f"[INFO] Proceeding with resolution test, found {len(squares)} boxes.." ) if filter == "bone": sorted_squares = sorted(squares, key=lambda x: x[0][0], reverse=True)[:-1] elif filter == "std": center_squares = [] for i in range(len(squares)): if squares[i][0][0] > 150 and squares[i][0][0] < 350: #keep only the centered squares center_squares.append(squares[i]) sorted_squares = sorted(center_squares, key=lambda x: x[0][0], reverse=True) if len(sorted_squares) < 5: print( f"[DEBUG] -> can't find all squares via edge detection, use default list" ) st.markdown( f"<font color='orange'>[WARNING]</font> -> can't find all squares via edge detection, use default list", unsafe_allow_html=True) sorted_squares = baseline if debug: print(f"[DEBUG] showing squares ..") drawing = np.zeros((gray.shape[0], gray.shape[1], 3), dtype=np.uint8) for i, c in enumerate(sorted_squares): # contour # cv2.drawContours(display, squares, i, (255,255,255)) # ellipse # if c.shape[0] > 5: # cv2.ellipse(display, minEllipse[i],(255,255,255), 2) # rotated rectangle box = cv2.boxPoints(sorted_squares[i]) box = np.intp( box ) # np.intp: Integer used for indexing (same as C ssize_t; normally either int32 or int64) cv2.drawContours(display, [box], 0, (255, 255, 255)) cv2.circle(display, (wc[0], wc[1]), wc[2], (255, 255, 255), 1) cv2.circle(display, (sc[0], sc[1]), sc[2], (255, 255, 255), 1) fig, ax = plt.subplots() ax.imshow(display, cmap="gray") st.write(fig) #cv2.imshow('Spatial ROIs', display) #cv2.waitKey(0) boxes = mask_square(img, sorted_squares) if filter == "std": passed, means, stds, MTFS = check_resolution(boxes, p - w, min_value=34.1, max_value=41.7) elif filter == "bone": passed, means, stds, MTFS = check_resolution(boxes, p - w, min_value=46.9, max_value=57.3) if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet}") st.write(f"[INFO] legacy mode: work on {legacy} in {sheet}") leg = legacyWriter(legacy, sheet) leg.write_resolution_report(means, w, p, stds, std_w, std_p, filter)
def test_thickness_multislice(im_list, legacy, sheet, function): print(f"[INFO] THICKNESS TEST..") st.write(f"[INFO] THICKNESS TEST..") #images_list = glob.glob(os.path.join(path, "*.DCM")) #dcm_images = [pydicom.dcmread(image) for image in images_list] images = [get_data(image)[0] for image in im_list] start_x, start_y, w, h = (100, 100, 300, 300) cropped = [crop_img(img, start_x, start_y, w, h) for img in images] print(f"[INFO] preprocessing images to find the dot..") st.write(f"[INFO] preprocessing images to find the dot..") normalized = normalize(cropped) print(f"[INFO] threesholding images to find the dot..") st.write(f"[INFO] threesholding images to find the dot..") thresholded=[] for i in normalized: ret,thresh = cv2.threshold(i,120,255,cv2.THRESH_BINARY) thresholded.append(thresh) # check out the images that cointains the dot dots_img = [] for i in thresholded: if np.max(i) == 255: dots_img.append(i) print(f"[INFO] find the dot in all images, and use the biggest minRect as ROI for all images...") st.write(f"[INFO] find the dot in all images, and use the biggest minRect as ROI for all images...") conts = [] for thresh in dots_img: thresh = thresh.astype("uint8") contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) conts.append(contours) print(f"[INFO] Generating miniminum enclosing rectangles..") st.write(f"[INFO] Generating miniminum enclosing rectangles..") minRect = [None] * len(conts) for i, c in enumerate(conts): minRect[i] = cv2.boundingRect(c[0]) areas = [rect[2] * rect[3] for rect in minRect] print(f"[INFO] computing max area rect..") st.write(f"[INFO] computing max area rect..") rect_roi = minRect[np.argmax(areas)] areas = [rect[2] * rect[3] for rect in minRect] print(f"[INFO] computing max area rect..") st.write(f"[INFO] computing max area rect..") rect_roi = minRect[np.argmax(areas)] print(f"[INFO] transfer the roi...") st.write(f"[INFO] transfer the roi...") l = max(rect_roi[2], rect_roi[3]) if (l>10): l=10 or_x = rect_roi[0] or_y = rect_roi[1] rect_roi = (or_x, or_y, l, l) x = [] y = [] x_rect, y_rect, w_rect, h_rect = rect_roi cc = [] for i in range(len(im_list)): x.append(im_list[i].SliceLocation) cc.append(crop_img(cropped[i], y_rect, x_rect, w_rect, h_rect)) y.append(np.mean(cc[i])) data=pd.DataFrame() data["x"]=x data["y"]=y st.write(data) passed, fwhm, fig = check_thickness(x, y, reference_value=2.5, max_distance=0.2,function=function) st.write(fig) #finally complete with legacy if legacy is not None: if legacy is not None: print(f"[INFO] legacy mode: work on {legacy} in {sheet} \nPlease be careful: this should be the semester dose file..." ) st.write(f"[INFO] legacy mode: work on {legacy} in {sheet} \nPlease be careful: this should be the semester dose file...") leg = legacyWriter(legacy, sheet) leg.write_dot_thickness_report(fwhm)