product-image / app.py
laitkor's picture
Update app.py
4f80f3b verified
import numpy as np
import torch
import torch.nn.functional as F
from torchvision.transforms.functional import normalize
import gradio as gr
from gradio_imageslider import ImageSlider
from briarmbg import BriaRMBG
import PIL
from PIL import Image, ImageDraw, ImageFont
from typing import Tuple
net = BriaRMBG.from_pretrained("briaai/RMBG-1.4")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
net.to(device)
def draw_ruler(image, actual_height_in_inches = 13, dpi=300, unit="in", color="black"):
# Load the plant image in its original size
canvas = image
plant_width, plant_height = canvas.size
#actual_height_in_inches = 13 #only do the changes in this and generate the images ,,,,,,
#think about approximate height of plant suppose it is 8.3 inch so mention here 9 .and run the code
# Calculate pixels per inch based on the plant image's pixel height
pixels_per_inch = plant_height / actual_height_in_inches
# Create a blank canvas larger than the plant image to accommodate rulers
canvas_width = plant_width #+ 150 # 50 pixels extra for the left ruler
canvas_height = plant_height #+ 150 # 50 pixels extra for the bottom ruler
#canvas = Image.new('RGB', (canvas_width, canvas_height), 'white')
draw = ImageDraw.Draw(canvas)
font = ImageFont.load_default(size=60)
# for drawing scale value on vertical side ................ 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5...................so on
for i in range(0, int(plant_height / (pixels_per_inch * 0.5)) + 1):
# Calculate position and inch value
#y_position = canvas_height - 150 - int(i * pixels_per_inch * 0.5)
y_position = canvas_height - 0 - int(i * pixels_per_inch * 0.5)
inch_value = i * 0.5
# Draw tick mark and add text label every 1 inch for better readability
draw.line((0, y_position, 10, y_position), fill='black')
if inch_value.is_integer() and inch_value % 5 == 0: # Display labels only at whole numbers
draw.text((15, y_position - 5), f"{int(inch_value)} in", fill='black',font=font)
#else:
# draw.text((15, y_position - 5), f"{inch_value:.1f} in", fill='black',font=font)
# for drawing scale value on horizontal side ................ 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5...................so on
for i in range(0, int(plant_width / (pixels_per_inch * 0.5)) + 1):
# Calculate position and inch value
#x_position = 150 + int(i * pixels_per_inch * 0.5)
x_position = 0 + int(i * pixels_per_inch * 0.5)
inch_value = i * 0.5
# Draw tick mark and add text label every 1 inch for better readability
draw.line((x_position, canvas_height - 40, x_position, canvas_height), fill='black')
if inch_value.is_integer() and inch_value % 5 == 0: # Display labels only at whole numbers
draw.text((x_position, canvas_height - 65), f"{int(inch_value)} in", fill='black',font=font)
#else:
#draw.text((x_position, canvas_height - 25), f"{inch_value:.1f} in", fill='black',font=font)
# Position to paste the plant image on the canvas (leaving space for rulers)
#plant_position = (150, canvas_height - plant_height - 150)
# Paste the original plant image onto the canvas without resizing
#canvas.paste(plant_image, plant_position)
# Save the final image with the plant and rulers
#canvas.save('image_with_plant_and_rulers_high_quality_ONE.png')
#print("The image with the plant and rulers has been saved at original quality.")
return canvas;
def draw_ruler0(image, dpi=300, unit="in", color="black"):
# Define the pot dimensions and distance from camera
pot_diameter_inches = 4
pot_height_inches = 6
distance_from_camera_feet = 0.5
zoom_level = 1 # Zoom level
# Convert distance from feet to inches
distance_from_camera_inches = distance_from_camera_feet * 12
# Calculate the scale factor (assuming a simple pinhole camera model)
# Scale factor = (actual height of the object) / (height of the object in the image)
# Adjust for zoom level
scale_factor = (pot_height_inches / distance_from_camera_inches) * zoom_level
print ("scale factor:",scale_factor)
# Draw the scale on the image
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
# Define the position and size of the scale
scale_length_pixels = int(pot_height_inches / scale_factor)
scale_position = (50, 50) # Adjust as needed
# Draw the scale line
draw.line((scale_position[0], scale_position[1], scale_position[0], scale_position[1] + scale_length_pixels), fill="red", width=5)
print("line:",(scale_position[0], scale_position[1], scale_position[0], scale_position[1] + scale_length_pixels))
# Add text label for the scale
draw.text((scale_position[0] + 10, scale_position[1] + scale_length_pixels // 2), f"{pot_height_inches} inches", fill="red", font=font)
return image
def draw_ruler1(image, dpi=300, unit="in", color="black"):
"""
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").
"""
#dpi = image.info.get("dpi", (72, 72))
#dpi = dpi[0] if isinstance(dpi, tuple) else dpi
# Define the pot dimensions and distance from camera
pot_diameter_inches = 4
pot_height_inches = 6
distance_from_camera_feet = 3
zoom_level = 0.5 # Zoom level
# Convert distance from feet to inches
distance_from_camera_inches = distance_from_camera_feet * 12
# Calculate the scale factor (assuming a simple pinhole camera model)
# Scale factor = (actual height of the object) / (height of the object in the image)
# Adjust for zoom level
scale_factor = (pot_height_inches / distance_from_camera_inches) * zoom_level
scale_length_pixels = int(pot_height_inches / scale_factor)
print ("scale factor:",scale_factor,"scale_length_pixels:",scale_length_pixels)
draw = ImageDraw.Draw(image)
width, height = image.size
font = ImageFont.load_default(size=60)
# Draw top ruler
#for i in range(0, width, int(dpi)):
for i in range(0, width, scale_length_pixels):
x = i
draw.line((x, 0, x, 10), fill=color)
if i % int(scale_length_pixels) == 0:
if scale_length_pixels< 70 :
draw.text((x, 12), str(i // scale_length_pixels), fill=color, font=font)
if scale_length_pixels > 70 and i % 5 == 0:
draw.text((x, 12), str(i // scale_length_pixels), 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-60), "inch", fill=color, font=font)
return image
def mm_to_pixels(mm, dpi):
if dpi is None:
dpi = 72 # Default DPI value
if mm is None:
mm = 0 # Default mm value
return int(mm * dpi / 25.4)
def crop_image(image, left_mm, top_mm, right_mm, bottom_mm):
# Open the image file
#img = Image.open(image)
img = image
print("img.width:",img.width,"img.height:",img.height)
# Get the image DPI (dots per inch)
#dpi = img.info.get('dpi', (72, 72))[0] # Default to 72 DPI if not available
# Get the DPI value as a single number
dpi = img.info.get("dpi", (72, 72))
dpi = dpi[0] if isinstance(dpi, tuple) else dpi
print ("dpi:",dpi)
# Convert mm to pixels
left = mm_to_pixels(left_mm, dpi)
top = mm_to_pixels(top_mm, dpi)
right = mm_to_pixels(right_mm, dpi)
bottom = mm_to_pixels(bottom_mm, dpi)
print("left:",left,"top:",top,"right:",right,"bottom:",bottom)
if img.width - right <= left :
right=img.width-left-1
# Crop the image
cropped_img = img.crop((left, top, img.width - right, img.height - bottom))
return cropped_img
def resize_image(image):
image = image.convert('RGB')
model_input_size = (1024, 1024)
image = image.resize(model_input_size, Image.BILINEAR)
return image
def process(image,left_mm, top_mm, right_mm, bottom_mm, ruler, resize,actual_height_in_inches,background):
# prepare input
orig_image = Image.fromarray(image)
print("orig size:",orig_image.size)
w,h = orig_im_size = orig_image.size
image = resize_image(orig_image)
new_im=image;
if background:
# remove background
im_np = np.array(image)
im_tensor = torch.tensor(im_np, dtype=torch.float32).permute(2,0,1)
im_tensor = torch.unsqueeze(im_tensor,0)
im_tensor = torch.divide(im_tensor,255.0)
im_tensor = normalize(im_tensor,[0.5,0.5,0.5],[1.0,1.0,1.0])
if torch.cuda.is_available():
im_tensor=im_tensor.cuda()
#inference
result=net(im_tensor)
# post process
result = torch.squeeze(F.interpolate(result[0][0], size=(h,w), mode='bilinear') ,0)
ma = torch.max(result)
mi = torch.min(result)
result = (result-mi)/(ma-mi)
# image to pil
im_array = (result*255).cpu().data.numpy().astype(np.uint8)
pil_im = Image.fromarray(np.squeeze(im_array))
# paste the mask on the original image
new_im = Image.new("RGBA", pil_im.size, (0,0,0,0))
new_im.paste(orig_image, mask=pil_im)
# new_orig_image = orig_image.convert('RGBA')
else:
new_im=image;
new_im= crop_image(new_im, left_mm, top_mm, right_mm, bottom_mm)
if ruler:
new_im = draw_ruler(new_im,actual_height_in_inches)
if resize is not None and resize > 0:
new_im = new_im.resize( [int(resize * s) for s in new_im.size],Image.Resampling.LANCZOS )
print("resize:",new_im.size)
return new_im
# return [new_orig_image, new_im]
# block = gr.Blocks().queue()
# with block:
# gr.Markdown("## BRIA RMBG 1.4")
# gr.HTML('''
# <p style="margin-bottom: 10px; font-size: 94%">
# This is a demo for BRIA RMBG 1.4 that using
# <a href="https://huggingface.co/briaai/RMBG-1.4" target="_blank">BRIA RMBG-1.4 image matting model</a> as backbone.
# </p>
# ''')
# with gr.Row():
# with gr.Column():
# input_image = gr.Image(sources=None, type="pil") # None for upload, ctrl+v and webcam
# # input_image = gr.Image(sources=None, type="numpy") # None for upload, ctrl+v and webcam
# run_button = gr.Button(value="Run")
# with gr.Column():
# result_gallery = gr.Gallery(label='Output', show_label=False, elem_id="gallery", columns=[1], height='auto')
# ips = [input_image]
# run_button.click(fn=process, inputs=ips, outputs=[result_gallery])
# block.launch(debug = True)
# block = gr.Blocks().queue()
gr.Markdown("## BRIA RMBG 1.4")
gr.HTML('''
<p style="margin-bottom: 10px; font-size: 94%">
This app is leveraging the RMBG v1.4 model which is developed on the IS-Net enhanced with unique training scheme and proprietary dataset.
These modifications significantly improve the model’s accuracy and effectiveness in diverse image-processing scenarios.
This is an internal tool being tested for rootsraja.in product images at
<a href="https://huggingface.co/spaces/laitkor/product-image" target="_blank">Rootsraja internal product tool using an image matting model</a> as backbone.
</p>
''')
title = "Background Removal, Cropping, Resizing, Ruler and scaling"
description = r"""<a href="https://huggingface.co/spaces/laitkor/product-image" target="_blank">Rootsraja internal product tool<br>
To use upload your image and wait. Read more at model card <a href='https://huggingface.co/briaai/RMBG-1.4' target='_blank'><b>briaai/RMBG-1.4</b></a>.<br>
"""
examples = [['./input.jpg'],]
# output = ImageSlider(position=0.5,label='Image without background', type="pil", show_download_button=True)
# demo = gr.Interface(fn=process,inputs="image", outputs=output, examples=examples, title=title, description=description)
demo = gr.Interface(fn=process,
inputs=[
gr.Image(type="numpy", label="Upload Image"),
gr.Number(label="Left Crop (mm)",value=0),
gr.Number(label="Top Crop (mm)",value=0),
gr.Number(label="Right Crop (mm)",value=0),
gr.Number(label="Bottom Crop (mm)",value=0),
gr.Checkbox(label="Ruler!"),
gr.Number(label="Resize (between 0.1 and 0.9)",value=0),
gr.Number(label="Plant height in inches",value=0),
gr.Checkbox(label="Remove Background?")
],
outputs="image", examples=examples, title=title, description=description)
if __name__ == "__main__":
demo.launch(share=False)