Spaces:
Sleeping
Sleeping
''' | |
This is the originall CLL Explorer application that allows users to upload, process, and save images. | |
The application provides the following functionalities: | |
- Upload microscope images. | |
- Adjust image view with zoom and enhancement controls. | |
- Detect and measure cells automatically. | |
- Save analysis results and annotations. | |
The application is divided into the following sections: | |
1. **Upload Images**: Users can upload microscope images in JPG or PNG format. | |
2. **Select Image**: Users can select an image from the uploaded files. | |
3. **Processed Image**: Displays the processed image with zoom and enhancement controls. | |
4. **Image Controls**: Allows users to adjust the image view with sliders for X and Y coordinates, zoom, contrast, brightness, and sharpness. | |
5. **Save Options**: Provides options to save the processed image, image description, and image parameters. | |
To run the application: | |
1. Save the script in a Python file (e.g., app.py). | |
2. Run the script using the Streamlit command: | |
```bash | |
streamlit run app.py | |
''' | |
import streamlit as st | |
from PIL import Image, ImageEnhance | |
import pandas as pd | |
import numpy as np | |
import io | |
import os | |
import tempfile | |
import zipfile | |
import cv2 | |
import numpy as np | |
def zoom_at(img, x, y, zoom): | |
''' | |
Zoom into an image at a specific location. | |
Parameters: | |
---------- | |
img : PIL.Image | |
Input image. | |
x : int | |
X-coordinate of the zoom center. | |
y : int | |
Y-coordinate of the zoom center. | |
zoom : float | |
Zoom factor. | |
Returns: | |
------- | |
PIL.Image | |
Zoomed image resized to 500x500 pixels. | |
''' | |
w, h = img.size | |
zoom_half = zoom / 2 | |
left = max(x - w * zoom_half, 0) | |
upper = max(y - h * zoom_half, 0) | |
right = min(x + w * zoom_half, w) | |
lower = min(y + h * zoom_half, h) | |
img_cropped = img.crop((left, upper, right, lower)) | |
return img_cropped.resize((500, 500), Image.LANCZOS) | |
def apply_enhancements(img, x, y, zoom, contrast, brightness, sharpness): | |
''' | |
Apply zoom and image enhancements to the input image. | |
Parameters: | |
---------- | |
img : PIL.Image | |
Input image. | |
x : int | |
X-coordinate of the zoom center. | |
y : int | |
Y-coordinate of the zoom center. | |
zoom : float | |
Zoom factor. | |
contrast : float | |
Contrast adjustment factor. | |
brightness : float | |
Brightness adjustment factor. | |
sharpness : float | |
Sharpness adjustment factor. | |
Returns: | |
------- | |
PIL.Image | |
Enhanced image resized to 500x500 pixels. | |
''' | |
zoomed = zoom_at(img, x, y, zoom) | |
enhanced_contrast = ImageEnhance.Contrast(zoomed).enhance(contrast) | |
enhanced_brightness = ImageEnhance.Brightness(enhanced_contrast).enhance(brightness) | |
enhanced_sharpness = ImageEnhance.Sharpness(enhanced_brightness).enhance(sharpness) | |
return enhanced_sharpness | |
def apply_enhancements_cv(img, x, y, zoom, contrast, brightness, sharpness): | |
""" | |
Use OpenCV for zoom and enhancements. | |
""" | |
# Convert PIL to OpenCV format | |
img_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) | |
h, w = img_cv.shape[:2] | |
# Zoom | |
zoom_half = int(zoom / 2) | |
left = max(x - w * zoom_half, 0) | |
top = max(y - h * zoom_half, 0) | |
right = min(x + w * zoom_half, w) | |
bottom = min(y + h * zoom_half, h) | |
cropped = img_cv[int(top):int(bottom), int(left):int(right)] | |
resized = cv2.resize(cropped, (500, 500), interpolation=cv2.INTER_LANCZOS4) | |
# Convert back to PIL for other enhancements | |
pil_img = Image.fromarray(cv2.cvtColor(resized, cv2.COLOR_BGR2RGB)) | |
enhanced_contrast = ImageEnhance.Contrast(pil_img).enhance(contrast) | |
enhanced_brightness = ImageEnhance.Brightness(enhanced_contrast).enhance(brightness) | |
enhanced_sharpness = ImageEnhance.Sharpness(enhanced_brightness).enhance(sharpness) | |
return enhanced_sharpness | |
def create_zip(processed_img, description, params): | |
''' | |
Create a zip archive containing the processed image and annotations. | |
Parameters: | |
---------- | |
processed_img : PIL.Image | |
The processed image. | |
description : str | |
Description of the image. | |
params : dict | |
Image parameters. | |
Returns: | |
------- | |
bytes | |
Byte content of the zip file. | |
''' | |
with tempfile.TemporaryDirectory() as tmpdirname: | |
img_path = os.path.join(tmpdirname, "processed_image.jpg") | |
desc_path = os.path.join(tmpdirname, "description.txt") | |
params_path = os.path.join(tmpdirname, "parameters.json") | |
# Save processed image | |
processed_img.save(img_path) | |
# Save description | |
with open(desc_path, "w") as f: | |
f.write(description) | |
# Save parameters | |
pd.DataFrame([params]).to_json(params_path, orient="records") | |
# Create zip | |
zip_buffer = io.BytesIO() | |
with zipfile.ZipFile(zip_buffer, "w") as zipf: | |
zipf.write(img_path, arcname="processed_image.jpg") | |
zipf.write(desc_path, arcname="description.txt") | |
zipf.write(params_path, arcname="parameters.json") | |
zip_buffer.seek(0) | |
return zip_buffer | |
# Streamlit App Configuration | |
st.set_page_config(page_title="CLL Explorer", layout="wide") | |
st.title("CLL Explorer: Cell Image Analysis Prep Tool") | |
st.markdown(""" | |
### About This Application | |
This tool assists researchers in analyzing microscope images of any cell type. | |
- **Upload** microscope images. | |
- **Adjust** image view with zoom and enhancement controls. | |
- **Detect** and measure cells automatically. | |
- **Save** analysis results and annotations. | |
""") | |
uploaded_files = st.file_uploader("Upload Images", accept_multiple_files=True, type=["jpg", "png"]) | |
if uploaded_files: | |
img_index = st.selectbox( | |
"Select Image", | |
range(len(uploaded_files)), | |
format_func=lambda x: uploaded_files[x].name | |
) | |
img_data = uploaded_files[img_index].read() | |
img = Image.open(io.BytesIO(img_data)).convert("RGB").resize((500, 500)) | |
# Create columns with image on the left and controls on the right | |
image_col, controls_col = st.columns([3, 1]) | |
with image_col: | |
st.subheader("Processed Image") | |
if 'processed_img' in st.session_state: | |
st.image(st.session_state.processed_img, use_column_width=True, caption="Processed Image") | |
else: | |
st.image(img, use_column_width=True, caption="Processed Image") | |
with controls_col: | |
st.subheader("Image Controls") | |
x = st.slider("X Coordinate", 0, 500, 250) | |
y = st.slider("Y Coordinate", 0, 500, 250) | |
zoom = st.slider("Zoom", 1.0, 10.0, 5.0, step=0.1) | |
with st.expander("Enhancement Settings", expanded=True): | |
contrast = st.slider("Contrast", 0.0, 5.0, 1.0, step=0.1) | |
brightness = st.slider("Brightness", 0.0, 5.0, 1.0, step=0.1) | |
sharpness = st.slider("Sharpness", 0.0, 2.0, 1.0, step=0.1) | |
if st.button("Apply Adjustments"): | |
processed_img = apply_enhancements(img, x, y, zoom, contrast, brightness, sharpness) | |
st.session_state.processed_img = processed_img | |
# Display Original Image Below | |
st.subheader("Original Image") | |
st.image(img, use_column_width=True, caption="Original Image") | |
# Save and Export Options | |
st.markdown("---") | |
st.subheader("Save and Export Options") | |
with st.expander("Add Annotations", expanded=True): | |
description = st.text_area("Describe the image", "") | |
params = { | |
"coordinates_x": x, | |
"coordinates_y": y, | |
"zoom": zoom, | |
"contrast": contrast, | |
"brightness": brightness, | |
"sharpness": sharpness | |
} | |
if st.button("Prepare Download"): | |
if 'processed_img' in st.session_state and description: | |
zip_buffer = create_zip(st.session_state.processed_img, description, params) | |
st.download_button( | |
label="Download Zip", | |
data=zip_buffer, | |
file_name="processed_image_and_annotations.zip", | |
mime="application/zip" | |
) | |
st.success("Zip file is ready for download.") | |
else: | |
st.warning("Ensure that the processed image is available and description is provided.") | |
# Optional: Save Processed Image Locally | |
save_image = st.checkbox("Save Processed Image Locally") | |
if save_image: | |
if 'processed_img' in st.session_state: | |
processed_img_path = os.path.join("processed_image_500x500.jpg") | |
st.session_state.processed_img.save(processed_img_path) | |
st.success(f"Image saved as `{processed_img_path}`") | |
else: | |
st.warning("No processed image to save.") | |
# Optional: Rename Files | |
if st.button("Rename Files"): | |
if 'processed_img' in st.session_state: | |
file_ext = str(np.random.randint(100)) | |
new_img_name = f"img_processed_{file_ext}.jpg" | |
processed_img_path = "processed_image_500x500.jpg" | |
if os.path.exists(processed_img_path): | |
os.rename(processed_img_path, new_img_name) | |
# Save parameters and description | |
params_path = f"parameters_{file_ext}.json" | |
description_path = f"description_{file_ext}.txt" | |
pd.DataFrame([params]).to_json(params_path, orient="records") | |
with open(description_path, "w") as f: | |
f.write(description) | |
st.success(f"Files renamed to `{new_img_name}`, `{params_path}`, and `{description_path}`") | |
else: | |
st.warning("No processed image to rename.") |