From ca1e9420bbded5af49db6540f9707f49a1e2bbbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jod=C5=82os=CC=81?= Date: Fri, 24 May 2024 09:35:47 +0200 Subject: [PATCH 1/7] Migrate to AI server --- config/config.exs | 3 +- config/dev.exs | 4 +- config/prod.exs | 4 - config/runtime.exs | 5 +- examples/3-membrane.livemd | 1 - lib/ex_vision/cache.ex | 11 +- lib/ex_vision/model/definition/ortex.ex | 2 +- models/coco_categories.json | 93 -- models/coco_with_voc_labels_categories.json | 23 - .../deeplab_v3_mobilenetv3_segmentation.onnx | 3 - models/fasterrcnn_resnet50_fpn_detector.onnx | 3 - models/imagenet_v2_categories.json | 1002 ----------------- models/mobilenetv3small-classifier.onnx | 3 - models/ssdlite320_mobilenetv3_detector.onnx | 3 - .../exvision/test_utils/mock_cache_server.ex | 25 +- 15 files changed, 31 insertions(+), 1154 deletions(-) delete mode 100644 models/coco_categories.json delete mode 100644 models/coco_with_voc_labels_categories.json delete mode 100644 models/deeplab_v3_mobilenetv3_segmentation.onnx delete mode 100644 models/fasterrcnn_resnet50_fpn_detector.onnx delete mode 100644 models/imagenet_v2_categories.json delete mode 100644 models/mobilenetv3small-classifier.onnx delete mode 100644 models/ssdlite320_mobilenetv3_detector.onnx diff --git a/config/config.exs b/config/config.exs index b5143cd..a92ca44 100644 --- a/config/config.exs +++ b/config/config.exs @@ -4,7 +4,6 @@ config :nx, default_backend: EXLA.Backend config :logger, level: :debug config :ex_vision, - server_url: "EX_VISION_HOSTING_URI" |> System.get_env("http://localhost:8000") |> URI.new!(), - cache_path: System.get_env("EX_VISION_CACHE_DIR", "/tmp/ex_vision/cache") + server_url: URI.new!("https://ai.swmansion.com/exvision/files") import_config "#{config_env()}.exs" diff --git a/config/dev.exs b/config/dev.exs index b59c744..88b40c5 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -2,6 +2,4 @@ import Config config :ortex, Ortex.Native, features: ["coreml"] -config :ex_vision, - server_url: "EX_VISION_HOSTING_URI" |> System.get_env("http://localhost:8000") |> URI.new!(), - cache_path: System.get_env("EX_VISION_CACHE_DIR", "models") +config :ex_vision, cache_path: "models" diff --git a/config/prod.exs b/config/prod.exs index 239090c..477c907 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -1,7 +1,3 @@ import Config config :logger, level: :info - -config :ex_vision, - server_url: "EX_VISION_HOSTING_URI" |> System.get_env("http://localhost:8000") |> URI.new!(), - cache_path: System.get_env("EX_VISION_CACHE_DIR", "/tmp/ex_vision/cache") diff --git a/config/runtime.exs b/config/runtime.exs index ec7bb76..b91ec17 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -1,5 +1,8 @@ import Config config :ex_vision, - server_url: "EX_VISION_HOSTING_URI" |> System.get_env("http://localhost:8000") |> URI.new!(), + server_url: + "EX_VISION_HOSTING_URI" + |> System.get_env("https://ai.swmansion.com/exvision/files") + |> URI.new!(), cache_path: System.get_env("EX_VISION_CACHE_DIR", "/tmp/ex_vision/cache") diff --git a/examples/3-membrane.livemd b/examples/3-membrane.livemd index dc92698..4430804 100644 --- a/examples/3-membrane.livemd +++ b/examples/3-membrane.livemd @@ -93,7 +93,6 @@ defmodule Membrane.ExVision.Detector do |> then(&"#{&1}") |> :base64.encode() |> String.to_atom() - |> dbg() {:ok, pid} = Model.start_link(name: name) diff --git a/lib/ex_vision/cache.ex b/lib/ex_vision/cache.ex index 44b9e94..e482e93 100644 --- a/lib/ex_vision/cache.ex +++ b/lib/ex_vision/cache.ex @@ -10,13 +10,17 @@ defmodule ExVision.Cache do Application.get_env(:ex_vision, :cache_path, @default_cache_path) end - @default_server_url Application.compile_env(:ex_vision, :server_url, "http://localhost:8000") + @default_server_url Application.compile_env( + :ex_vision, + :server_url, + URI.new!("https://ai.swmansion.com/exvision/files") + ) defp get_server_url() do Application.get_env(:ex_vision, :server_url, @default_server_url) end @type lazy_get_option_t() :: - {:cache_path, Path.t()} | {:server_url, String.t() | URI.t()} | {:force, true} + {:cache_path, Path.t()} | {:server_url, String.t() | URI.t()} | {:force, boolean()} @doc """ Lazily evaluate the path from the cache directory. @@ -73,7 +77,8 @@ defmodule ExVision.Cache do {:ok, _resp} -> :ok - {:error, _reason} = error -> + {:error, reason} = error -> + Logger.error("Failed to download the file due to #{inspect(reason)}") File.rm(target_file_path) error end diff --git a/lib/ex_vision/model/definition/ortex.ex b/lib/ex_vision/model/definition/ortex.ex index c42882c..f953508 100644 --- a/lib/ex_vision/model/definition/ortex.ex +++ b/lib/ex_vision/model/definition/ortex.ex @@ -36,7 +36,7 @@ defmodule ExVision.Model.Definition.Ortex do - `:cache_path` - specifies a caching directory for this model. - `:providers` - a list of desired providers, sorted by preference. Onnx will attempt to use the first available provider. If none of the provided is available, onnx will fallback to `:cpu`. Default: `[:cpu]` - - `:batch_size` - specifies a default batch size for this instance. Default: `1` + - `:batch_size` - specifies a default batch size for this instance. Default: `1`. """ @type load_option_t() :: {:cache_path, Path.t()} diff --git a/models/coco_categories.json b/models/coco_categories.json deleted file mode 100644 index 7978e7e..0000000 --- a/models/coco_categories.json +++ /dev/null @@ -1,93 +0,0 @@ -[ - "__background__", - "person", - "bicycle", - "car", - "motorcycle", - "airplane", - "bus", - "train", - "truck", - "boat", - "traffic light", - "fire hydrant", - "N/A", - "stop sign", - "parking meter", - "bench", - "bird", - "cat", - "dog", - "horse", - "sheep", - "cow", - "elephant", - "bear", - "zebra", - "giraffe", - "N/A", - "backpack", - "umbrella", - "N/A", - "N/A", - "handbag", - "tie", - "suitcase", - "frisbee", - "skis", - "snowboard", - "sports ball", - "kite", - "baseball bat", - "baseball glove", - "skateboard", - "surfboard", - "tennis racket", - "bottle", - "N/A", - "wine glass", - "cup", - "fork", - "knife", - "spoon", - "bowl", - "banana", - "apple", - "sandwich", - "orange", - "broccoli", - "carrot", - "hot dog", - "pizza", - "donut", - "cake", - "chair", - "couch", - "potted plant", - "bed", - "N/A", - "dining table", - "N/A", - "N/A", - "toilet", - "N/A", - "tv", - "laptop", - "mouse", - "remote", - "keyboard", - "cell phone", - "microwave", - "oven", - "toaster", - "sink", - "refrigerator", - "N/A", - "book", - "clock", - "vase", - "scissors", - "teddy bear", - "hair drier", - "toothbrush" -] diff --git a/models/coco_with_voc_labels_categories.json b/models/coco_with_voc_labels_categories.json deleted file mode 100644 index 63ee1cb..0000000 --- a/models/coco_with_voc_labels_categories.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - "__background__", - "aeroplane", - "bicycle", - "bird", - "boat", - "bottle", - "bus", - "car", - "cat", - "chair", - "cow", - "diningtable", - "dog", - "horse", - "motorbike", - "person", - "pottedplant", - "sheep", - "sofa", - "train", - "tvmonitor" -] diff --git a/models/deeplab_v3_mobilenetv3_segmentation.onnx b/models/deeplab_v3_mobilenetv3_segmentation.onnx deleted file mode 100644 index 3b699d3..0000000 --- a/models/deeplab_v3_mobilenetv3_segmentation.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d6043d375e83b91793acb86af7ef37783405814e4c03fc8e9d02dcb44beb75ea -size 44111008 diff --git a/models/fasterrcnn_resnet50_fpn_detector.onnx b/models/fasterrcnn_resnet50_fpn_detector.onnx deleted file mode 100644 index d817afc..0000000 --- a/models/fasterrcnn_resnet50_fpn_detector.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:358a3a72bb702a9540cd8482063b899937c5d11da34f45f7272e67b41fc558b3 -size 167514670 diff --git a/models/imagenet_v2_categories.json b/models/imagenet_v2_categories.json deleted file mode 100644 index 88cde3e..0000000 --- a/models/imagenet_v2_categories.json +++ /dev/null @@ -1,1002 +0,0 @@ -[ - "tench", - "goldfish", - "great white shark", - "tiger shark", - "hammerhead", - "electric ray", - "stingray", - "cock", - "hen", - "ostrich", - "brambling", - "goldfinch", - "house finch", - "junco", - "indigo bunting", - "robin", - "bulbul", - "jay", - "magpie", - "chickadee", - "water ouzel", - "kite", - "bald eagle", - "vulture", - "great grey owl", - "European fire salamander", - "common newt", - "eft", - "spotted salamander", - "axolotl", - "bullfrog", - "tree frog", - "tailed frog", - "loggerhead", - "leatherback turtle", - "mud turtle", - "terrapin", - "box turtle", - "banded gecko", - "common iguana", - "American chameleon", - "whiptail", - "agama", - "frilled lizard", - "alligator lizard", - "Gila monster", - "green lizard", - "African chameleon", - "Komodo dragon", - "African crocodile", - "American alligator", - "triceratops", - "thunder snake", - "ringneck snake", - "hognose snake", - "green snake", - "king snake", - "garter snake", - "water snake", - "vine snake", - "night snake", - "boa constrictor", - "rock python", - "Indian cobra", - "green mamba", - "sea snake", - "horned viper", - "diamondback", - "sidewinder", - "trilobite", - "harvestman", - "scorpion", - "black and gold garden spider", - "barn spider", - "garden spider", - "black widow", - "tarantula", - "wolf spider", - "tick", - "centipede", - "black grouse", - "ptarmigan", - "ruffed grouse", - "prairie chicken", - "peacock", - "quail", - "partridge", - "African grey", - "macaw", - "sulphur-crested cockatoo", - "lorikeet", - "coucal", - "bee eater", - "hornbill", - "hummingbird", - "jacamar", - "toucan", - "drake", - "red-breasted merganser", - "goose", - "black swan", - "tusker", - "echidna", - "platypus", - "wallaby", - "koala", - "wombat", - "jellyfish", - "sea anemone", - "brain coral", - "flatworm", - "nematode", - "conch", - "snail", - "slug", - "sea slug", - "chiton", - "chambered nautilus", - "Dungeness crab", - "rock crab", - "fiddler crab", - "king crab", - "American lobster", - "spiny lobster", - "crayfish", - "hermit crab", - "isopod", - "white stork", - "black stork", - "spoonbill", - "flamingo", - "little blue heron", - "American egret", - "bittern", - "crane bird", - "limpkin", - "European gallinule", - "American coot", - "bustard", - "ruddy turnstone", - "red-backed sandpiper", - "redshank", - "dowitcher", - "oystercatcher", - "pelican", - "king penguin", - "albatross", - "grey whale", - "killer whale", - "dugong", - "sea lion", - "Chihuahua", - "Japanese spaniel", - "Maltese dog", - "Pekinese", - "Shih-Tzu", - "Blenheim spaniel", - "papillon", - "toy terrier", - "Rhodesian ridgeback", - "Afghan hound", - "basset", - "beagle", - "bloodhound", - "bluetick", - "black-and-tan coonhound", - "Walker hound", - "English foxhound", - "redbone", - "borzoi", - "Irish wolfhound", - "Italian greyhound", - "whippet", - "Ibizan hound", - "Norwegian elkhound", - "otterhound", - "Saluki", - "Scottish deerhound", - "Weimaraner", - "Staffordshire bullterrier", - "American Staffordshire terrier", - "Bedlington terrier", - "Border terrier", - "Kerry blue terrier", - "Irish terrier", - "Norfolk terrier", - "Norwich terrier", - "Yorkshire terrier", - "wire-haired fox terrier", - "Lakeland terrier", - "Sealyham terrier", - "Airedale", - "cairn", - "Australian terrier", - "Dandie Dinmont", - "Boston bull", - "miniature schnauzer", - "giant schnauzer", - "standard schnauzer", - "Scotch terrier", - "Tibetan terrier", - "silky terrier", - "soft-coated wheaten terrier", - "West Highland white terrier", - "Lhasa", - "flat-coated retriever", - "curly-coated retriever", - "golden retriever", - "Labrador retriever", - "Chesapeake Bay retriever", - "German short-haired pointer", - "vizsla", - "English setter", - "Irish setter", - "Gordon setter", - "Brittany spaniel", - "clumber", - "English springer", - "Welsh springer spaniel", - "cocker spaniel", - "Sussex spaniel", - "Irish water spaniel", - "kuvasz", - "schipperke", - "groenendael", - "malinois", - "briard", - "kelpie", - "komondor", - "Old English sheepdog", - "Shetland sheepdog", - "collie", - "Border collie", - "Bouvier des Flandres", - "Rottweiler", - "German shepherd", - "Doberman", - "miniature pinscher", - "Greater Swiss Mountain dog", - "Bernese mountain dog", - "Appenzeller", - "EntleBucher", - "boxer", - "bull mastiff", - "Tibetan mastiff", - "French bulldog", - "Great Dane", - "Saint Bernard", - "Eskimo dog", - "malamute", - "Siberian husky", - "dalmatian", - "affenpinscher", - "basenji", - "pug", - "Leonberg", - "Newfoundland", - "Great Pyrenees", - "Samoyed", - "Pomeranian", - "chow", - "keeshond", - "Brabancon griffon", - "Pembroke", - "Cardigan", - "toy poodle", - "miniature poodle", - "standard poodle", - "Mexican hairless", - "timber wolf", - "white wolf", - "red wolf", - "coyote", - "dingo", - "dhole", - "African hunting dog", - "hyena", - "red fox", - "kit fox", - "Arctic fox", - "grey fox", - "tabby", - "tiger cat", - "Persian cat", - "Siamese cat", - "Egyptian cat", - "cougar", - "lynx", - "leopard", - "snow leopard", - "jaguar", - "lion", - "tiger", - "cheetah", - "brown bear", - "American black bear", - "ice bear", - "sloth bear", - "mongoose", - "meerkat", - "tiger beetle", - "ladybug", - "ground beetle", - "long-horned beetle", - "leaf beetle", - "dung beetle", - "rhinoceros beetle", - "weevil", - "fly", - "bee", - "ant", - "grasshopper", - "cricket", - "walking stick", - "cockroach", - "mantis", - "cicada", - "leafhopper", - "lacewing", - "dragonfly", - "damselfly", - "admiral", - "ringlet", - "monarch", - "cabbage butterfly", - "sulphur butterfly", - "lycaenid", - "starfish", - "sea urchin", - "sea cucumber", - "wood rabbit", - "hare", - "Angora", - "hamster", - "porcupine", - "fox squirrel", - "marmot", - "beaver", - "guinea pig", - "sorrel", - "zebra", - "hog", - "wild boar", - "warthog", - "hippopotamus", - "ox", - "water buffalo", - "bison", - "ram", - "bighorn", - "ibex", - "hartebeest", - "impala", - "gazelle", - "Arabian camel", - "llama", - "weasel", - "mink", - "polecat", - "black-footed ferret", - "otter", - "skunk", - "badger", - "armadillo", - "three-toed sloth", - "orangutan", - "gorilla", - "chimpanzee", - "gibbon", - "siamang", - "guenon", - "patas", - "baboon", - "macaque", - "langur", - "colobus", - "proboscis monkey", - "marmoset", - "capuchin", - "howler monkey", - "titi", - "spider monkey", - "squirrel monkey", - "Madagascar cat", - "indri", - "Indian elephant", - "African elephant", - "lesser panda", - "giant panda", - "barracouta", - "eel", - "coho", - "rock beauty", - "anemone fish", - "sturgeon", - "gar", - "lionfish", - "puffer", - "abacus", - "abaya", - "academic gown", - "accordion", - "acoustic guitar", - "aircraft carrier", - "airliner", - "airship", - "altar", - "ambulance", - "amphibian", - "analog clock", - "apiary", - "apron", - "ashcan", - "assault rifle", - "backpack", - "bakery", - "balance beam", - "balloon", - "ballpoint", - "Band Aid", - "banjo", - "bannister", - "barbell", - "barber chair", - "barbershop", - "barn", - "barometer", - "barrel", - "barrow", - "baseball", - "basketball", - "bassinet", - "bassoon", - "bathing cap", - "bath towel", - "bathtub", - "beach wagon", - "beacon", - "beaker", - "bearskin", - "beer bottle", - "beer glass", - "bell cote", - "bib", - "bicycle-built-for-two", - "bikini", - "binder", - "binoculars", - "birdhouse", - "boathouse", - "bobsled", - "bolo tie", - "bonnet", - "bookcase", - "bookshop", - "bottlecap", - "bow", - "bow tie", - "brass", - "brassiere", - "breakwater", - "breastplate", - "broom", - "bucket", - "buckle", - "bulletproof vest", - "bullet train", - "butcher shop", - "cab", - "caldron", - "candle", - "cannon", - "canoe", - "can opener", - "cardigan", - "car mirror", - "carousel", - "carpenter's kit", - "carton", - "car wheel", - "cash machine", - "cassette", - "cassette player", - "castle", - "catamaran", - "CD player", - "cello", - "cellular telephone", - "chain", - "chainlink fence", - "chain mail", - "chain saw", - "chest", - "chiffonier", - "chime", - "china cabinet", - "Christmas stocking", - "church", - "cinema", - "cleaver", - "cliff dwelling", - "cloak", - "clog", - "cocktail shaker", - "coffee mug", - "coffeepot", - "coil", - "combination lock", - "computer keyboard", - "confectionery", - "container ship", - "convertible", - "corkscrew", - "cornet", - "cowboy boot", - "cowboy hat", - "cradle", - "crane", - "crash helmet", - "crate", - "crib", - "Crock Pot", - "croquet ball", - "crutch", - "cuirass", - "dam", - "desk", - "desktop computer", - "dial telephone", - "diaper", - "digital clock", - "digital watch", - "dining table", - "dishrag", - "dishwasher", - "disk brake", - "dock", - "dogsled", - "dome", - "doormat", - "drilling platform", - "drum", - "drumstick", - "dumbbell", - "Dutch oven", - "electric fan", - "electric guitar", - "electric locomotive", - "entertainment center", - "envelope", - "espresso maker", - "face powder", - "feather boa", - "file", - "fireboat", - "fire engine", - "fire screen", - "flagpole", - "flute", - "folding chair", - "football helmet", - "forklift", - "fountain", - "fountain pen", - "four-poster", - "freight car", - "French horn", - "frying pan", - "fur coat", - "garbage truck", - "gasmask", - "gas pump", - "goblet", - "go-kart", - "golf ball", - "golfcart", - "gondola", - "gong", - "gown", - "grand piano", - "greenhouse", - "grille", - "grocery store", - "guillotine", - "hair slide", - "hair spray", - "half track", - "hammer", - "hamper", - "hand blower", - "hand-held computer", - "handkerchief", - "hard disc", - "harmonica", - "harp", - "harvester", - "hatchet", - "holster", - "home theater", - "honeycomb", - "hook", - "hoopskirt", - "horizontal bar", - "horse cart", - "hourglass", - "iPod", - "iron", - "jack-o'-lantern", - "jean", - "jeep", - "jersey", - "jigsaw puzzle", - "jinrikisha", - "joystick", - "kimono", - "knee pad", - "knot", - "lab coat", - "ladle", - "lampshade", - "laptop", - "lawn mower", - "lens cap", - "letter opener", - "library", - "lifeboat", - "lighter", - "limousine", - "liner", - "lipstick", - "Loafer", - "lotion", - "loudspeaker", - "loupe", - "lumbermill", - "magnetic compass", - "mailbag", - "mailbox", - "maillot", - "maillot tank suit", - "manhole cover", - "maraca", - "marimba", - "mask", - "matchstick", - "maypole", - "maze", - "measuring cup", - "medicine chest", - "megalith", - "microphone", - "microwave", - "military uniform", - "milk can", - "minibus", - "miniskirt", - "minivan", - "missile", - "mitten", - "mixing bowl", - "mobile home", - "Model T", - "modem", - "monastery", - "monitor", - "moped", - "mortar", - "mortarboard", - "mosque", - "mosquito net", - "motor scooter", - "mountain bike", - "mountain tent", - "mouse", - "mousetrap", - "moving van", - "muzzle", - "nail", - "neck brace", - "necklace", - "nipple", - "notebook", - "obelisk", - "oboe", - "ocarina", - "odometer", - "oil filter", - "organ", - "oscilloscope", - "overskirt", - "oxcart", - "oxygen mask", - "packet", - "paddle", - "paddlewheel", - "padlock", - "paintbrush", - "pajama", - "palace", - "panpipe", - "paper towel", - "parachute", - "parallel bars", - "park bench", - "parking meter", - "passenger car", - "patio", - "pay-phone", - "pedestal", - "pencil box", - "pencil sharpener", - "perfume", - "Petri dish", - "photocopier", - "pick", - "pickelhaube", - "picket fence", - "pickup", - "pier", - "piggy bank", - "pill bottle", - "pillow", - "ping-pong ball", - "pinwheel", - "pirate", - "pitcher", - "plane", - "planetarium", - "plastic bag", - "plate rack", - "plow", - "plunger", - "Polaroid camera", - "pole", - "police van", - "poncho", - "pool table", - "pop bottle", - "pot", - "potter's wheel", - "power drill", - "prayer rug", - "printer", - "prison", - "projectile", - "projector", - "puck", - "punching bag", - "purse", - "quill", - "quilt", - "racer", - "racket", - "radiator", - "radio", - "radio telescope", - "rain barrel", - "recreational vehicle", - "reel", - "reflex camera", - "refrigerator", - "remote control", - "restaurant", - "revolver", - "rifle", - "rocking chair", - "rotisserie", - "rubber eraser", - "rugby ball", - "rule", - "running shoe", - "safe", - "safety pin", - "saltshaker", - "sandal", - "sarong", - "sax", - "scabbard", - "scale", - "school bus", - "schooner", - "scoreboard", - "screen", - "screw", - "screwdriver", - "seat belt", - "sewing machine", - "shield", - "shoe shop", - "shoji", - "shopping basket", - "shopping cart", - "shovel", - "shower cap", - "shower curtain", - "ski", - "ski mask", - "sleeping bag", - "slide rule", - "sliding door", - "slot", - "snorkel", - "snowmobile", - "snowplow", - "soap dispenser", - "soccer ball", - "sock", - "solar dish", - "sombrero", - "soup bowl", - "space bar", - "space heater", - "space shuttle", - "spatula", - "speedboat", - "spider web", - "spindle", - "sports car", - "spotlight", - "stage", - "steam locomotive", - "steel arch bridge", - "steel drum", - "stethoscope", - "stole", - "stone wall", - "stopwatch", - "stove", - "strainer", - "streetcar", - "stretcher", - "studio couch", - "stupa", - "submarine", - "suit", - "sundial", - "sunglass", - "sunglasses", - "sunscreen", - "suspension bridge", - "swab", - "sweatshirt", - "swimming trunks", - "swing", - "switch", - "syringe", - "table lamp", - "tank", - "tape player", - "teapot", - "teddy", - "television", - "tennis ball", - "thatch", - "theater curtain", - "thimble", - "thresher", - "throne", - "tile roof", - "toaster", - "tobacco shop", - "toilet seat", - "torch", - "totem pole", - "tow truck", - "toyshop", - "tractor", - "trailer truck", - "tray", - "trench coat", - "tricycle", - "trimaran", - "tripod", - "triumphal arch", - "trolleybus", - "trombone", - "tub", - "turnstile", - "typewriter keyboard", - "umbrella", - "unicycle", - "upright", - "vacuum", - "vase", - "vault", - "velvet", - "vending machine", - "vestment", - "viaduct", - "violin", - "volleyball", - "waffle iron", - "wall clock", - "wallet", - "wardrobe", - "warplane", - "washbasin", - "washer", - "water bottle", - "water jug", - "water tower", - "whiskey jug", - "whistle", - "wig", - "window screen", - "window shade", - "Windsor tie", - "wine bottle", - "wing", - "wok", - "wooden spoon", - "wool", - "worm fence", - "wreck", - "yawl", - "yurt", - "web site", - "comic book", - "crossword puzzle", - "street sign", - "traffic light", - "book jacket", - "menu", - "plate", - "guacamole", - "consomme", - "hot pot", - "trifle", - "ice cream", - "ice lolly", - "French loaf", - "bagel", - "pretzel", - "cheeseburger", - "hotdog", - "mashed potato", - "head cabbage", - "broccoli", - "cauliflower", - "zucchini", - "spaghetti squash", - "acorn squash", - "butternut squash", - "cucumber", - "artichoke", - "bell pepper", - "cardoon", - "mushroom", - "Granny Smith", - "strawberry", - "orange", - "lemon", - "fig", - "pineapple", - "banana", - "jackfruit", - "custard apple", - "pomegranate", - "hay", - "carbonara", - "chocolate sauce", - "dough", - "meat loaf", - "pizza", - "potpie", - "burrito", - "red wine", - "espresso", - "cup", - "eggnog", - "alp", - "bubble", - "cliff", - "coral reef", - "geyser", - "lakeside", - "promontory", - "sandbar", - "seashore", - "valley", - "volcano", - "ballplayer", - "groom", - "scuba diver", - "rapeseed", - "daisy", - "yellow lady's slipper", - "corn", - "acorn", - "hip", - "buckeye", - "coral fungus", - "agaric", - "gyromitra", - "stinkhorn", - "earthstar", - "hen-of-the-woods", - "bolete", - "ear", - "toilet tissue" -] diff --git a/models/mobilenetv3small-classifier.onnx b/models/mobilenetv3small-classifier.onnx deleted file mode 100644 index 7cf136c..0000000 --- a/models/mobilenetv3small-classifier.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ca63401f87d2e29f8c8d9f81942b51c94424bd37465b960e6552c9e8f068abf2 -size 10181063 diff --git a/models/ssdlite320_mobilenetv3_detector.onnx b/models/ssdlite320_mobilenetv3_detector.onnx deleted file mode 100644 index c3b265c..0000000 --- a/models/ssdlite320_mobilenetv3_detector.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c31e2e989eb12fb631db58e3e6ed77e5a98dc18c201b67923773ff0699c7dea2 -size 14116834 diff --git a/test/support/exvision/test_utils/mock_cache_server.ex b/test/support/exvision/test_utils/mock_cache_server.ex index 14ee1f1..058d945 100644 --- a/test/support/exvision/test_utils/mock_cache_server.ex +++ b/test/support/exvision/test_utils/mock_cache_server.ex @@ -1,24 +1,31 @@ defmodule ExVision.TestUtils.MockCacheServer do @moduledoc false - # It will add a setup step that will mock all calls to Req, eliminating the need to host the files during testing - defmacro __using__(_opts) do + require Logger + + defp get_server() do quote do use Mimic setup_all do - stub(Req, :get, fn - %URI{path: path}, _options -> - file = Path.join("models", path) + if File.exists?("models") do + stub(Req, :get, fn + %URI{path: path}, _options -> + file = Path.join("models", path) - if File.exists?(file), - do: {:ok, %Req.Response{status: 200, body: File.read!(path)}}, - else: {:ok, %Req.Response{status: 404}} - end) + if File.exists?(file), + do: {:ok, %Req.Response{status: 200, body: File.read!(path)}}, + else: {:ok, %Req.Response{status: 404}} + end) + end :ok end end end + + defmacro __using__(_opts) do + get_server() + end end From d1013558d73d78bdfb0a05baa95b1eab4f223876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jod=C5=82os=CC=81?= Date: Fri, 24 May 2024 09:57:14 +0200 Subject: [PATCH 2/7] Fix test cases --- .github/workflows/release.yml | 21 ------------- .gitignore | 1 + test/support/exvision/model/case.ex | 2 +- .../exvision/test_utils/mock_cache_server.ex | 31 ------------------- 4 files changed, 2 insertions(+), 53 deletions(-) delete mode 100644 .github/workflows/release.yml delete mode 100644 test/support/exvision/test_utils/mock_cache_server.ex diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 3d4a34e..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Release package - -on: - push: - tags: - - "v*" - -jobs: - release: - permissions: "write-all" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: nschloe/action-cached-lfs-checkout@v1.1.2 - - uses: "marvinpinto/action-automatic-releases@latest" - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "latest" - prerelease: true - title: "Release ${{ github.ref }}" - files: models/* diff --git a/.gitignore b/.gitignore index 324f6fa..70bb34c 100644 --- a/.gitignore +++ b/.gitignore @@ -177,3 +177,4 @@ $RECYCLE.BIN/ *.lnk # End of https://www.gitignore.io/api/c,vim,linux,macos,elixir,windows,visualstudiocode +models/ diff --git a/test/support/exvision/model/case.ex b/test/support/exvision/model/case.ex index 8d0f837..7cc4ef5 100644 --- a/test/support/exvision/model/case.ex +++ b/test/support/exvision/model/case.ex @@ -9,7 +9,7 @@ defmodule ExVision.Model.Case do quote do use ExUnit.Case, async: true - use ExVision.TestUtils.MockCacheServer + # use ExVision.TestUtils.MockCacheServer @behaviour ExVision.Model.Case setup_all do diff --git a/test/support/exvision/test_utils/mock_cache_server.ex b/test/support/exvision/test_utils/mock_cache_server.ex deleted file mode 100644 index 058d945..0000000 --- a/test/support/exvision/test_utils/mock_cache_server.ex +++ /dev/null @@ -1,31 +0,0 @@ -defmodule ExVision.TestUtils.MockCacheServer do - @moduledoc false - # It will add a setup step that will mock all calls to Req, eliminating the need to host the files during testing - - require Logger - - defp get_server() do - quote do - use Mimic - - setup_all do - if File.exists?("models") do - stub(Req, :get, fn - %URI{path: path}, _options -> - file = Path.join("models", path) - - if File.exists?(file), - do: {:ok, %Req.Response{status: 200, body: File.read!(path)}}, - else: {:ok, %Req.Response{status: 404}} - end) - end - - :ok - end - end - end - - defmacro __using__(_opts) do - get_server() - end -end From e528ea4250214e1abd63ea8370f4fccae4c3553d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jod=C5=82os=CC=81?= Date: Mon, 27 May 2024 06:00:25 +0200 Subject: [PATCH 3/7] Remove categories from downloadable assets This should fix the tests and resolve the issue of the internet being required for the compilation --- assets/categories/coco_categories.json | 93 ++ .../coco_with_voc_labels_categories.json | 23 + assets/categories/imagenet_v2_categories.json | 1002 +++++++++++++++++ lib/ex_vision/cache.ex | 40 + .../classification/mobilenet_v3_small.ex | 2 +- .../detection/fasterrcnn_resnet50_fpn.ex | 2 +- .../detection/ssdlite320_mobilenetv3.ex | 2 +- .../model/definition/parts/with_categories.ex | 17 +- .../segmentation/deep_lab_v3_mobilenet_v3.ex | 2 +- 9 files changed, 1164 insertions(+), 19 deletions(-) create mode 100644 assets/categories/coco_categories.json create mode 100644 assets/categories/coco_with_voc_labels_categories.json create mode 100644 assets/categories/imagenet_v2_categories.json diff --git a/assets/categories/coco_categories.json b/assets/categories/coco_categories.json new file mode 100644 index 0000000..7978e7e --- /dev/null +++ b/assets/categories/coco_categories.json @@ -0,0 +1,93 @@ +[ + "__background__", + "person", + "bicycle", + "car", + "motorcycle", + "airplane", + "bus", + "train", + "truck", + "boat", + "traffic light", + "fire hydrant", + "N/A", + "stop sign", + "parking meter", + "bench", + "bird", + "cat", + "dog", + "horse", + "sheep", + "cow", + "elephant", + "bear", + "zebra", + "giraffe", + "N/A", + "backpack", + "umbrella", + "N/A", + "N/A", + "handbag", + "tie", + "suitcase", + "frisbee", + "skis", + "snowboard", + "sports ball", + "kite", + "baseball bat", + "baseball glove", + "skateboard", + "surfboard", + "tennis racket", + "bottle", + "N/A", + "wine glass", + "cup", + "fork", + "knife", + "spoon", + "bowl", + "banana", + "apple", + "sandwich", + "orange", + "broccoli", + "carrot", + "hot dog", + "pizza", + "donut", + "cake", + "chair", + "couch", + "potted plant", + "bed", + "N/A", + "dining table", + "N/A", + "N/A", + "toilet", + "N/A", + "tv", + "laptop", + "mouse", + "remote", + "keyboard", + "cell phone", + "microwave", + "oven", + "toaster", + "sink", + "refrigerator", + "N/A", + "book", + "clock", + "vase", + "scissors", + "teddy bear", + "hair drier", + "toothbrush" +] diff --git a/assets/categories/coco_with_voc_labels_categories.json b/assets/categories/coco_with_voc_labels_categories.json new file mode 100644 index 0000000..63ee1cb --- /dev/null +++ b/assets/categories/coco_with_voc_labels_categories.json @@ -0,0 +1,23 @@ +[ + "__background__", + "aeroplane", + "bicycle", + "bird", + "boat", + "bottle", + "bus", + "car", + "cat", + "chair", + "cow", + "diningtable", + "dog", + "horse", + "motorbike", + "person", + "pottedplant", + "sheep", + "sofa", + "train", + "tvmonitor" +] diff --git a/assets/categories/imagenet_v2_categories.json b/assets/categories/imagenet_v2_categories.json new file mode 100644 index 0000000..88cde3e --- /dev/null +++ b/assets/categories/imagenet_v2_categories.json @@ -0,0 +1,1002 @@ +[ + "tench", + "goldfish", + "great white shark", + "tiger shark", + "hammerhead", + "electric ray", + "stingray", + "cock", + "hen", + "ostrich", + "brambling", + "goldfinch", + "house finch", + "junco", + "indigo bunting", + "robin", + "bulbul", + "jay", + "magpie", + "chickadee", + "water ouzel", + "kite", + "bald eagle", + "vulture", + "great grey owl", + "European fire salamander", + "common newt", + "eft", + "spotted salamander", + "axolotl", + "bullfrog", + "tree frog", + "tailed frog", + "loggerhead", + "leatherback turtle", + "mud turtle", + "terrapin", + "box turtle", + "banded gecko", + "common iguana", + "American chameleon", + "whiptail", + "agama", + "frilled lizard", + "alligator lizard", + "Gila monster", + "green lizard", + "African chameleon", + "Komodo dragon", + "African crocodile", + "American alligator", + "triceratops", + "thunder snake", + "ringneck snake", + "hognose snake", + "green snake", + "king snake", + "garter snake", + "water snake", + "vine snake", + "night snake", + "boa constrictor", + "rock python", + "Indian cobra", + "green mamba", + "sea snake", + "horned viper", + "diamondback", + "sidewinder", + "trilobite", + "harvestman", + "scorpion", + "black and gold garden spider", + "barn spider", + "garden spider", + "black widow", + "tarantula", + "wolf spider", + "tick", + "centipede", + "black grouse", + "ptarmigan", + "ruffed grouse", + "prairie chicken", + "peacock", + "quail", + "partridge", + "African grey", + "macaw", + "sulphur-crested cockatoo", + "lorikeet", + "coucal", + "bee eater", + "hornbill", + "hummingbird", + "jacamar", + "toucan", + "drake", + "red-breasted merganser", + "goose", + "black swan", + "tusker", + "echidna", + "platypus", + "wallaby", + "koala", + "wombat", + "jellyfish", + "sea anemone", + "brain coral", + "flatworm", + "nematode", + "conch", + "snail", + "slug", + "sea slug", + "chiton", + "chambered nautilus", + "Dungeness crab", + "rock crab", + "fiddler crab", + "king crab", + "American lobster", + "spiny lobster", + "crayfish", + "hermit crab", + "isopod", + "white stork", + "black stork", + "spoonbill", + "flamingo", + "little blue heron", + "American egret", + "bittern", + "crane bird", + "limpkin", + "European gallinule", + "American coot", + "bustard", + "ruddy turnstone", + "red-backed sandpiper", + "redshank", + "dowitcher", + "oystercatcher", + "pelican", + "king penguin", + "albatross", + "grey whale", + "killer whale", + "dugong", + "sea lion", + "Chihuahua", + "Japanese spaniel", + "Maltese dog", + "Pekinese", + "Shih-Tzu", + "Blenheim spaniel", + "papillon", + "toy terrier", + "Rhodesian ridgeback", + "Afghan hound", + "basset", + "beagle", + "bloodhound", + "bluetick", + "black-and-tan coonhound", + "Walker hound", + "English foxhound", + "redbone", + "borzoi", + "Irish wolfhound", + "Italian greyhound", + "whippet", + "Ibizan hound", + "Norwegian elkhound", + "otterhound", + "Saluki", + "Scottish deerhound", + "Weimaraner", + "Staffordshire bullterrier", + "American Staffordshire terrier", + "Bedlington terrier", + "Border terrier", + "Kerry blue terrier", + "Irish terrier", + "Norfolk terrier", + "Norwich terrier", + "Yorkshire terrier", + "wire-haired fox terrier", + "Lakeland terrier", + "Sealyham terrier", + "Airedale", + "cairn", + "Australian terrier", + "Dandie Dinmont", + "Boston bull", + "miniature schnauzer", + "giant schnauzer", + "standard schnauzer", + "Scotch terrier", + "Tibetan terrier", + "silky terrier", + "soft-coated wheaten terrier", + "West Highland white terrier", + "Lhasa", + "flat-coated retriever", + "curly-coated retriever", + "golden retriever", + "Labrador retriever", + "Chesapeake Bay retriever", + "German short-haired pointer", + "vizsla", + "English setter", + "Irish setter", + "Gordon setter", + "Brittany spaniel", + "clumber", + "English springer", + "Welsh springer spaniel", + "cocker spaniel", + "Sussex spaniel", + "Irish water spaniel", + "kuvasz", + "schipperke", + "groenendael", + "malinois", + "briard", + "kelpie", + "komondor", + "Old English sheepdog", + "Shetland sheepdog", + "collie", + "Border collie", + "Bouvier des Flandres", + "Rottweiler", + "German shepherd", + "Doberman", + "miniature pinscher", + "Greater Swiss Mountain dog", + "Bernese mountain dog", + "Appenzeller", + "EntleBucher", + "boxer", + "bull mastiff", + "Tibetan mastiff", + "French bulldog", + "Great Dane", + "Saint Bernard", + "Eskimo dog", + "malamute", + "Siberian husky", + "dalmatian", + "affenpinscher", + "basenji", + "pug", + "Leonberg", + "Newfoundland", + "Great Pyrenees", + "Samoyed", + "Pomeranian", + "chow", + "keeshond", + "Brabancon griffon", + "Pembroke", + "Cardigan", + "toy poodle", + "miniature poodle", + "standard poodle", + "Mexican hairless", + "timber wolf", + "white wolf", + "red wolf", + "coyote", + "dingo", + "dhole", + "African hunting dog", + "hyena", + "red fox", + "kit fox", + "Arctic fox", + "grey fox", + "tabby", + "tiger cat", + "Persian cat", + "Siamese cat", + "Egyptian cat", + "cougar", + "lynx", + "leopard", + "snow leopard", + "jaguar", + "lion", + "tiger", + "cheetah", + "brown bear", + "American black bear", + "ice bear", + "sloth bear", + "mongoose", + "meerkat", + "tiger beetle", + "ladybug", + "ground beetle", + "long-horned beetle", + "leaf beetle", + "dung beetle", + "rhinoceros beetle", + "weevil", + "fly", + "bee", + "ant", + "grasshopper", + "cricket", + "walking stick", + "cockroach", + "mantis", + "cicada", + "leafhopper", + "lacewing", + "dragonfly", + "damselfly", + "admiral", + "ringlet", + "monarch", + "cabbage butterfly", + "sulphur butterfly", + "lycaenid", + "starfish", + "sea urchin", + "sea cucumber", + "wood rabbit", + "hare", + "Angora", + "hamster", + "porcupine", + "fox squirrel", + "marmot", + "beaver", + "guinea pig", + "sorrel", + "zebra", + "hog", + "wild boar", + "warthog", + "hippopotamus", + "ox", + "water buffalo", + "bison", + "ram", + "bighorn", + "ibex", + "hartebeest", + "impala", + "gazelle", + "Arabian camel", + "llama", + "weasel", + "mink", + "polecat", + "black-footed ferret", + "otter", + "skunk", + "badger", + "armadillo", + "three-toed sloth", + "orangutan", + "gorilla", + "chimpanzee", + "gibbon", + "siamang", + "guenon", + "patas", + "baboon", + "macaque", + "langur", + "colobus", + "proboscis monkey", + "marmoset", + "capuchin", + "howler monkey", + "titi", + "spider monkey", + "squirrel monkey", + "Madagascar cat", + "indri", + "Indian elephant", + "African elephant", + "lesser panda", + "giant panda", + "barracouta", + "eel", + "coho", + "rock beauty", + "anemone fish", + "sturgeon", + "gar", + "lionfish", + "puffer", + "abacus", + "abaya", + "academic gown", + "accordion", + "acoustic guitar", + "aircraft carrier", + "airliner", + "airship", + "altar", + "ambulance", + "amphibian", + "analog clock", + "apiary", + "apron", + "ashcan", + "assault rifle", + "backpack", + "bakery", + "balance beam", + "balloon", + "ballpoint", + "Band Aid", + "banjo", + "bannister", + "barbell", + "barber chair", + "barbershop", + "barn", + "barometer", + "barrel", + "barrow", + "baseball", + "basketball", + "bassinet", + "bassoon", + "bathing cap", + "bath towel", + "bathtub", + "beach wagon", + "beacon", + "beaker", + "bearskin", + "beer bottle", + "beer glass", + "bell cote", + "bib", + "bicycle-built-for-two", + "bikini", + "binder", + "binoculars", + "birdhouse", + "boathouse", + "bobsled", + "bolo tie", + "bonnet", + "bookcase", + "bookshop", + "bottlecap", + "bow", + "bow tie", + "brass", + "brassiere", + "breakwater", + "breastplate", + "broom", + "bucket", + "buckle", + "bulletproof vest", + "bullet train", + "butcher shop", + "cab", + "caldron", + "candle", + "cannon", + "canoe", + "can opener", + "cardigan", + "car mirror", + "carousel", + "carpenter's kit", + "carton", + "car wheel", + "cash machine", + "cassette", + "cassette player", + "castle", + "catamaran", + "CD player", + "cello", + "cellular telephone", + "chain", + "chainlink fence", + "chain mail", + "chain saw", + "chest", + "chiffonier", + "chime", + "china cabinet", + "Christmas stocking", + "church", + "cinema", + "cleaver", + "cliff dwelling", + "cloak", + "clog", + "cocktail shaker", + "coffee mug", + "coffeepot", + "coil", + "combination lock", + "computer keyboard", + "confectionery", + "container ship", + "convertible", + "corkscrew", + "cornet", + "cowboy boot", + "cowboy hat", + "cradle", + "crane", + "crash helmet", + "crate", + "crib", + "Crock Pot", + "croquet ball", + "crutch", + "cuirass", + "dam", + "desk", + "desktop computer", + "dial telephone", + "diaper", + "digital clock", + "digital watch", + "dining table", + "dishrag", + "dishwasher", + "disk brake", + "dock", + "dogsled", + "dome", + "doormat", + "drilling platform", + "drum", + "drumstick", + "dumbbell", + "Dutch oven", + "electric fan", + "electric guitar", + "electric locomotive", + "entertainment center", + "envelope", + "espresso maker", + "face powder", + "feather boa", + "file", + "fireboat", + "fire engine", + "fire screen", + "flagpole", + "flute", + "folding chair", + "football helmet", + "forklift", + "fountain", + "fountain pen", + "four-poster", + "freight car", + "French horn", + "frying pan", + "fur coat", + "garbage truck", + "gasmask", + "gas pump", + "goblet", + "go-kart", + "golf ball", + "golfcart", + "gondola", + "gong", + "gown", + "grand piano", + "greenhouse", + "grille", + "grocery store", + "guillotine", + "hair slide", + "hair spray", + "half track", + "hammer", + "hamper", + "hand blower", + "hand-held computer", + "handkerchief", + "hard disc", + "harmonica", + "harp", + "harvester", + "hatchet", + "holster", + "home theater", + "honeycomb", + "hook", + "hoopskirt", + "horizontal bar", + "horse cart", + "hourglass", + "iPod", + "iron", + "jack-o'-lantern", + "jean", + "jeep", + "jersey", + "jigsaw puzzle", + "jinrikisha", + "joystick", + "kimono", + "knee pad", + "knot", + "lab coat", + "ladle", + "lampshade", + "laptop", + "lawn mower", + "lens cap", + "letter opener", + "library", + "lifeboat", + "lighter", + "limousine", + "liner", + "lipstick", + "Loafer", + "lotion", + "loudspeaker", + "loupe", + "lumbermill", + "magnetic compass", + "mailbag", + "mailbox", + "maillot", + "maillot tank suit", + "manhole cover", + "maraca", + "marimba", + "mask", + "matchstick", + "maypole", + "maze", + "measuring cup", + "medicine chest", + "megalith", + "microphone", + "microwave", + "military uniform", + "milk can", + "minibus", + "miniskirt", + "minivan", + "missile", + "mitten", + "mixing bowl", + "mobile home", + "Model T", + "modem", + "monastery", + "monitor", + "moped", + "mortar", + "mortarboard", + "mosque", + "mosquito net", + "motor scooter", + "mountain bike", + "mountain tent", + "mouse", + "mousetrap", + "moving van", + "muzzle", + "nail", + "neck brace", + "necklace", + "nipple", + "notebook", + "obelisk", + "oboe", + "ocarina", + "odometer", + "oil filter", + "organ", + "oscilloscope", + "overskirt", + "oxcart", + "oxygen mask", + "packet", + "paddle", + "paddlewheel", + "padlock", + "paintbrush", + "pajama", + "palace", + "panpipe", + "paper towel", + "parachute", + "parallel bars", + "park bench", + "parking meter", + "passenger car", + "patio", + "pay-phone", + "pedestal", + "pencil box", + "pencil sharpener", + "perfume", + "Petri dish", + "photocopier", + "pick", + "pickelhaube", + "picket fence", + "pickup", + "pier", + "piggy bank", + "pill bottle", + "pillow", + "ping-pong ball", + "pinwheel", + "pirate", + "pitcher", + "plane", + "planetarium", + "plastic bag", + "plate rack", + "plow", + "plunger", + "Polaroid camera", + "pole", + "police van", + "poncho", + "pool table", + "pop bottle", + "pot", + "potter's wheel", + "power drill", + "prayer rug", + "printer", + "prison", + "projectile", + "projector", + "puck", + "punching bag", + "purse", + "quill", + "quilt", + "racer", + "racket", + "radiator", + "radio", + "radio telescope", + "rain barrel", + "recreational vehicle", + "reel", + "reflex camera", + "refrigerator", + "remote control", + "restaurant", + "revolver", + "rifle", + "rocking chair", + "rotisserie", + "rubber eraser", + "rugby ball", + "rule", + "running shoe", + "safe", + "safety pin", + "saltshaker", + "sandal", + "sarong", + "sax", + "scabbard", + "scale", + "school bus", + "schooner", + "scoreboard", + "screen", + "screw", + "screwdriver", + "seat belt", + "sewing machine", + "shield", + "shoe shop", + "shoji", + "shopping basket", + "shopping cart", + "shovel", + "shower cap", + "shower curtain", + "ski", + "ski mask", + "sleeping bag", + "slide rule", + "sliding door", + "slot", + "snorkel", + "snowmobile", + "snowplow", + "soap dispenser", + "soccer ball", + "sock", + "solar dish", + "sombrero", + "soup bowl", + "space bar", + "space heater", + "space shuttle", + "spatula", + "speedboat", + "spider web", + "spindle", + "sports car", + "spotlight", + "stage", + "steam locomotive", + "steel arch bridge", + "steel drum", + "stethoscope", + "stole", + "stone wall", + "stopwatch", + "stove", + "strainer", + "streetcar", + "stretcher", + "studio couch", + "stupa", + "submarine", + "suit", + "sundial", + "sunglass", + "sunglasses", + "sunscreen", + "suspension bridge", + "swab", + "sweatshirt", + "swimming trunks", + "swing", + "switch", + "syringe", + "table lamp", + "tank", + "tape player", + "teapot", + "teddy", + "television", + "tennis ball", + "thatch", + "theater curtain", + "thimble", + "thresher", + "throne", + "tile roof", + "toaster", + "tobacco shop", + "toilet seat", + "torch", + "totem pole", + "tow truck", + "toyshop", + "tractor", + "trailer truck", + "tray", + "trench coat", + "tricycle", + "trimaran", + "tripod", + "triumphal arch", + "trolleybus", + "trombone", + "tub", + "turnstile", + "typewriter keyboard", + "umbrella", + "unicycle", + "upright", + "vacuum", + "vase", + "vault", + "velvet", + "vending machine", + "vestment", + "viaduct", + "violin", + "volleyball", + "waffle iron", + "wall clock", + "wallet", + "wardrobe", + "warplane", + "washbasin", + "washer", + "water bottle", + "water jug", + "water tower", + "whiskey jug", + "whistle", + "wig", + "window screen", + "window shade", + "Windsor tie", + "wine bottle", + "wing", + "wok", + "wooden spoon", + "wool", + "worm fence", + "wreck", + "yawl", + "yurt", + "web site", + "comic book", + "crossword puzzle", + "street sign", + "traffic light", + "book jacket", + "menu", + "plate", + "guacamole", + "consomme", + "hot pot", + "trifle", + "ice cream", + "ice lolly", + "French loaf", + "bagel", + "pretzel", + "cheeseburger", + "hotdog", + "mashed potato", + "head cabbage", + "broccoli", + "cauliflower", + "zucchini", + "spaghetti squash", + "acorn squash", + "butternut squash", + "cucumber", + "artichoke", + "bell pepper", + "cardoon", + "mushroom", + "Granny Smith", + "strawberry", + "orange", + "lemon", + "fig", + "pineapple", + "banana", + "jackfruit", + "custard apple", + "pomegranate", + "hay", + "carbonara", + "chocolate sauce", + "dough", + "meat loaf", + "pizza", + "potpie", + "burrito", + "red wine", + "espresso", + "cup", + "eggnog", + "alp", + "bubble", + "cliff", + "coral reef", + "geyser", + "lakeside", + "promontory", + "sandbar", + "seashore", + "valley", + "volcano", + "ballplayer", + "groom", + "scuba diver", + "rapeseed", + "daisy", + "yellow lady's slipper", + "corn", + "acorn", + "hip", + "buckeye", + "coral fungus", + "agaric", + "gyromitra", + "stinkhorn", + "earthstar", + "hen-of-the-woods", + "bolete", + "ear", + "toilet tissue" +] diff --git a/lib/ex_vision/cache.ex b/lib/ex_vision/cache.ex index e482e93..90092dc 100644 --- a/lib/ex_vision/cache.ex +++ b/lib/ex_vision/cache.ex @@ -1,10 +1,13 @@ defmodule ExVision.Cache do @moduledoc false + alias ExVision.Cache.PubSub # Module responsible for handling model file caching require Logger + alias ExVision.Cache.PubSub + @default_cache_path Application.compile_env(:ex_vision, :cache_path, "/tmp/ex_vision/cache") defp get_cache_path() do Application.get_env(:ex_vision, :cache_path, @default_cache_path) @@ -22,6 +25,11 @@ defmodule ExVision.Cache do @type lazy_get_option_t() :: {:cache_path, Path.t()} | {:server_url, String.t() | URI.t()} | {:force, boolean()} + @spec child_spec(keyword()) + def child_spec(_opts) do + Registry.child_spec(name: __MODULE__) + end + @doc """ Lazily evaluate the path from the cache directory. It will only download the file if it's missing or the `force: true` option is given. @@ -50,6 +58,16 @@ defmodule ExVision.Cache do end end + defp create_download_job(url, cache_path) do + key = {url, cache_path} + + spawn(fn -> + PubSub.notify(__MODULE__, key, download_file(url, cache_path)) + end) + + PubSub.subscribe(__MODULE__, key) + end + @spec download_file(URI.t(), Path.t()) :: {:ok, Path.t()} | {:error, reason :: any()} defp download_file(url, cache_path) do @@ -109,3 +127,25 @@ defmodule ExVision.Cache do defp ensure_backslash("/" <> _rest = path), do: path defp ensure_backslash(path), do: "/" <> path end + +defmodule ExVision.Cache.PubSub do + @moduledoc false + + @spec subscribe(term(), Path.t()) :: :ok + def subscribe(registry, key) do + Registry.register(registry, key, []) + + receive do + {^registry, :notification, result} -> + Registry.unregister(registry, key) + result + end + end + + @spec notify(term(), Path.t()) :: :ok + def notify(registry, key, result) do + Registry.dispatch(registry, key, fn entries -> + for {pid, _value} <- entries, do: send(pid, {registry, :notification, result}) + end) + end +end diff --git a/lib/ex_vision/classification/mobilenet_v3_small.ex b/lib/ex_vision/classification/mobilenet_v3_small.ex index 3375f37..5f4358c 100644 --- a/lib/ex_vision/classification/mobilenet_v3_small.ex +++ b/lib/ex_vision/classification/mobilenet_v3_small.ex @@ -6,7 +6,7 @@ defmodule ExVision.Classification.MobileNetV3Small do """ use ExVision.Model.Definition.Ortex, model: "mobilenetv3small-classifier.onnx", - categories: "imagenet_v2_categories.json" + categories: "assets/categories/imagenet_v2_categories.json" require Bunch.Typespec alias ExVision.Utils diff --git a/lib/ex_vision/detection/fasterrcnn_resnet50_fpn.ex b/lib/ex_vision/detection/fasterrcnn_resnet50_fpn.ex index 162b8e2..0476b15 100644 --- a/lib/ex_vision/detection/fasterrcnn_resnet50_fpn.ex +++ b/lib/ex_vision/detection/fasterrcnn_resnet50_fpn.ex @@ -4,7 +4,7 @@ defmodule ExVision.Detection.FasterRCNN_ResNet50_FPN do """ use ExVision.Model.Definition.Ortex, model: "fasterrcnn_resnet50_fpn_detector.onnx", - categories: "coco_categories.json" + categories: "assets/categories/coco_categories.json" use ExVision.Detection.GenericDetector diff --git a/lib/ex_vision/detection/ssdlite320_mobilenetv3.ex b/lib/ex_vision/detection/ssdlite320_mobilenetv3.ex index 492522a..043ddb1 100644 --- a/lib/ex_vision/detection/ssdlite320_mobilenetv3.ex +++ b/lib/ex_vision/detection/ssdlite320_mobilenetv3.ex @@ -4,7 +4,7 @@ defmodule ExVision.Detection.Ssdlite320_MobileNetv3 do """ use ExVision.Model.Definition.Ortex, model: "ssdlite320_mobilenetv3_detector.onnx", - categories: "coco_categories.json" + categories: "assets/categories/coco_categories.json" use ExVision.Detection.GenericDetector diff --git a/lib/ex_vision/model/definition/parts/with_categories.ex b/lib/ex_vision/model/definition/parts/with_categories.ex index ae91809..81d552b 100644 --- a/lib/ex_vision/model/definition/parts/with_categories.ex +++ b/lib/ex_vision/model/definition/parts/with_categories.ex @@ -1,24 +1,11 @@ defmodule ExVision.Model.Definition.Parts.WithCategories do @moduledoc false require Logger - alias ExVision.{Cache, Utils} - - defp get_categories(file) do - file - |> Cache.lazy_get() - |> case do - {:ok, file} -> - Utils.load_categories(file) - - error -> - Logger.error("Failed to load categories from #{file} due to #{inspect(error)}") - raise "Failed to load categories from #{file}" - end - end + alias ExVision.Utils defmacro __using__(options) do options = Keyword.validate!(options, [:name, :categories]) - categories = options |> Keyword.fetch!(:categories) |> get_categories() + categories = options |> Keyword.fetch!(:categories) |> Utils.load_categories() spec = categories |> Enum.uniq() |> Bunch.Typespec.enum_to_alternative() quote do diff --git a/lib/ex_vision/segmentation/deep_lab_v3_mobilenet_v3.ex b/lib/ex_vision/segmentation/deep_lab_v3_mobilenet_v3.ex index 1bc0a07..c2a6735 100644 --- a/lib/ex_vision/segmentation/deep_lab_v3_mobilenet_v3.ex +++ b/lib/ex_vision/segmentation/deep_lab_v3_mobilenet_v3.ex @@ -4,7 +4,7 @@ defmodule ExVision.Segmentation.DeepLabV3_MobileNetV3 do """ use ExVision.Model.Definition.Ortex, model: "deeplab_v3_mobilenetv3_segmentation.onnx", - categories: "coco_with_voc_labels_categories.json" + categories: "assets/categories/coco_with_voc_labels_categories.json" @type output_t() :: %{category_t() => Nx.Tensor.t()} From e42d52bc1519cacf5fd4b88b4dcb7fbdf031b760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jod=C5=82os=CC=81?= Date: Mon, 27 May 2024 10:05:45 +0200 Subject: [PATCH 4/7] Reimplement the cache management to use the process workflow This way, we avoid race conditions related to trying to download the same file twice, when we request it before the previous download has finished. --- lib/ex_vision/cache.ex | 182 ++++++++++++++---------- lib/ex_vision/ex_vision.ex | 10 ++ lib/ex_vision/model/definition/ortex.ex | 8 +- mix.exs | 2 + test/ex_vision/cache_test.exs | 44 +++--- test/support/exvision/model/case.ex | 9 +- 6 files changed, 149 insertions(+), 106 deletions(-) create mode 100644 lib/ex_vision/ex_vision.ex diff --git a/lib/ex_vision/cache.ex b/lib/ex_vision/cache.ex index 90092dc..8361472 100644 --- a/lib/ex_vision/cache.ex +++ b/lib/ex_vision/cache.ex @@ -1,71 +1,129 @@ defmodule ExVision.Cache do @moduledoc false - alias ExVision.Cache.PubSub - # Module responsible for handling model file caching + use GenServer require Logger - alias ExVision.Cache.PubSub + @type lazy_get_option_t() :: {:force, boolean()} - @default_cache_path Application.compile_env(:ex_vision, :cache_path, "/tmp/ex_vision/cache") - defp get_cache_path() do - Application.get_env(:ex_vision, :cache_path, @default_cache_path) + @doc """ + Lazily evaluate the path from the cache directory. + It will only download the file if it's missing or the `force: true` option is given. + """ + @spec lazy_get(term() | pid(), Path.t(), options :: [lazy_get_option_t()]) :: + {:ok, Path.t()} | {:error, reason :: atom()} + def lazy_get(server, path, options \\ []) do + with {:ok, options} <- Keyword.validate(options, force: false), + do: GenServer.call(server, {:download, path, options}, :infinity) end - @default_server_url Application.compile_env( - :ex_vision, - :server_url, - URI.new!("https://ai.swmansion.com/exvision/files") - ) - defp get_server_url() do - Application.get_env(:ex_vision, :server_url, @default_server_url) + @spec start_link(keyword()) :: GenServer.on_start() + def start_link(opts) do + {init_args, opts} = Keyword.split(opts, [:server_url, :cache_path]) + GenServer.start_link(__MODULE__, init_args, opts) end - @type lazy_get_option_t() :: - {:cache_path, Path.t()} | {:server_url, String.t() | URI.t()} | {:force, boolean()} + @impl true + def init(opts) do + opts = Keyword.validate!(opts, cache_path: get_cache_path(), server_url: get_server_url()) + + with {:ok, server_url} <- URI.new(opts[:server_url]), + :ok <- File.mkdir_p(opts[:cache_path]) do + {:ok, + %{ + downloads: %{}, + server_url: server_url, + cache_path: opts[:cache_path], + refs: %{} + }} + end + end + + @impl true + def handle_call({:download, cache_path, options}, from, state) do + file_path = Path.join(state.cache_path, cache_path) + + updated_downloads = + Map.update(state.downloads, cache_path, MapSet.new([from]), &MapSet.put(&1, from)) + + cond do + Map.has_key?(state.downloads, cache_path) -> + {:noreply, %{state | downloads: updated_downloads}} - @spec child_spec(keyword()) - def child_spec(_opts) do - Registry.child_spec(name: __MODULE__) + File.exists?(file_path) or options[:force] -> + {:reply, {:ok, file_path}, state} + + true -> + ref = do_create_download_job(cache_path, state) + + {:noreply, + %{state | downloads: updated_downloads, refs: Map.put(state.refs, ref, cache_path)}} + end end - @doc """ - Lazily evaluate the path from the cache directory. - It will only download the file if it's missing or the `force: true` option is given. - """ - @spec lazy_get(Path.t(), options :: [lazy_get_option_t()]) :: - {:ok, Path.t()} | {:error, reason :: atom()} - def lazy_get(path, options \\ []) do - options = - Keyword.validate!(options, - cache_path: get_cache_path(), - server_url: get_server_url(), - force: false - ) - - cache_path = Path.join(options[:cache_path], path) - ok? = File.exists?(cache_path) - - if ok? and not options[:force] do - Logger.debug("Found existing cache entry for #{path}. Loading.") - {:ok, cache_path} - else - with {:ok, server_url} <- URI.new(options[:server_url]) do - download_url = URI.append_path(server_url, ensure_backslash(path)) - download_file(download_url, cache_path) + @impl true + def handle_info({ref, result}, state) do + Logger.info("Task #{inspect(ref)} finished with #{inspect(result)}") + state = emit(result, ref, state) + {:noreply, state} + end + + @impl true + def handle_info({:DOWN, ref, :process, _pid, reason}, state) do + state = + if reason != :normal do + Logger.error("Task #{inspect(ref)} has crashed due to #{inspect(reason)}") + emit({:error, reason}, ref, state) + else + state end - end + + {:noreply, state} + end + + @impl true + def handle_info(msg, state) do + Logger.warning("Received an unknown message #{inspect(msg)}. Ignoring") + {:noreply, state} end - defp create_download_job(url, cache_path) do - key = {url, cache_path} + defp emit(message, ref, state) do + path = state.refs[ref] - spawn(fn -> - PubSub.notify(__MODULE__, key, download_file(url, cache_path)) + state.downloads + |> Map.get(path, []) + |> Enum.each(fn from -> + GenServer.reply(from, message) end) - PubSub.subscribe(__MODULE__, key) + %{state | refs: Map.delete(state.refs, ref), downloads: Map.delete(state.downloads, path)} + end + + defp do_create_download_job(path, %{server_url: server_url, cache_path: cache_path}) do + target_file_path = Path.join(cache_path, path) + download_url = URI.append_path(server_url, ensure_backslash(path)) + + %Task{ref: ref} = + Task.async(fn -> + download_file(download_url, target_file_path) + end) + + ref + end + + @default_cache_path Application.compile_env(:ex_vision, :cache_path, "/tmp/ex_vision/cache") + defp get_cache_path() do + Application.get_env(:ex_vision, :cache_path, @default_cache_path) + end + + @default_server_url Application.compile_env( + :ex_vision, + :server_url, + URI.new!("https://ai.swmansion.com/exvision/files") + ) + defp get_server_url() do + Application.get_env(:ex_vision, :server_url, @default_server_url) end @spec download_file(URI.t(), Path.t()) :: @@ -81,6 +139,9 @@ defmodule ExVision.Cache do end end + defp ensure_backslash("/" <> _rest = i), do: i + defp ensure_backslash(i), do: "/" <> i + defp validate_download(path) do if File.exists?(path), do: :ok, @@ -123,29 +184,4 @@ defmodule ExVision.Cache do {:error, :connection_failed} end end - - defp ensure_backslash("/" <> _rest = path), do: path - defp ensure_backslash(path), do: "/" <> path -end - -defmodule ExVision.Cache.PubSub do - @moduledoc false - - @spec subscribe(term(), Path.t()) :: :ok - def subscribe(registry, key) do - Registry.register(registry, key, []) - - receive do - {^registry, :notification, result} -> - Registry.unregister(registry, key) - result - end - end - - @spec notify(term(), Path.t()) :: :ok - def notify(registry, key, result) do - Registry.dispatch(registry, key, fn entries -> - for {pid, _value} <- entries, do: send(pid, {registry, :notification, result}) - end) - end end diff --git a/lib/ex_vision/ex_vision.ex b/lib/ex_vision/ex_vision.ex new file mode 100644 index 0000000..1340a02 --- /dev/null +++ b/lib/ex_vision/ex_vision.ex @@ -0,0 +1,10 @@ +defmodule ExVision do + @moduledoc false + use Application + + @impl true + def start(_type, _args) do + children = [{ExVision.Cache, name: ExVision.Cache}] + Supervisor.start_link(children, strategy: :one_for_one) + end +end diff --git a/lib/ex_vision/model/definition/ortex.ex b/lib/ex_vision/model/definition/ortex.ex index f953508..56884c0 100644 --- a/lib/ex_vision/model/definition/ortex.ex +++ b/lib/ex_vision/model/definition/ortex.ex @@ -85,13 +85,11 @@ defmodule ExVision.Model.Definition.Ortex do {:ok, ExVision.Model.t()} | {:error, atom()} def load_ortex_model(module, model_path, options) do with {:ok, options} <- - Keyword.validate(options, [ - :cache_path, + Keyword.validate(options, batch_size: 1, providers: [:cpu] - ]), - cache_options = Keyword.take(options, [:cache_path, :file_path]), - {:ok, path} <- ExVision.Cache.lazy_get(model_path, cache_options), + ), + {:ok, path} <- ExVision.Cache.lazy_get(ExVision.Cache, model_path), {:ok, model} <- do_load_model(path, options[:providers]) do output_names = ExVision.Utils.onnx_output_names(model) diff --git a/mix.exs b/mix.exs index b83db47..d7be525 100644 --- a/mix.exs +++ b/mix.exs @@ -30,6 +30,8 @@ defmodule ExVision.Mixfile do def application do [ + included_applications: [:ex_vision], + mod: {ExVision, []}, extra_applications: [] ] end diff --git a/test/ex_vision/cache_test.exs b/test/ex_vision/cache_test.exs index 76530b4..e4a1523 100644 --- a/test/ex_vision/cache_test.exs +++ b/test/ex_vision/cache_test.exs @@ -6,17 +6,14 @@ defmodule ExVision.CacheTest do @moduletag :tmp_dir - setup %{tmp_dir: tmp_dir} do - app_env_override(:server_url, URI.new!("http://mock_server:8000")) - app_env_override(:cache_path, tmp_dir) - end - setup ctx do files = Map.get(ctx, :files, %{ "/test" => rand_string(256) }) + set_mimic_global() + stub(Req, :get, fn %URI{host: "mock_server", port: 8000, path: path}, options -> options = Keyword.validate!(options, [:raw, :into]) @@ -39,34 +36,35 @@ defmodule ExVision.CacheTest do [files: files] end + setup %{tmp_dir: tmp_dir} do + {:ok, _cache} = + Cache.start_link( + name: MyCache, + server_url: URI.new!("http://mock_server:8000"), + cache_path: tmp_dir + ) + + :ok + end + test "Can download the file", ctx do [{path, expected_contents}] = Enum.to_list(ctx.files) expected_path = Path.join(ctx.tmp_dir, path) - assert {:ok, ^expected_path} = Cache.lazy_get(path) + assert {:ok, ^expected_path} = Cache.lazy_get(MyCache, path) verify_download(expected_path, expected_contents) end test "will fail if server is unreachable" do - app_env_override(:server_url, URI.new!("http://localhost:9999")) - assert {:error, :connection_failed} = Cache.lazy_get("/test") - assert {:error, :connection_failed} = Cache.lazy_get("/test") - end + url = "http://localhost:9999" + {:ok, c} = Cache.start_link(server_url: url, name: nil) - test "will fail if we request file that doesn't exist" do - assert {:error, :doesnt_exist} = Cache.lazy_get("/idk") - assert {:error, :doesnt_exist} = Cache.lazy_get("/idk") + assert {:error, :connection_failed} = Cache.lazy_get(c, "/test") + assert {:error, :connection_failed} = Cache.lazy_get(c, "/test") end - defp app_env_override(key, new_value) do - original = Application.fetch_env(:ex_vision, key) - Application.put_env(:ex_vision, key, new_value) - - on_exit(fn -> - case original do - {:ok, value} -> Application.put_env(:ex_vision, key, value) - :error -> Application.delete_env(:ex_vision, key) - end - end) + test "will fail if we request file that doesn't exist" do + assert {:error, :doesnt_exist} = Cache.lazy_get(MyCache, "/idk") + assert {:error, :doesnt_exist} = Cache.lazy_get(MyCache, "/idk") end defp verify_download(path, expected_contents) do diff --git a/test/support/exvision/model/case.ex b/test/support/exvision/model/case.ex index 7cc4ef5..dbeb13e 100644 --- a/test/support/exvision/model/case.ex +++ b/test/support/exvision/model/case.ex @@ -13,7 +13,7 @@ defmodule ExVision.Model.Case do @behaviour ExVision.Model.Case setup_all do - {:ok, model} = unquote(opts[:module]).load(cache_path: "models") + {:ok, model} = unquote(opts[:module]).load() [model: model] end @@ -34,7 +34,7 @@ defmodule ExVision.Model.Case do end test "child_spec/1" do - assert spec = unquote(opts[:module]).child_spec(cache_path: "models") + assert spec = unquote(opts[:module]).child_spec() end describe "stateful/process workflow" do @@ -44,7 +44,7 @@ defmodule ExVision.Model.Case do {:ok, _supervisor} = Supervisor.start_link( - [unquote(opts[:module]).child_spec(name: name, cache_path: "models")], + [unquote(opts[:module]).child_spec(name: name)], strategy: :one_for_one ) @@ -69,8 +69,7 @@ defmodule ExVision.Model.Case do name: __MODULE__.TestProcess1, batch_size: 8, batch_timeout: 10, - partitions: true, - cache_path: "models" + partitions: true ] child_spec = {unquote(opts[:module]), options} From 0e8ac87d76a76537558f5ef485fcac2425b3a3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jod=C5=82os=CC=81?= Date: Mon, 27 May 2024 10:08:16 +0200 Subject: [PATCH 5/7] Set MIX_ENV to test on CI --- .github/workflows/elixir.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 05ba6bf..0bf3432 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -9,6 +9,9 @@ on: permissions: contents: read +env: + MIX_ENV: test + jobs: build: name: Build and test From 423118fa6a11e72616e4fda810ac5de38bd43968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jod=C5=82os=CC=81?= Date: Mon, 27 May 2024 10:13:07 +0200 Subject: [PATCH 6/7] Remove unnecessary log --- lib/ex_vision/cache.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ex_vision/cache.ex b/lib/ex_vision/cache.ex index 8361472..eff2f41 100644 --- a/lib/ex_vision/cache.ex +++ b/lib/ex_vision/cache.ex @@ -64,7 +64,6 @@ defmodule ExVision.Cache do @impl true def handle_info({ref, result}, state) do - Logger.info("Task #{inspect(ref)} finished with #{inspect(result)}") state = emit(result, ref, state) {:noreply, state} end From 6b188d2dd1322ffed9fbf40902e25b48392028cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jod=C5=82os=CC=81?= Date: Mon, 27 May 2024 10:18:17 +0200 Subject: [PATCH 7/7] Remove checkout LFS step from CI --- .github/workflows/elixir.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 0bf3432..d1d07b8 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -32,8 +32,6 @@ jobs: path: deps key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} restore-keys: ${{ runner.os }}-mix- - - name: Checkout LFS - uses: nschloe/action-cached-lfs-checkout@v1.1.2 - name: Install dependencies run: mix deps.get && mix deps.compile - name: Checks if compiles without warning