"""Dataset E viewer — browse images with labels and filters."""
import os, io, math, pyarrow.parquet as pq
from flask import Flask, send_file, request, render_template_string
from PIL import Image
from collections import Counter
ROOT = "/home/kccitadmin/jupyterlab/sandbox/data/E"
app = Flask(__name__)
# Load parquet once
table = pq.read_table(os.path.join(ROOT, "data", "data-00000-of-00001.parquet"))
N = table.num_rows
DATA = {
"cancer": table.column("cancer_label").to_pylist(),
"grade": table.column("grade_label").to_pylist(),
"subclass": table.column("subclass_label").to_pylist(),
"source": table.column("source_dataset").to_pylist(),
"filename": table.column("original_filename").to_pylist(),
"pid": table.column("patient_id").to_pylist(),
"imaging": table.column("imaging_type").to_pylist(),
"img_paths": [v["path"] for v in table.column("image").to_pylist()],
}
LABELS = {
"cancer": {0: "non_cancer", 1: "cancer"},
"grade": {0: "low_grade", 1: "high_grade", 2: "not_applicable"},
"subclass": {0: "malignant", 1: "non_malignant", 2: "normal", 3: "landmark", 4: "foreign_body"},
"imaging": {0: "WLI", 1: "NBI", 2: "BLC"},
}
HTML = """
Dataset E Viewer
Dataset E Viewer
Total: {{ total }}
Showing: {{ filtered_count }}
Page: {{ page }}/{{ pages }}
Cancer: {{ stats.cancer }} | Non-cancer: {{ stats.non_cancer }}
WLI: {{ stats.wli }} | NBI: {{ stats.nbi }} | BLC: {{ stats.blc }}
{% for i in indices %}
{{ items[loop.index0].source }} pid={{ items[loop.index0].pid }}
{{ items[loop.index0].cancer_label }}
{{ items[loop.index0].grade_label }}
{{ items[loop.index0].subclass_label }}
{{ items[loop.index0].imaging_label }}
{{ items[loop.index0].filename }}
{% endfor %}
×
"""
PER_PAGE = 48
@app.route("/")
def index():
# Parse filters
f = {
"source": request.args.get("source", ""),
"cancer": request.args.get("cancer", ""),
"grade": request.args.get("grade", ""),
"subclass": request.args.get("subclass", ""),
"imaging": request.args.get("imaging", ""),
"pid": request.args.get("pid", ""),
}
page = max(1, int(request.args.get("page", 1)))
# Filter indices
indices = list(range(N))
if f["source"]:
indices = [i for i in indices if DATA["source"][i] == f["source"]]
if f["cancer"] != "":
indices = [i for i in indices if DATA["cancer"][i] == int(f["cancer"])]
if f["grade"] != "":
indices = [i for i in indices if DATA["grade"][i] == int(f["grade"])]
if f["subclass"] != "":
indices = [i for i in indices if DATA["subclass"][i] == int(f["subclass"])]
if f["imaging"] != "":
indices = [i for i in indices if DATA["imaging"][i] == int(f["imaging"])]
if f["pid"]:
indices = [i for i in indices if str(DATA["pid"][i]) == f["pid"]]
filtered_count = len(indices)
pages = max(1, math.ceil(filtered_count / PER_PAGE))
page = min(page, pages)
start = (page - 1) * PER_PAGE
page_indices = indices[start:start + PER_PAGE]
# Build item data for template
items = []
for i in page_indices:
items.append({
"source": DATA["source"][i],
"pid": DATA["pid"][i],
"cancer": DATA["cancer"][i],
"cancer_label": LABELS["cancer"][DATA["cancer"][i]],
"grade": DATA["grade"][i],
"grade_label": LABELS["grade"][DATA["grade"][i]],
"subclass": DATA["subclass"][i],
"subclass_label": LABELS["subclass"][DATA["subclass"][i]],
"imaging": DATA["imaging"][i],
"imaging_label": LABELS["imaging"][DATA["imaging"][i]],
"filename": DATA["filename"][i],
})
# Build all_data for JS modal (all filtered indices on this page)
all_data = {}
for idx, i in enumerate(page_indices):
all_data[str(i)] = {
"index": str(i),
"source": DATA["source"][i],
"patient_id": str(DATA["pid"][i]),
"cancer_label": LABELS["cancer"][DATA["cancer"][i]],
"grade_label": LABELS["grade"][DATA["grade"][i]],
"subclass_label": LABELS["subclass"][DATA["subclass"][i]],
"imaging_type": LABELS["imaging"][DATA["imaging"][i]],
"original_filename": DATA["filename"][i],
"image_path": DATA["img_paths"][i].split("data/E/")[-1],
}
# Stats for filtered set
fc = Counter(DATA["cancer"][i] for i in indices)
fi = Counter(DATA["imaging"][i] for i in indices)
# Query string for pagination links
qs_parts = []
for k, v in f.items():
if v:
qs_parts.append(f"{k}={v}")
qs = "&".join(qs_parts)
return render_template_string(HTML,
total=N, filtered_count=filtered_count, page=page, pages=pages,
indices=page_indices, items=items, all_data=all_data,
sources=["B", "C", "D"], filters=f,
stats={"cancer": fc[1], "non_cancer": fc[0], "wli": fi[0], "nbi": fi[1], "blc": fi[2]},
qs=qs)
@app.route("/img//")
def serve_img(i, size):
path = DATA["img_paths"][i]
img = Image.open(path)
if img.mode in ("RGBA", "P"):
img = img.convert("RGB")
if size == "thumb":
img.thumbnail((300, 300))
else:
img.thumbnail((1000, 1000))
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=85)
buf.seek(0)
return send_file(buf, mimetype="image/jpeg")
if __name__ == "__main__":
print(f"Dataset E viewer: {N} images loaded")
print("Starting server at http://localhost:5173")
app.run(host="0.0.0.0", port=5173, debug=False)