WIP: add quick-draw example sketch

This commit is contained in:
2025-05-28 21:06:03 +02:00
parent b2ff18e76d
commit 52083735c7
3 changed files with 1689 additions and 0 deletions

460
quick_draw_api.py Normal file
View File

@@ -0,0 +1,460 @@
"""Quick draw dataset
Example contributed by Blair Morrison (https://github.com/blrm) and adapted by Antoine Beyeler
"""
import pathlib
import random
import struct
import urllib.request
from itertools import islice
import vpype as vp
from shapely.geometry import MultiLineString
import vsketch
quick_draw_categories = (
"aircraft carrier",
"airplane",
"alarm clock",
"ambulance",
"angel",
"animal migration",
"ant",
"anvil",
"apple",
"arm",
"asparagus",
"axe",
"backpack",
"banana",
"bandage",
"barn",
"baseball",
"baseball bat",
"basket",
"basketball",
"bat",
"bathtub",
"beach",
"bear",
"beard",
"bed",
"bee",
"belt",
"bench",
"bicycle",
"binoculars",
"bird",
"birthday cake",
"blackberry",
"blueberry",
"book",
"boomerang",
"bottlecap",
"bowtie",
"bracelet",
"brain",
"bread",
"bridge",
"broccoli",
"broom",
"bucket",
"bulldozer",
"bus",
"bush",
"butterfly",
"cactus",
"cake",
"calculator",
"calendar",
"camel",
"camera",
"camouflage",
"campfire",
"candle",
"cannon",
"canoe",
"car",
"carrot",
"castle",
"cat",
"ceiling fan",
"cello",
"cell phone",
"chair",
"chandelier",
"church",
"circle",
"clarinet",
"clock",
"cloud",
"coffee cup",
"compass",
"computer",
"cookie",
"cooler",
"couch",
"cow",
"crab",
"crayon",
"crocodile",
"crown",
"cruise ship",
"cup",
"diamond",
"dishwasher",
"diving board",
"dog",
"dolphin",
"donut",
"door",
"dragon",
"dresser",
"drill",
"drums",
"duck",
"dumbbell",
"ear",
"elbow",
"elephant",
"envelope",
"eraser",
"eye",
"eyeglasses",
"face",
"fan",
"feather",
"fence",
"finger",
"fire hydrant",
"fireplace",
"firetruck",
"fish",
"flamingo",
"flashlight",
"flip flops",
"floor lamp",
"flower",
"flying saucer",
"foot",
"fork",
"frog",
"frying pan",
"garden",
"garden hose",
"giraffe",
"goatee",
"golf club",
"grapes",
"grass",
"guitar",
"hamburger",
"hammer",
"hand",
"harp",
"hat",
"headphones",
"hedgehog",
"helicopter",
"helmet",
"hexagon",
"hockey puck",
"hockey stick",
"horse",
"hospital",
"hot air balloon",
"hot dog",
"hot tub",
"hourglass",
"house",
"house plant",
"hurricane",
"ice cream",
"jacket",
"jail",
"kangaroo",
"key",
"keyboard",
"knee",
"knife",
"ladder",
"lantern",
"laptop",
"leaf",
"leg",
"light bulb",
"lighter",
"lighthouse",
"lightning",
"line",
"lion",
"lipstick",
"lobster",
"lollipop",
"mailbox",
"map",
"marker",
"matches",
"megaphone",
"mermaid",
"microphone",
"microwave",
"monkey",
"moon",
"mosquito",
"motorbike",
"mountain",
"mouse",
"moustache",
"mouth",
"mug",
"mushroom",
"nail",
"necklace",
"nose",
"ocean",
"octagon",
"octopus",
"onion",
"oven",
"owl",
"paintbrush",
"paint can",
"palm tree",
"panda",
"pants",
"paper clip",
"parachute",
"parrot",
"passport",
"peanut",
"pear",
"peas",
"pencil",
"penguin",
"piano",
"pickup truck",
"picture frame",
"pig",
"pillow",
"pineapple",
"pizza",
"pliers",
"police car",
"pond",
"pool",
"popsicle",
"postcard",
"potato",
"power outlet",
"purse",
"rabbit",
"raccoon",
"radio",
"rain",
"rainbow",
"rake",
"remote control",
"rhinoceros",
"rifle",
"river",
"roller coaster",
"rollerskates",
"sailboat",
"sandwich",
"saw",
"saxophone",
"school bus",
"scissors",
"scorpion",
"screwdriver",
"sea turtle",
"see saw",
"shark",
"sheep",
"shoe",
"shorts",
"shovel",
"sink",
"skateboard",
"skull",
"skyscraper",
"sleeping bag",
"smiley face",
"snail",
"snake",
"snorkel",
"snowflake",
"snowman",
"soccer ball",
"sock",
"speedboat",
"spider",
"spoon",
"spreadsheet",
"square",
"squiggle",
"squirrel",
"stairs",
"star",
"steak",
"stereo",
"stethoscope",
"stitches",
"stop sign",
"stove",
"strawberry",
"streetlight",
"string bean",
"submarine",
"suitcase",
"sun",
"swan",
"sweater",
"swing set",
"sword",
"syringe",
"table",
"teapot",
"teddy-bear",
"telephone",
"television",
"tennis racquet",
"tent",
"The Eiffel Tower",
"The Great Wall of China",
"The Mona Lisa",
"tiger",
"toaster",
"toe",
"toilet",
"tooth",
"toothbrush",
"toothpaste",
"tornado",
"tractor",
"traffic light",
"train",
"tree",
"triangle",
"trombone",
"truck",
"trumpet",
"t-shirt",
"umbrella",
"underwear",
"van",
"vase",
"violin",
"washing machine",
"watermelon",
"waterslide",
"whale",
"wheel",
"windmill",
"wine bottle",
"wine glass",
"wristwatch",
"yoga",
"zebra",
"zigzag",
)
def unpack_drawing(file_handle):
(key_id,) = struct.unpack("Q", file_handle.read(8))
(country_code,) = struct.unpack("2s", file_handle.read(2))
(recognized,) = struct.unpack("b", file_handle.read(1))
(timestamp,) = struct.unpack("I", file_handle.read(4))
(n_strokes,) = struct.unpack("H", file_handle.read(2))
image = []
for i in range(n_strokes):
(n_points,) = struct.unpack("H", file_handle.read(2))
fmt = str(n_points) + "B"
x = struct.unpack(fmt, file_handle.read(n_points))
y = struct.unpack(fmt, file_handle.read(n_points))
image.append((x, y))
return {
"key_id": key_id,
"country_code": country_code,
"recognized": recognized,
"timestamp": timestamp,
"image": image,
}
def unpack_drawings(filename):
with open(filename, "rb") as f:
while True:
try:
yield unpack_drawing(f)
except struct.error:
break
def quickdraw_to_linestring(qd_image):
"""Returns a Shapely MultiLineString for the provided quickdraw image.
This MultiLineString can be passed to vsketch
"""
linestrings = []
for i in range(0, len(qd_image["image"])):
line = zip(qd_image["image"][i][0], qd_image["image"][i][1])
linestrings.append(tuple(line))
return MultiLineString(linestrings)
class QuickDrawSketch(vsketch.SketchClass):
category = vsketch.Param("crab", choices=quick_draw_categories)
page_size = vsketch.Param("a4", choices=vp.PAGE_SIZES.keys())
landscape = vsketch.Param(False)
margins = vsketch.Param(10, 0, unit="mm")
layer_count = vsketch.Param(2, 1)
columns = vsketch.Param(9, 1)
rows = vsketch.Param(13, 1)
scale_factor = vsketch.Param(3.0)
def draw(self, vsk: vsketch.Vsketch) -> None:
vsk.size(self.page_size, landscape=self.landscape)
vsk.penWidth("0.5mm")
# obtain the datafile
file_name = self.category + ".bin"
file_path = pathlib.Path(file_name)
url = "https://storage.googleapis.com/quickdraw_dataset/full/binary/"
url += file_name.replace(" ", "%20")
if not file_path.exists():
urllib.request.urlretrieve(url, file_name)
# extract some drawings
drawing_set = unpack_drawings(file_name)
drawing_subset = list(islice(drawing_set, 10000))
# draw stuff
width = vsk.width - 2 * self.margins
height = vsk.height - 2 * self.margins
n = self.columns * self.rows
samples = random.sample(drawing_subset, n)
for j in range(self.rows):
with vsk.pushMatrix():
for i in range(self.columns):
idx = j * self.columns + i
with vsk.pushMatrix():
vsk.scale(self.scale_factor * min(1 / self.columns, 1 / self.rows))
drawing = quickdraw_to_linestring(samples[idx])
vsk.stroke((idx % self.layer_count) + 1)
vsk.geometry(drawing)
vsk.translate(width / self.columns, 0)
vsk.translate(0, height / self.rows)
def finalize(self, vsk: vsketch.Vsketch) -> None:
vsk.vpype("linemerge linesort")
if __name__ == "__main__":
QuickDrawSketch.display()