예제 #1
0
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)
예제 #3
0
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)
예제 #10
0
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)