|
import streamlit as st |
|
import os |
|
import zipfile |
|
from PIL import Image, ImageDraw, ImageFont |
|
from rembg import remove |
|
|
|
|
|
def draw_ruler(image, dpi=300, unit="in", color="white"): |
|
""" |
|
Draws a ruler on the top and left edges of an image. |
|
|
|
Args: |
|
image: The PIL Image object to draw on. |
|
dpi: The DPI of the image (default 300). |
|
unit: The unit of measurement (default "in"). |
|
color: The color of the ruler (default "black"). |
|
""" |
|
|
|
draw = ImageDraw.Draw(image) |
|
width, height = image.size |
|
font = ImageFont.load_default(size=100) |
|
|
|
|
|
for i in range(0, width, int(dpi)): |
|
x = i |
|
draw.line((x, 0, x, 10), fill=color) |
|
if i % int(dpi) == 0: |
|
draw.text((x, 12), str(i // dpi), fill=color, font=font) |
|
|
|
|
|
for i in range(0, height, int(dpi)): |
|
y = i |
|
draw.line((0, y, 10, y), fill=color) |
|
if i % int(dpi) == 0: |
|
if i != 0: |
|
draw.text((12, y), str(i // dpi), fill=color, font=font) |
|
|
|
draw.text((100, y-30), "inch", fill=color, font=font) |
|
|
|
return image |
|
|
|
def draw_ruler1(image, dpi=72, font_size=144): |
|
""" |
|
Draw a virtual ruler 10 cm from the bottom of the image. |
|
Args: |
|
image (PIL.Image.Image): The input image. |
|
dpi (int): The DPI (dots per inch) of the image. Default is 72. |
|
Returns: |
|
PIL.Image.Image: The image with the ruler drawn on it. |
|
""" |
|
dpi = image.info.get("dpi", (72, 72)) |
|
print("dpi:",image.info.get("dpi",(300,300))) |
|
dpi = dpi[0] if isinstance(dpi, tuple) else dpi |
|
|
|
draw = ImageDraw.Draw(image) |
|
|
|
|
|
|
|
|
|
|
|
font = ImageFont.load_default(size=100) |
|
|
|
print("font:",font) |
|
|
|
|
|
ruler_height = int(1 * dpi / 2.54) |
|
ruler_y_position = image.height - int(10 * dpi / 2.54) |
|
|
|
|
|
for i in range(0, image.width, int(10 * dpi / 2.54)): |
|
if i % int(dpi * 10 / 2.54) == 0: |
|
draw.line([(i, ruler_y_position), (i, ruler_y_position + ruler_height)], fill="black", width=2) |
|
draw.text((i + 2, ruler_y_position + 2), str(i // int(dpi / 2.54)), fill="black", font=font) |
|
|
|
|
|
else: |
|
draw.line([(i, ruler_y_position), (i, ruler_y_position + ruler_height // 2)], fill="black", width=1) |
|
|
|
return image |
|
|
|
def draw_ruler0(image, dpi=72): |
|
""" |
|
Draw a virtual ruler 1 cm from the bottom of the image. |
|
Args: |
|
image (PIL.Image.Image): The input image. |
|
dpi (int): The DPI (dots per inch) of the image. Default is 72. |
|
Returns: |
|
PIL.Image.Image: The image with the ruler drawn on it. |
|
""" |
|
|
|
dpi = image.info.get("dpi", (72, 72)) |
|
dpi = dpi[0] if isinstance(dpi, tuple) else dpi |
|
|
|
|
|
draw = ImageDraw.Draw(image) |
|
|
|
|
|
ruler_height = int(10 * dpi / 2.54) |
|
ruler_y_position = image.height - ruler_height |
|
|
|
print("ruler_y_position",ruler_y_position) |
|
print("image.height",image.height) |
|
|
|
for i in range(0, image.width, int(dpi / 2.54)): |
|
if i % int(dpi * 10 / 2.54) == 0: |
|
draw.line([(i, ruler_y_position), (i, image.height)], fill="black", width=5) |
|
draw.text((i + 2, ruler_y_position + 2), str(i // int(dpi / 2.54)), fill="black") |
|
else: |
|
draw.line([(i, ruler_y_position), (i, ruler_y_position + ruler_height // 2)], fill="black", width=5) |
|
|
|
return image |
|
|
|
|
|
|
|
|
|
|
|
|
|
def crop_image(image, left_mm, right_mm, top_mm, bottom_mm): |
|
""" |
|
Crop the image from the left, right, top, and bottom sides. |
|
Args: |
|
image (PIL.Image.Image): The input image. |
|
left_mm (float): The amount to crop from the left side in millimeters. |
|
right_mm (float): The amount to crop from the right side in millimeters. |
|
top_mm (float): The amount to crop from the top side in millimeters. |
|
bottom_mm (float): The amount to crop from the bottom side in millimeters. |
|
Returns: |
|
PIL.Image.Image: The cropped image. |
|
""" |
|
|
|
dpi = image.info.get("dpi", (72, 72)) |
|
dpi = dpi[0] if isinstance(dpi, tuple) else dpi |
|
|
|
|
|
width_mm = image.width * 25.4 / dpi |
|
height_mm = image.height * 25.4 / dpi |
|
|
|
left_crop = int(left_mm * image.width / width_mm) |
|
right_crop = image.width - int(right_mm * image.width / width_mm) |
|
top_crop = int(top_mm * image.height / height_mm) |
|
bottom_crop = image.height - int(bottom_mm * image.height / height_mm) |
|
|
|
|
|
cropped_image = image.crop((left_crop, top_crop, right_crop, bottom_crop)) |
|
|
|
return cropped_image |
|
|
|
def crop_image1(image, left_mm, right_mm): |
|
""" |
|
Crop the image from both the left and right sides. |
|
|
|
Args: |
|
image (PIL.Image.Image): The input image. |
|
left_mm (float): The amount to crop from the left side in millimeters. |
|
right_mm (float): The amount to crop from the right side in millimeters. |
|
|
|
Returns: |
|
PIL.Image.Image: The cropped image. |
|
""" |
|
|
|
dpi = image.info.get("dpi", (72, 72)) |
|
dpi = dpi[0] if isinstance(dpi, tuple) else dpi |
|
|
|
|
|
width_mm = image.width * 25.4 / dpi |
|
cropping_width = int((left_mm + right_mm) * image.width / width_mm) |
|
|
|
|
|
left_crop = int(left_mm * image.width / width_mm) |
|
right_crop = image.width - int(right_mm * image.width / width_mm) |
|
|
|
|
|
cropped_image = image.crop((left_crop, left_crop, right_crop, image.height)) |
|
|
|
return cropped_image |
|
|
|
|
|
|
|
def main(): |
|
st.title("Batch Image Cropper") |
|
|
|
|
|
uploaded_files = st.file_uploader("Upload image files", type=["jpg", "jpeg", "png"], accept_multiple_files=True) |
|
|
|
if uploaded_files is not None: |
|
col1, col2, col3, col4, col5 = st.columns(5) |
|
|
|
with col1: |
|
left_mm = st.number_input("Crop left (mm)", min_value=0.0, value=0.0, step=0.1) |
|
with col2: |
|
right_mm = st.number_input("Crop right (mm)", min_value=0.0, value=0.0, step=0.1) |
|
with col3: |
|
top_mm = st.number_input("Crop top (mm)", min_value=0.0, value=0.0, step=0.1) |
|
with col4: |
|
bottom_mm = st.number_input("Crop bottom (mm)", min_value=0.0, value=0.0, step=0.1) |
|
with col5: |
|
ruler_chk = st.checkbox("Ruler?") |
|
|
|
|
|
images_container = st.empty() |
|
|
|
|
|
cropped_images = [] |
|
for uploaded_file in uploaded_files: |
|
|
|
image = Image.open(uploaded_file) |
|
|
|
|
|
cropped_image = crop_image(image, left_mm, right_mm, top_mm, bottom_mm) |
|
|
|
cropped_image=remove(cropped_image) |
|
|
|
if ruler_chk: |
|
cropped_image= draw_ruler(cropped_image) |
|
cropped_images.append(cropped_image) |
|
|
|
|
|
col_width = 300 |
|
num_images = len(uploaded_files) |
|
num_columns = 2 |
|
num_rows = (num_images + 1) // num_columns |
|
for row in range(num_rows): |
|
cols = st.columns(num_columns) |
|
for col in range(num_columns): |
|
index = row * num_columns + col |
|
if index < num_images: |
|
cols[col].subheader(f"Image {index+1}") |
|
cols[col].image(uploaded_files[index], caption="Original Image", width=col_width) |
|
cols[col].image(cropped_images[index], caption="Cropped Image", width=col_width) |
|
|
|
|
|
if st.button("Save All"): |
|
zip_filename = "cropped_images.zip" |
|
with zipfile.ZipFile(zip_filename, "w") as zipf: |
|
for i, cropped_image in enumerate(cropped_images): |
|
filename, ext = os.path.splitext(uploaded_files[i].name) |
|
cropped_filename = f"cropped_{filename}.png" |
|
cropped_image.save(cropped_filename) |
|
zipf.write(cropped_filename) |
|
os.remove(cropped_filename) |
|
st.success(f"All cropped images saved successfully as {zip_filename}") |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |
|
|