edpf / app.py
Gerold Meisinger
fixed requirements
e310885
import glob
import os
import cv2
import gradio as gr
def center_crop_mod64(img):
"""Crop image to largest area that's divisible by 64"""
h, w = img.shape[:2]
shortside, longside = min(w, h), max(w, h)
longside_crop = (longside // 64) * 64
left = (w - longside_crop) // 2 if w > h else 0
top = (h - longside_crop) // 2 if h > w else 0
right = left + longside_crop if w > h else shortside
bottom = top + longside_crop if h > w else shortside
return img[top:bottom, left:right]
def load_example_images():
"""Load all images from images/ directory"""
image_dir = os.path.join(os.path.dirname(__file__), "images")
if not os.path.exists(image_dir):
return []
extensions = ['*.png', '*.jpg', '*.jpeg', '*.webp']
examples = []
for ext in extensions:
examples.extend(glob.glob(os.path.join(image_dir, ext)))
examples.extend(glob.glob(os.path.join(image_dir, ext.upper())))
return sorted(examples)
def apply_edge_detection(image_rgb, algorithm, apply_crop, convert2grayscale, params):
"""Apply selected edge detection algorithm with specified parameters"""
if image_rgb is None:
return None
# Apply cropping if requested
if apply_crop:
img_processed = center_crop_mod64(image_rgb)
else:
img_processed = image_rgb.copy()
# Convert to grayscale if requested
if convert2grayscale and len(img_processed.shape) == 3:
img_input = cv2.cvtColor(cv2.cvtColor(img_processed, cv2.COLOR_RGB2GRAY), cv2.COLOR_GRAY2RGB)
else:
img_input = img_processed
ret = img_input.copy()
# set parameters
# Detect edges
ed = cv2.ximgproc.createEdgeDrawing()
ed.setParams(params)
ed.detectEdges(img_input)
if algorithm == "Edges":
ret = ed.getEdgeImage()
return ret
elif algorithm == "Lines":
lines = ed.detectLines()
# draw lines
if lines is not None:
for line in lines:
x1, y1, x2, y2 = map(int, line[0])
cv2.line(ret, (x1, y1), (x2, y2), (0, 255, 0), 1)
return ret
elif algorithm == "Ellipses":
ellipsess = ed.detectEllipses()
# draw ellipses
if ellipsess is not None:
for ellipses in ellipsess:
ellipse = ellipses[0]
center = (int(ellipse[0]), int(ellipse[1]))
if ellipse[2] == 0: # Ellipse
axes = (int(ellipse[3]), int(ellipse[4]))
angle = ellipse[5]
cv2.ellipse(ret, center, axes, angle, 0, 360, (0, 255, 0), 1)
else: # Circle
radius = int(ellipse[2])
cv2.circle(ret, center, radius, (0, 255, 0), 1)
return ret
return img_input
# Create Gradio interface
with gr.Blocks(title="Edge Drawing") as app:
examples = load_example_images()
gr.Markdown("# Enhanced Edge Detection using [Edge Drawing](https://github.com/CihanTopal/ED_Lib) with [opencv-contrib-python](https://docs.opencv.org/4.x/d1/d1c/classcv_1_1ximgproc_1_1EdgeDrawing.html)")
# Image row - Input and Output side by side
with gr.Row():
input_image = gr.Image(value=examples[0] if examples else None , label="Input Image" , type="numpy")
output_image = gr.Image(value=None , label="Detection Result" , type="numpy")
# Controls row - Processing Options on left, Parameters on right
with gr.Row():
# Processing Options
with gr.Column():
with gr.Group():
gr.Markdown("### Processing Options")
algorithm_radio = gr.Radio(label="Detection mode", value="Edges", choices=["Edges", "Lines", "Ellipses"])
paramterfree_checkbox = gr.Checkbox(label ="Parameter Free Mode" , value=True , info="Auto-determine optimal parameters")
convert2grayscale_checkbox = gr.Checkbox(label ="Convert to Grayscale" , value=False , info="Force conversion to grayscale prior to edge detection (note that Edge Drawing has native support for color images)")
crop_checkbox = gr.Checkbox(label ="Apply center crop mod-64" , value=False , info="Crop to largest area divisible by 64 (used for Stable Diffusion)")
# EdgeDrawing Parameters
with gr.Column():
with gr.Group():
gr.Markdown("### Edge Drawing Parameters")
# Parameter controls group (disabled by default)
with gr.Group(visible=False) as param_group:
with gr.Column():
with gr.Row():
gradient_operator = gr.Radio(choices=["PREWITT", "SOBEL", "SCHARR", "LSD"], value="PREWITT", label="Gradient Operator", info="indicates the operator used for gradient calculation. Default value is PREWITT")
gradient_threshold = gr.Slider(minimum= 1 , maximum= 100 , value= 20 , step= 1 , label="Gradient Threshold" , scale= 1 , info="Threshold value of gradiential difference between pixels. Used to create gradient image. Default value is 20" )
with gr.Row():
anchor_threshold = gr.Slider(minimum= 0 , maximum= 255 , value= 0 , step= 1 , label="Anchor Threshold" , scale= 1 , info="Threshold value used to select anchor points. Default value is 0" )
min_path_length = gr.Slider(minimum= 1 , maximum= 50 , value= 10 , step= 1 , label="Min Path Length" , scale= 1 , info="Minimum connected pixels length processed to create an edge segment. Default value is 10" )
with gr.Row():
min_line_length = gr.Slider(minimum= -1 , maximum= 100 , value= -1 , step= 1 , label="Min Line Length" , scale= 1 , info="Minimum line length to detect. Default value is -1" )
line_fit_error_threshold = gr.Slider(minimum= 0.1 , maximum= 10.0 , value= 1.0 , step= 0.1 , label="Line Fit Error Threshold" , scale= 1 , info="Default value is 1.0" )
max_distance_between_two_lines = gr.Slider(minimum= 1.0 , maximum= 20.0 , value= 6.0 , step= 0.1 , label="Max Distance Between Two Lines" , scale= 1 , info="Default value is 6.0" )
with gr.Row():
max_error_threshold = gr.Slider(minimum= 0.1 , maximum= 5.0 , value= 1.3 , step= 0.1 , label="Max Error Threshold" , scale= 1 , info="Default value is 1.3" )
scan_interval = gr.Slider(minimum= 1 , maximum= 10 , value= 1 , step= 1 , label="Scan Interval" , scale= 1 , info="Default value is 1" )
with gr.Row():
sigma = gr.Slider(minimum= 0.1 , maximum= 5.0 , value= 1.0 , step= 0.1 , label="Sigma" , scale= 1 , info="Sigma value for internal GaussianBlur() function. Default value is 1.0" )
nfa_validation = gr.Checkbox(label="NFA Validation" , value=True , info="Indicates if NFA (Number of False Alarms) algorithm will be used for line and ellipse validation. Default value is true")
sum_flag = gr.Checkbox(label="Sum Flag" , value=True , info="Default value is true")
# Update function
def update_output(
image_rgb, algorithm, apply_crop, conv2grayscale, pf_mode,
gradient_operator, anchor_threshold, gradient_threshold, min_path_length, min_line_length, line_fit_error_threshold, max_distance_between_two_lines, max_error_threshold, nfa_validation, scan_interval, sigma, sum_flag
):
operator_map = {
"PREWITT" : cv2.ximgproc.EdgeDrawing_PREWITT,
"SOBEL" : cv2.ximgproc.EdgeDrawing_SOBEL,
"SCHARR" : cv2.ximgproc.EdgeDrawing_SCHARR,
"LSD" : cv2.ximgproc.EdgeDrawing_LSD,
}
params = cv2.ximgproc.EdgeDrawing.Params()
params.PFmode = pf_mode
params.EdgeDetectionOperator = operator_map.get(gradient_operator, cv2.ximgproc.EdgeDrawing_PREWITT)
if not pf_mode:
params.AnchorThresholdValue = anchor_threshold
params.GradientThresholdValue = gradient_threshold
params.MinPathLength = min_path_length
params.MinLineLength = min_line_length
params.LineFitErrorThreshold = line_fit_error_threshold
params.MaxDistanceBetweenTwoLines = max_distance_between_two_lines
params.MaxErrorThreshold = max_error_threshold
params.NFAValidation = nfa_validation
params.ScanInterval = scan_interval
params.Sigma = sigma
params.SumFlag = sum_flag
ret = apply_edge_detection(image_rgb, algorithm, apply_crop, conv2grayscale, params)
return ret
# Function to toggle parameter group visibility
def toggle_params(pf_mode):
return gr.Group(visible=not pf_mode)
# Set up event handlers
inputs = [
input_image,
algorithm_radio,
crop_checkbox,
convert2grayscale_checkbox,
paramterfree_checkbox,
gradient_operator,
anchor_threshold,
gradient_threshold,
min_path_length,
min_line_length,
line_fit_error_threshold,
max_distance_between_two_lines,
max_error_threshold,
nfa_validation,
scan_interval,
sigma,
sum_flag,
]
# Update output when any input changes
for inp in inputs:
inp.change(fn=update_output, inputs=inputs, outputs=output_image)
# Toggle parameter group when parameter free mode changes
paramterfree_checkbox.change(fn=toggle_params, inputs=paramterfree_checkbox, outputs=param_group)
# Apply edge detection immediately on startup
app.load(fn=update_output, inputs=inputs, outputs=output_image)
# Examples
if examples:
gr.Examples(examples=examples, inputs=input_image, label="Example Images" )
if __name__ == "__main__":
app.launch()