I am trying to make a python code that takes an image and converts it into JavaScript so my Blot by Hack Club can read that JS and create that image. Blot documentation Currently my code does that but draws random lines over the code. Also the code generates way to many lines for the blot to handle. How could I find the sweet spot of highest quality with least amount of lines generated
from PIL import Image, ImageOps
import numpy as np
import cv2
from skimage import measure, transform
import math
# Set the detail level (1 for maximum detail, 0 for minimal detail)
detail_level = 0.4
# Function to process the image and extract edges with higher precision
def process_image(image_path):
# Load image and convert to grayscale
image = Image.open(image_path).convert("L")
image = ImageOps.mirror(image) # Mirror the image horizontally
image = ImageOps.invert(image) # Invert to make the background black and foreground white
image = image.rotate(180) # Rotate the image by 180 degrees
image_array = np.array(image)
# Calculate ksize (ensuring it's odd and positive)
ksize_value = max(3, int(round(-3.333333333333 * detail_level + 5.666666666666666667)))
if ksize_value % 2 == 0:
ksize_value += 1
ksize = (ksize_value, ksize_value)
# Apply a slight blur to reduce noise
blurred = cv2.GaussianBlur(image_array, ksize, 0)
# Use Canny edge detection with lower thresholds for more sensitivity
canny_threshold1 = int(round(-33.333333333 * detail_level + 56.6666666666667))
canny_threshold2 = int(round(-83.33333333 * detail_level + 166.666666667))
edges = cv2.Canny(blurred, canny_threshold1, canny_threshold2)
# Optionally, thicken the edges slightly
edges = transform.rescale(edges, 1.0, anti_aliasing=True)
edges = cv2.dilate(edges, np.ones((2,2),np.uint8), iterations=1)
# Use contours to find connected components
contours = measure.find_contours(edges, 0.8)
return contours, image_array.shape
# Function to generate the Blot code
def generate_blot_code(contours, dimensions, detail_level=0.8, min_distance_threshold=5):
print("Generating Blot code...")
lines = []
# Set a fixed tolerance for fine control over details
max_tolerance = 0.5 # More detail
min_tolerance = 0.1 # Minimal simplification
tolerance = (1 - detail_level) * (max_tolerance - min_tolerance) + min_tolerance
# Calculate bounding box of all contours
all_points = np.concatenate(contours)
min_y, min_x = np.min(all_points, axis=0)
max_y, max_x = np.max(all_points, axis=0)
# Calculate scale and translation to center the drawing
scale_x = (dimensions[1] - 1) / (max_x - min_x)
scale_y = (dimensions[0] - 1) / (max_y - min_y)
scale = min(scale_x, scale_y) # Maintain aspect ratio by using the smallest scale factor
translate_x = (dimensions[1] - (max_x - min_x) * scale) / 2 - min_x * scale
translate_y = (dimensions[0] - (max_y - min_y) * scale) / 2 - min_y * scale
# Initialize line counter
line_counter = 0
for contour in contours:
# Smooth the contour and simplify based on the detail level
smoothed_contour = measure.approximate_polygon(contour, tolerance=tolerance)
if len(smoothed_contour) >= 2: # Only consider meaningful contours
prev_x, prev_y = smoothed_contour[0] # Initialize with the first point
for i in range(1, len(smoothed_contour)):
y1, x1 = prev_y, prev_x
y2, x2 = smoothed_contour[i]
# Scale and translate coordinates
x1_scaled = int(x1 * scale + translate_x)
y1_scaled = int(y1 * scale + translate_y)
x2_scaled = int(x2 * scale + translate_x)
y2_scaled = int(y2 * scale + translate_y)
# Calculate the Euclidean distance between consecutive points
distance = math.sqrt((x2_scaled - x1_scaled) ** 2 + (y2_scaled - y1_scaled) ** 2)
# Only draw the line if the distance is greater than the threshold
if distance > min_distance_threshold:
lines.append(f"finalLines.push([[{x1_scaled}, {y1_scaled}], [{x2_scaled}, {y2_scaled}]]);n")
prev_x, prev_y = x2, y2 # Update the previous point to the current point
# Increment line counter
line_counter += 1
if line_counter % 50 == 0:
lines.append(f"console.log('{line_counter} lines completed');n")
blot_code = [
"// Produced by Aditya Anand's Blotinator, not human-writtenn",
f"setDocDimensions({dimensions[0]}, {dimensions[1]});n",
"const finalLines = [];n"
]
blot_code.extend(lines)
blot_code.append("drawLines(finalLines);")
return blot_code
# Main function
if __name__ == "__main__":
# Use the correct image path
image_path = '/Users/vivaanshahani/Downloads/IMG_9654 3.jpg'
# Process the image
contours, dimensions = process_image(image_path)
# Generate the Blot code with the specified detail level
blot_code = generate_blot_code(contours, dimensions, detail_level)
# Write the Blot code to a file
output_path = "/Users/vivaanshahani/Downloads/Blotcode.js"
with open(output_path, "w") as file:
file.writelines(blot_code)
print(f"Blot code generated and saved to {output_path}")