Spaces:
Sleeping
Sleeping
File size: 6,036 Bytes
6f7e88c 6ee26b7 6f7e88c 511a575 6f7e88c ccef6d5 6f7e88c ccef6d5 6f7e88c 3d340b6 6f7e88c 45e8490 6f7e88c 45e8490 6f7e88c 45e8490 6f7e88c 45e8490 3d340b6 45e8490 3d340b6 be66cb9 3d340b6 db18e99 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | import streamlit as st
from PIL import Image
import numpy as np
import cv2
from huggingface_hub import from_pretrained_keras
import pandas as pd
st.header("Tooth detection and segmentation in panoramic X-Rays")
st.subheader("Iteration to improve demo")
st.markdown(
"""
Demo for testing image segmentation CNN model
"""
)
st.markdown(
"""
### Technical overview
* **Architecture:** It utilizes the U-Net architecture, a popular "encoder-decoder" convolutional neural network (CNN) specifically optimized for biomedical image segmentation where pixel-level accuracy is critical.
* **Performance:** In the accompanying research, the model achieved a Dice overlap score of 95.4% for overall teeth segmentation.
* **Post-processing:** A key highlight of this specific implementation is the use of grayscale morphological filtering and operations applied to the sigmoid output. This reduces tooth counting errors significantly (from 26.8% down to roughly 6.2%).
* **Dataset:** The model was trained on a relatively small but highly curated dataset (approximately 105 to 116 panoramic images) based on work by Abdi et al. (2015).
### Key applications
* **Clinical diagnosis:** Assists dentists in identifying the boundaries of individual teeth to detect caries, lesions, or bone loss.
* **Forensics and identification:** Automates the process of identifying dental patterns for human remains or age/gender determination.
* **Treatment planning:** Provides a baseline for orthodontic therapy workups by isolating dental structures from the surrounding mandible and maxilla.
"""
)
## Select and load the model
model_id = "SerdarHelli/Segmentation-of-Teeth-in-Panoramic-X-ray-Image-Using-U-Net"
model = from_pretrained_keras(model_id)
## Allow the user to upload an image
archivo_imagen = st.file_uploader("Upload your image here.", type=["png", "jpg", "jpeg"])
## If an image has more than one channel, it is converted to grayscale (1 channel)
def convertir_one_channel(img):
if len(img.shape) > 2:
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return img
else:
return img
def convertir_rgb(img):
if len(img.shape) == 2:
img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
return img
else:
return img
## We will manipulate the interface so we can use example images
## If the user clicks on an example, the model will run with the following:
ejemplos = ["dientes_1.png", "dientes_2.png", "dientes_3.png"]
## Create three columns; an example image will be in each one
col1, col2, col3 = st.columns(3)
with col1:
## The image is loaded and displayed in the interface
ex = Image.open(ejemplos[0])
st.image(ex, width=200)
## If the button is pressed, we will use this example in the model
if st.button("Run this example 1"):
archivo_imagen = ejemplos[0]
with col2:
ex1 = Image.open(ejemplos[1])
st.image(ex1, width=200)
if st.button("Run this example 2"):
archivo_imagen = ejemplos[1]
with col3:
ex2 = Image.open(ejemplos[2])
st.image(ex2, width=200)
if st.button("Run this example 3"):
archivo_imagen = ejemplos[2]
## If we have an image to input into the model,
## we process it and feed it to the model
if archivo_imagen is not None:
## Load the image with PIL and display it immediately
img = Image.open(archivo_imagen)
st.image(img, width=850)
## Trigger the loading spinner for the heavy lifting
with st.spinner('Analyzing panoramic X-ray. This may take a few seconds...'):
img = np.array(img) #Creates a writable copy
## Process the image for model input
img_cv = convertir_one_channel(img)
img_cv = cv2.resize(img_cv, (512, 512), interpolation=cv2.INTER_LANCZOS4)
img_cv = np.float32(img_cv / 255)
img_cv = np.reshape(img_cv, (1, 512, 512, 1))
## Feed the NumPy array into the model
predicted = model.predict(img_cv)
predicted = predicted[0]
## Resize the image back to its original shape and add the segmentation masks
predicted = cv2.resize(
predicted, (img.shape[1], img.shape[0]), interpolation=cv2.INTER_LANCZOS4)
mask = np.uint8(predicted * 255)
_, mask = cv2.threshold(
mask, thresh=0, maxval=255, type=cv2.THRESH_BINARY + cv2.THRESH_OTSU
)
kernel = np.ones((5, 5), dtype=np.float32)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=1)
cnts, hieararch = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
output = cv2.drawContours(convertir_one_channel(img), cnts, -1, (255, 0, 0), 3)
## If we successfully obtained a result, display it in the interface
if output is not None:
st.subheader("Image segmentation:")
st.write(output.shape)
st.image(output, width=850)
st.subheader("Diagnostic metrics overview")
conteo_dientes = len(cnts)
area_total = np.sum(mask > 0)
area_promedio = area_total / conteo_dientes if conteo_dientes > 0 else 0
col_m1, col_m2, col_m3 = st.columns(3)
col_m1.metric("Estimated tooth count", conteo_dientes)
col_m2.metric("Total dental area (px)", f"{area_total:,}")
col_m3.metric("Average tooth area (px)", f"{int(area_promedio):,}")
st.markdown("### Detected instances data")
datos_dientes = []
for i, c in enumerate(cnts):
area = cv2.contourArea(c)
x, y, w, h = cv2.boundingRect(c)
datos_dientes.append({
"ID": i + 1,
"Area (px)": area,
"Width (px)": w,
"Height (px)": h
})
df_dientes = pd.DataFrame(datos_dientes)
st.dataframe(df_dientes, use_container_width=True)
|