crop-it / app.py
laitkor's picture
Update app.py
2f6e1d1 verified
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)
# Draw top ruler
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)
# Draw left ruler
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
# Create a drawing context
draw = ImageDraw.Draw(image)
# Load a font
#try:
#font = ImageFont.truetype("arial.ttf", font_size)
#except IOError:
font = ImageFont.load_default(size=100)
print("font:",font)
# Define the ruler dimensions
ruler_height = int(1 * dpi / 2.54) # 1 cm in pixels
ruler_y_position = image.height - int(10 * dpi / 2.54) # 10 cm from the bottom
# Draw horizontal ruler
for i in range(0, image.width, int(10 * dpi / 2.54)): # 10 cm intervals
if i % int(dpi * 10 / 2.54) == 0: # 10 cm intervals
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)
#draw.text((i + 2, ruler_y_position + 2), "8", 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.
"""
# Get the DPI value as a single number
dpi = image.info.get("dpi", (72, 72))
dpi = dpi[0] if isinstance(dpi, tuple) else dpi
# Create a drawing context
draw = ImageDraw.Draw(image)
# Define the ruler dimensions
ruler_height = int(10 * dpi / 2.54) # 10 cm in pixels
ruler_y_position = image.height - ruler_height # 10 cm from the bottom
print("ruler_y_position",ruler_y_position)
print("image.height",image.height)
# Draw horizontal ruler
for i in range(0, image.width, int(dpi / 2.54)): # 1 cm intervals
if i % int(dpi * 10 / 2.54) == 0: # 10 cm intervals
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
# Example usage
#image = Image.open("path_to_your_image.jpg")
#image_with_ruler = draw_ruler(image)
#image_with_ruler.show()
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.
"""
# Get the DPI value as a single number
dpi = image.info.get("dpi", (72, 72))
dpi = dpi[0] if isinstance(dpi, tuple) else dpi
# Calculate the cropping width and height in pixels based on image 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)
# Crop the image
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.
"""
# Get the DPI value as a single number
dpi = image.info.get("dpi", (72, 72))
dpi = dpi[0] if isinstance(dpi, tuple) else dpi
# Calculate the cropping width in pixels based on image DPI
width_mm = image.width * 25.4 / dpi
cropping_width = int((left_mm + right_mm) * image.width / width_mm)
# Calculate the left and right cropping boundaries
left_crop = int(left_mm * image.width / width_mm)
right_crop = image.width - int(right_mm * image.width / width_mm)
# Crop the image
cropped_image = image.crop((left_crop, left_crop, right_crop, image.height))
return cropped_image
def main():
st.title("Batch Image Cropper")
# Upload multiple image files
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?")
# Create a container to hold the cropped images
images_container = st.empty()
# Process each uploaded image file
cropped_images = []
for uploaded_file in uploaded_files:
# Open the image file
image = Image.open(uploaded_file)
# Crop the image
cropped_image = crop_image(image, left_mm, right_mm, top_mm, bottom_mm)
#remove background
cropped_image=remove(cropped_image)
#do we draw a ruler
if ruler_chk:
cropped_image= draw_ruler(cropped_image)
cropped_images.append(cropped_image)
# Display the original and cropped images side by side
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)
# Save all cropped images
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()