├── .gitignore ├── LICENSE ├── README.md ├── fish.gif ├── infer.sh └── source.m4 /.gitignore: -------------------------------------------------------------------------------- 1 | *.mp4 2 | *.mov 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Roboflow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚨🚨🚨 REPO DEPRECATED 🚨🚨🚨 2 | 3 | * For Realtime Video (or webcam): use [Roboflow `inference`](https://inference.roboflow.com); docs for [realtime video streaming are available here](https://inference.roboflow.com/quickstart/run_model_on_rtsp_webcam/). 4 | * For Async/Recorded Video: Roboflow now provides a [new video inference API](https://blog.roboflow.com/roboflow-video-inference-api/), please visit [the Hosted Video Inference docs](https://docs.roboflow.com/deploy/video-inference) to learn more. 5 | 6 | 7 | 8 | # Roboflow Video Inference 9 | Example script to do inference on a video file with 10 | [Roboflow Infer](https://docs.roboflow.com/inference/hosted-api) 11 | 12 | ![Example Output](fish.gif) 13 | 14 | An example video (gif) generated with this utility script from a Roboflow model 15 | with the following command: 16 | 17 | ``` 18 | ROBOFLOW_KEY=xxxxxxxx ./infer.sh rf-aquarium-merged/3 IMG_3203.mov fish.gif --fps_in 3 --fps_out 12 --scale 4 19 | ``` 20 | ## Getting Started 21 | 22 | Colab Tutorial Here: 23 | 24 | Open In Colab 25 | 26 | ## Installation 27 | 28 | The shell script you need is [`infer.sh`](infer.sh) in this repo. 29 | 30 | To use it 31 | * copy that file to your local machine, 32 | * add execute permissions `chmod +x infer.sh`, 33 | * install the requirements (see below) 34 | * and then use it: 35 | 36 | ``` 37 | # Simple usage with ROBOFLOW_KEY env var set inline 38 | ROBOFLOW_KEY=xxxxx ./infer.sh [YOUR_DATASET]/[YOUR_VERSION] video_in.mp4 video_out.mov 39 | ``` 40 | 41 | To obtain your `ROBOFLOW_KEY`, go to 42 | [your account settings page](https://app.roboflow.com/account/api) and copy 43 | your `API Key` (*not* your `Publishable Key`). Treat this API Key like a password; 44 | it gives access to your Roboflow account and is meant to remain a secret. 45 | 46 | The model ID (`[YOUR_DATASET]/[YOUR_VERSION]` in the command above) is obtained from the Roboflow 47 | UI after training. Click "Example Web App" in the "Use Your Trained Model" section 48 | and copy it from the pre-filled text field at the top of the page. 49 | 50 | ## Requirements 51 | 52 | The script's main requirements are [`ffmpeg`](https://www.ffmpeg.org/), 53 | `base64`, and `curl`. On most modern Unix-like operating systems, you will only 54 | need to install `ffmpeg` because the other requirements are preinstalled. 55 | 56 | ### macOS 57 | 58 | Already includes `base64` and `curl`. 59 | 60 | Use [`homebrew`](https://brew.sh/) to install ffmpeg: `brew install ffmpeg` 61 | 62 | ### Ubuntu 63 | 64 | Already includes `base64` and `curl`. 65 | 66 | Use `apt` to install ffmpeg: `sudo apt install ffmpeg` 67 | 68 | ### Windows 69 | 70 | Download `ffmpeg` from [their website](https://www.ffmpeg.org/download.html) and 71 | install [`git` for Windows](https://git-scm.com/downloads) which conveniently 72 | includes `base64` and `curl`. 73 | 74 | ## Options 75 | 76 | A `ROBOFLOW_KEY` environment variable with 77 | [your Roboflow API Key](https://app.roboflow.com/account/api) 78 | is required. 79 | 80 | The [`infer.sh`](infer.sh) script accepts the following parameters: 81 | 82 | | Parameter | Example | Description 83 | | --- | --- | --- 84 | | `--host` | `--host "http://localhost:9001"` | The Roboflow Infer host; set for [On-Device Inference](https://docs.roboflow.com/inference/nvidia-jetson) (default: `https://infer.roboflow.com`). 85 | | `--confidence` | `--confidence 50` | The minimum threshold for the model to output box predictions (default: `50`). 86 | | `--overlap` | `--overlap 50` | The maximum amount two predicted boxes of the same class can intersect before being combined (default: `50`). 87 | | `--stroke` | `--stroke 5` | The thickness of the predicted bounding boxes (default: `5`). 88 | | `--labels` | `--labels` | Enable printing the class names (implicit default: off) 89 | | `--classes` | `--classes car` | A comma separated (no whitespace) list of classes to predict (default: show all). 90 | | `--fps_in` | `--fps_in 6` | The sample rate from the input video in frames per second (default: 6). 91 | | `--fps_out` | `--fps_out 24` | The render speed; setting higher than `fps_in` gives a timelapse effect (default: 24). 92 | | `--scale` | `--scale 2` | The amount to shrink the video; eg 2 to make video_out width and height 2x smaller than video_in (default: 1). 93 | | `--tmp` | `--tmp .` | The `tmp` directory; must be writable (default: `/tmp`). 94 | | `--retries` | `--retries 3` | The number of times to retry a failed inference (default: `3`). 95 | | `--parallel` | `--parallel 8` | The number of concurrent frames to send to the model (default: `8`). 96 | | model | `xx-mymodel--1` | The Roboflow model to use for inference (required). 97 | | video_in | `video_in.mp4` | The input video file (required). 98 | | video_out | `video_out.mp4` | The output video file (required). Can be of any format supported by `ffmpeg`'s encoder (eg `.mov`, `.mp4`, `.gif`). 99 | 100 | ## Building 101 | 102 | [The infer script](infer.sh) is built with [`argbash`](https://argbash.io) 103 | (a code generator that adds smart parameter parsing for bash scripts) 104 | from [`source.m4`](source.m4). 105 | 106 | To build, clone this repo, [install `argbash`](https://argbash.readthedocs.io/en/stable/install.html), 107 | then run the following in the repo's top-level directory: 108 | 109 | ``` 110 | argbash source.m4 -o infer.sh 111 | ``` 112 | 113 | You can then use your newly generated [`infer.sh`](infer.sh) as described above. 114 | 115 | ## License 116 | 117 | This example repo is released under an MIT License. You will need to train a model 118 | with [Roboflow Train](https://docs.roboflow.com/train) which is goverened by 119 | the [Roboflow Terms of Service](https://roboflow.com/terms). 120 | 121 | 122 | **_Note: Update from November 2023 - Roboflow now provides a [new video inference API](https://blog.roboflow.com/roboflow-video-inference-api/), please visit this link to learn more. This repository is archived._ 123 | ** 124 | -------------------------------------------------------------------------------- /fish.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roboflow/video-inference/898da8e34ee6f0fa7c0d9a0251c90453d0ae5837/fish.gif -------------------------------------------------------------------------------- /infer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################## 4 | # Roboflow Video Inference Example # 5 | # Bash script released under MIT License # 6 | # Copyright (c) 2021 Roboflow Inc # 7 | # # 8 | # https://github.com/roboflow-ai/video-inference # 9 | ################################################## 10 | 11 | # Created by argbash-init v2.10.0 12 | # ARG_OPTIONAL_SINGLE([host],[],[The Roboflow Infer host; set for On-Device Inference],["https://infer.roboflow.com"]) 13 | # ARG_OPTIONAL_SINGLE([confidence],[c],[The minimum threshold for the model to output box predictions.],[50]) 14 | # ARG_OPTIONAL_SINGLE([overlap],[o],[The maximum amount two predicted boxes of the same class can intersect before being combined.],[50]) 15 | # ARG_OPTIONAL_SINGLE([stroke],[s],[The thickness of the predicted bounding boxes.],[5]) 16 | # ARG_OPTIONAL_BOOLEAN([labels],[l],[Print the class names]) 17 | # ARG_OPTIONAL_SINGLE([fps_in],[],[The sample rate from the input video (in frames per second).],[6]) 18 | # ARG_OPTIONAL_SINGLE([fps_out],[],[The render rate (setting higher than fps_in will give a timelapse effect).],[24]) 19 | # ARG_OPTIONAL_SINGLE([scale],[],[The amount to shrink the video; eg 2 to make video_out width and height 2x smaller than video_in.],[1]) 20 | # ARG_OPTIONAL_SINGLE([tmp],[t],[The tmp directory; must be writable.],["/tmp"]) 21 | # ARG_OPTIONAL_SINGLE([retries],[r],[The number of times to retry a failed inference.],[3]) 22 | # ARG_OPTIONAL_SINGLE([parallel],[p],[The number of concurrent frames to send to the model.],[8]) 23 | # ARG_OPTIONAL_SINGLE([classes],[f],[The classes to show, separated by a comma (no spaces).],[]) 24 | # ARG_OPTIONAL_BOOLEAN([verbose],[v],[Print debugging information.]) 25 | # ARG_POSITIONAL_SINGLE([model],[The Roboflow model to use for inference (required).]) 26 | # ARG_POSITIONAL_SINGLE([video_in],[The input video file (required).]) 27 | # ARG_POSITIONAL_SINGLE([video_out],[The output video file (required).]) 28 | # ARG_DEFAULTS_POS() 29 | # ARG_HELP([]) 30 | # ARGBASH_GO() 31 | # needed because of Argbash --> m4_ignore([ 32 | ### START OF CODE GENERATED BY Argbash v2.10.0 one line above ### 33 | # Argbash is a bash code generator used to get arguments parsing right. 34 | # Argbash is FREE SOFTWARE, see https://argbash.io for more info 35 | 36 | 37 | die() 38 | { 39 | local _ret="${2:-1}" 40 | test "${_PRINT_HELP:-no}" = yes && print_help >&2 41 | echo "$1" >&2 42 | exit "${_ret}" 43 | } 44 | 45 | 46 | begins_with_short_option() 47 | { 48 | local first_option all_short_options='cosltrpfvh' 49 | first_option="${1:0:1}" 50 | test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0 51 | } 52 | 53 | # THE DEFAULTS INITIALIZATION - POSITIONALS 54 | _positionals=() 55 | _arg_model= 56 | _arg_video_in= 57 | _arg_video_out= 58 | # THE DEFAULTS INITIALIZATION - OPTIONALS 59 | _arg_host="https://detect.roboflow.com" 60 | _arg_confidence="50" 61 | _arg_overlap="50" 62 | _arg_stroke="5" 63 | _arg_labels="off" 64 | _arg_fps_in="6" 65 | _arg_fps_out="24" 66 | _arg_scale="1" 67 | _arg_tmp="/tmp" 68 | _arg_retries="3" 69 | _arg_parallel="8" 70 | _arg_classes= 71 | _arg_verbose="off" 72 | 73 | 74 | print_help() 75 | { 76 | printf '%s\n' "" 77 | printf 'Usage: %s [--host ] [-c|--confidence ] [-o|--overlap ] [-s|--stroke ] [-l|--(no-)labels] [--fps_in ] [--fps_out ] [--scale ] [-t|--tmp ] [-r|--retries ] [-p|--parallel ] [-f|--classes ] [-v|--(no-)verbose] [-h|--help] \n' "$0" 78 | printf '\t%s\n' ": The Roboflow model to use for inference (required)." 79 | printf '\t%s\n' ": The input video file (required)." 80 | printf '\t%s\n' ": The output video file (required)." 81 | printf '\t%s\n' "--host: The Roboflow Infer host; set for On-Device Inference (default: '"https://infer.roboflow.com"')" 82 | printf '\t%s\n' "-c, --confidence: The minimum threshold for the model to output box predictions. (default: '50')" 83 | printf '\t%s\n' "-o, --overlap: The maximum amount two predicted boxes of the same class can intersect before being combined. (default: '50')" 84 | printf '\t%s\n' "-s, --stroke: The thickness of the predicted bounding boxes. (default: '5')" 85 | printf '\t%s\n' "-l, --labels, --no-labels: Print the class names (off by default)" 86 | printf '\t%s\n' "--fps_in: The sample rate from the input video (in frames per second). (default: '6')" 87 | printf '\t%s\n' "--fps_out: The render rate (setting higher than fps_in will give a timelapse effect). (default: '24')" 88 | printf '\t%s\n' "--scale: The amount to shrink the video; eg 2 to make video_out width and height 2x smaller than video_in. (default: '1')" 89 | printf '\t%s\n' "-t, --tmp: The tmp directory; must be writable. (default: '"/tmp"')" 90 | printf '\t%s\n' "-r, --retries: The number of times to retry a failed inference. (default: '3')" 91 | printf '\t%s\n' "-p, --parallel: The number of concurrent frames to send to the model. (default: '8')" 92 | printf '\t%s\n' "-f, --classes: The classes to show, separated by a comma (no spaces). (no default)" 93 | printf '\t%s\n' "-v, --verbose, --no-verbose: Print debugging information. (off by default)" 94 | printf '\t%s\n' "-h, --help: Prints help" 95 | } 96 | 97 | 98 | parse_commandline() 99 | { 100 | _positionals_count=0 101 | while test $# -gt 0 102 | do 103 | _key="$1" 104 | case "$_key" in 105 | --host) 106 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 107 | _arg_host="$2" 108 | shift 109 | ;; 110 | --host=*) 111 | _arg_host="${_key##--host=}" 112 | ;; 113 | -c|--confidence) 114 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 115 | _arg_confidence="$2" 116 | shift 117 | ;; 118 | --confidence=*) 119 | _arg_confidence="${_key##--confidence=}" 120 | ;; 121 | -c*) 122 | _arg_confidence="${_key##-c}" 123 | ;; 124 | -o|--overlap) 125 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 126 | _arg_overlap="$2" 127 | shift 128 | ;; 129 | --overlap=*) 130 | _arg_overlap="${_key##--overlap=}" 131 | ;; 132 | -o*) 133 | _arg_overlap="${_key##-o}" 134 | ;; 135 | -s|--stroke) 136 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 137 | _arg_stroke="$2" 138 | shift 139 | ;; 140 | --stroke=*) 141 | _arg_stroke="${_key##--stroke=}" 142 | ;; 143 | -s*) 144 | _arg_stroke="${_key##-s}" 145 | ;; 146 | -l|--no-labels|--labels) 147 | _arg_labels="on" 148 | test "${1:0:5}" = "--no-" && _arg_labels="off" 149 | ;; 150 | -l*) 151 | _arg_labels="on" 152 | _next="${_key##-l}" 153 | if test -n "$_next" -a "$_next" != "$_key" 154 | then 155 | { begins_with_short_option "$_next" && shift && set -- "-l" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option." 156 | fi 157 | ;; 158 | --fps_in) 159 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 160 | _arg_fps_in="$2" 161 | shift 162 | ;; 163 | --fps_in=*) 164 | _arg_fps_in="${_key##--fps_in=}" 165 | ;; 166 | --fps_out) 167 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 168 | _arg_fps_out="$2" 169 | shift 170 | ;; 171 | --fps_out=*) 172 | _arg_fps_out="${_key##--fps_out=}" 173 | ;; 174 | --scale) 175 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 176 | _arg_scale="$2" 177 | shift 178 | ;; 179 | --scale=*) 180 | _arg_scale="${_key##--scale=}" 181 | ;; 182 | -t|--tmp) 183 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 184 | _arg_tmp="$2" 185 | shift 186 | ;; 187 | --tmp=*) 188 | _arg_tmp="${_key##--tmp=}" 189 | ;; 190 | -t*) 191 | _arg_tmp="${_key##-t}" 192 | ;; 193 | -r|--retries) 194 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 195 | _arg_retries="$2" 196 | shift 197 | ;; 198 | --retries=*) 199 | _arg_retries="${_key##--retries=}" 200 | ;; 201 | -r*) 202 | _arg_retries="${_key##-r}" 203 | ;; 204 | -p|--parallel) 205 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 206 | _arg_parallel="$2" 207 | shift 208 | ;; 209 | --parallel=*) 210 | _arg_parallel="${_key##--parallel=}" 211 | ;; 212 | -p*) 213 | _arg_parallel="${_key##-p}" 214 | ;; 215 | -f|--classes) 216 | test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1 217 | _arg_classes="$2" 218 | shift 219 | ;; 220 | --classes=*) 221 | _arg_classes="${_key##--classes=}" 222 | ;; 223 | -f*) 224 | _arg_classes="${_key##-f}" 225 | ;; 226 | -v|--no-verbose|--verbose) 227 | _arg_verbose="on" 228 | test "${1:0:5}" = "--no-" && _arg_verbose="off" 229 | ;; 230 | -v*) 231 | _arg_verbose="on" 232 | _next="${_key##-v}" 233 | if test -n "$_next" -a "$_next" != "$_key" 234 | then 235 | { begins_with_short_option "$_next" && shift && set -- "-v" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option." 236 | fi 237 | ;; 238 | -h|--help) 239 | print_help 240 | exit 0 241 | ;; 242 | -h*) 243 | print_help 244 | exit 0 245 | ;; 246 | *) 247 | _last_positional="$1" 248 | _positionals+=("$_last_positional") 249 | _positionals_count=$((_positionals_count + 1)) 250 | ;; 251 | esac 252 | shift 253 | done 254 | } 255 | 256 | 257 | handle_passed_args_count() 258 | { 259 | local _required_args_string="'model', 'video_in' and 'video_out'" 260 | test "${_positionals_count}" -ge 3 || _PRINT_HELP=yes die "FATAL ERROR: Not enough positional arguments - we require exactly 3 (namely: $_required_args_string), but got only ${_positionals_count}." 1 261 | test "${_positionals_count}" -le 3 || _PRINT_HELP=yes die "FATAL ERROR: There were spurious positional arguments --- we expect exactly 3 (namely: $_required_args_string), but got ${_positionals_count} (the last one was: '${_last_positional}')." 1 262 | } 263 | 264 | 265 | assign_positional_args() 266 | { 267 | local _positional_name _shift_for=$1 268 | _positional_names="_arg_model _arg_video_in _arg_video_out " 269 | 270 | shift "$_shift_for" 271 | for _positional_name in ${_positional_names} 272 | do 273 | test $# -gt 0 || break 274 | eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1 275 | shift 276 | done 277 | } 278 | 279 | parse_commandline "$@" 280 | handle_passed_args_count 281 | assign_positional_args 1 "${_positionals[@]}" 282 | 283 | # OTHER STUFF GENERATED BY Argbash 284 | 285 | ### END OF CODE GENERATED BY Argbash (sortof) ### ]) 286 | # [ <-- needed because of Argbash 287 | 288 | 289 | if [ -z "$ROBOFLOW_KEY" ]; then 290 | echo "ROBOFLOW_KEY environment variable not found; please set it to your Roboflow API key." 291 | exit 1 292 | fi 293 | 294 | verbose=$_arg_verbose 295 | 296 | if [ ! -z "$verbose" ]; then 297 | printf 'Value of --%s: %s\n' 'host' "$_arg_host" 298 | printf 'Value of --%s: %s\n' 'confidence' "$_arg_confidence" 299 | printf 'Value of --%s: %s\n' 'overlap' "$_arg_overlap" 300 | printf 'Value of --%s: %s\n' 'stroke' "$_arg_stroke" 301 | printf 'Value of --%s: %s\n' 'labels' "$_arg_labels" 302 | printf 'Value of --%s: %s\n' 'fps_in' "$_arg_fps_in" 303 | printf 'Value of --%s: %s\n' 'fps_out' "$_arg_fps_out" 304 | printf 'Value of --%s: %s\n' 'scale' "$_arg_scale" 305 | printf 'Value of --%s: %s\n' 'tmp' "$_arg_tmp" 306 | printf 'Value of --%s: %s\n' 'retries' "$_arg_retries" 307 | printf 'Value of --%s: %s\n' 'parallel' "$_arg_parallel" 308 | printf 'Value of --%s: %s\n' 'classes' "$_arg_classes" 309 | printf 'Value of --%s: %s\n' 'verbose' "$_arg_verbose" 310 | printf "Value of '%s': %s\\n" 'model' "$_arg_model" 311 | printf "Value of '%s': %s\\n" 'video_in' "$_arg_video_in" 312 | printf "Value of '%s': %s\\n" 'video_out' "$_arg_video_out" 313 | 314 | printf "\n" 315 | fi 316 | 317 | in=$_arg_video_in 318 | out=$_arg_video_out 319 | tmp=$_arg_tmp 320 | host=$_arg_host 321 | model=$_arg_model 322 | confidence=$_arg_confidence 323 | overlap=$_arg_overlap 324 | stroke=$_arg_stroke 325 | labels=$_arg_labels 326 | classes=$_arg_classes 327 | fps_in=$_arg_fps_in 328 | fps_out=$_arg_fps_out 329 | scale=$_arg_scale 330 | 331 | if [ $in = $out ]; then 332 | echo "Cannot overwrite input file. Please make sure video_in and video_out are different." 333 | exit 1 334 | fi 335 | 336 | # Check dependencies 337 | for command in ffmpeg base64 curl 338 | do 339 | command -v $command >/dev/null 2>&1 || { echo -en "\n$command needs to be installed but was not found.";deps=1; } 340 | done 341 | [[ $deps -ne 1 ]] || { 342 | echo -en "\nError: Install the above dependencies and rerun this script\n"; 343 | exit 1; 344 | } 345 | 346 | inference_url="$host/$model?api_key=$ROBOFLOW_KEY&format=image&confidence=$confidence&overlap=$overlap&stroke=$stroke" 347 | if [ $labels = "on" ]; then 348 | inference_url="$inference_url&labels=on" 349 | fi 350 | 351 | if [ $classes ]; then 352 | inference_url="$inference_url&classes=$classes" 353 | fi 354 | 355 | if [ ! -z "$verbose" ]; then 356 | echo "Inference URL: $inference_url" 357 | fi 358 | 359 | if [[ $(curl -s $inference_url | grep -e "not authorized" -e "does not exist") ]]; then 360 | echo "Invalid API Key or Model ID."; 361 | exit 1; 362 | fi 363 | 364 | mkdir -p $tmp/roboflow_in 365 | rm -f $tmp/roboflow_in/* 366 | 367 | mkdir -p $tmp/roboflow_out 368 | rm -f $tmp/roboflow_out/* 369 | 370 | if [[ $(find "$in" -type f -size +256c 2>/dev/null) ]]; then 371 | echo "Splitting input video ($in) into frames... this could take a while for large files." 372 | else 373 | echo "Error: Input file ($in) not found..." 374 | exit 1 375 | fi 376 | 377 | ffmpeg -i $in -r $fps_in -vf scale=iw/$scale:ih/$scale $tmp/roboflow_in/frame%05d.jpg 378 | 379 | FILES=$tmp/roboflow_in/frame*.jpg 380 | 381 | echo "Running inference on $(ls $tmp/roboflow_in | wc -l | xargs) frames..." 382 | trap 'exit' INT 383 | for x in {0..$_arg_retries} 384 | do 385 | for f in $FILES 386 | do 387 | ((i=i%$_arg_parallel)); ((i++==0)) && wait 388 | f=$(basename $f) 389 | if [[ $(find "$tmp/roboflow_out/$f" -type f -size +256c 2>/dev/null) ]]; then 390 | # this inference was already successful; no need to retry. 391 | true 392 | else 393 | if [ ! -z "$verbose" ]; then 394 | echo "Running inference on frame $f..." 395 | fi 396 | cat $tmp/roboflow_in/$f | base64 | curl -s -d @- $inference_url > "$tmp/roboflow_out/$f" & 397 | fi 398 | done 399 | done 400 | 401 | rm -f $out 402 | echo "Rendering final video ($out)." 403 | ffmpeg -i $tmp/roboflow_out/frame%05d.jpg -vf fps=$fps_out $out 404 | 405 | rm -rf $tmp/roboflow_in 406 | rm -rf $tmp/roboflow_out 407 | 408 | # ] <-- needed because of Argbash 409 | -------------------------------------------------------------------------------- /source.m4: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################## 4 | # Roboflow Video Inference Example # 5 | # Bash script released under MIT License # 6 | # Copyright (c) 2021 Roboflow Inc # 7 | # # 8 | # https://github.com/roboflow-ai/video-inference # 9 | ################################################## 10 | 11 | # m4_ignore( 12 | echo "This is just a script template, not the script (yet) - pass it to 'argbash' to fix this." >&2 13 | exit 11 #)Created by argbash-init v2.10.0 14 | # ARG_OPTIONAL_SINGLE([host], , [The Roboflow Infer host; set for On-Device Inference], ["https://infer.roboflow.com"]) 15 | # ARG_OPTIONAL_SINGLE([confidence], c, [The minimum threshold for the model to output box predictions.], [50]) 16 | # ARG_OPTIONAL_SINGLE([overlap], o, [The maximum amount two predicted boxes of the same class can intersect before being combined.], [50]) 17 | # ARG_OPTIONAL_SINGLE([stroke], s, [The thickness of the predicted bounding boxes.], [5]) 18 | # ARG_OPTIONAL_BOOLEAN([labels], l, [Print the class names]) 19 | # ARG_OPTIONAL_SINGLE([fps_in], , [The sample rate from the input video (in frames per second).], [6]) 20 | # ARG_OPTIONAL_SINGLE([fps_out], , [The render rate (setting higher than fps_in will give a timelapse effect).], [24]) 21 | # ARG_OPTIONAL_SINGLE([scale], , [The amount to shrink the video; eg 2 to make video_out width and height 2x smaller than video_in.], [1]) 22 | # ARG_OPTIONAL_SINGLE([tmp], t, [The tmp directory; must be writable.], ["/tmp"]) 23 | # ARG_OPTIONAL_SINGLE([retries], r, [The number of times to retry a failed inference.], [3]) 24 | # ARG_OPTIONAL_SINGLE([parallel], p, [The number of concurrent frames to send to the model.], [8]) 25 | # ARG_OPTIONAL_SINGLE([classes], f, [The classes to show, separated by a comma (no spaces).], []) 26 | # ARG_OPTIONAL_BOOLEAN([verbose], v, [Print debugging information.]) 27 | # ARG_POSITIONAL_SINGLE([model], [The Roboflow model to use for inference (required).]) 28 | # ARG_POSITIONAL_SINGLE([video_in], [The input video file (required).]) 29 | # ARG_POSITIONAL_SINGLE([video_out], [The output video file (required).]) 30 | # ARG_DEFAULTS_POS 31 | # ARG_HELP([]) 32 | # ARGBASH_GO 33 | 34 | # [ <-- needed because of Argbash 35 | 36 | if [ -z "$ROBOFLOW_KEY" ]; then 37 | echo "ROBOFLOW_KEY environment variable not found; please set it to your Roboflow API key." 38 | exit 1 39 | fi 40 | 41 | verbose=$_arg_verbose 42 | 43 | if [ ! -z "$verbose" ]; then 44 | printf 'Value of --%s: %s\n' 'host' "$_arg_host" 45 | printf 'Value of --%s: %s\n' 'confidence' "$_arg_confidence" 46 | printf 'Value of --%s: %s\n' 'overlap' "$_arg_overlap" 47 | printf 'Value of --%s: %s\n' 'stroke' "$_arg_stroke" 48 | printf 'Value of --%s: %s\n' 'labels' "$_arg_labels" 49 | printf 'Value of --%s: %s\n' 'fps_in' "$_arg_fps_in" 50 | printf 'Value of --%s: %s\n' 'fps_out' "$_arg_fps_out" 51 | printf 'Value of --%s: %s\n' 'scale' "$_arg_scale" 52 | printf 'Value of --%s: %s\n' 'tmp' "$_arg_tmp" 53 | printf 'Value of --%s: %s\n' 'retries' "$_arg_retries" 54 | printf 'Value of --%s: %s\n' 'parallel' "$_arg_parallel" 55 | printf 'Value of --%s: %s\n' 'classes' "$_arg_classes" 56 | printf 'Value of --%s: %s\n' 'verbose' "$_arg_verbose" 57 | printf "Value of '%s': %s\\n" 'model' "$_arg_model" 58 | printf "Value of '%s': %s\\n" 'video_in' "$_arg_video_in" 59 | printf "Value of '%s': %s\\n" 'video_out' "$_arg_video_out" 60 | 61 | printf "\n" 62 | fi 63 | 64 | in=$_arg_video_in 65 | out=$_arg_video_out 66 | tmp=$_arg_tmp 67 | host=$_arg_host 68 | model=$_arg_model 69 | confidence=$_arg_confidence 70 | overlap=$_arg_overlap 71 | stroke=$_arg_stroke 72 | labels=$_arg_labels 73 | classes=$_arg_classes 74 | fps_in=$_arg_fps_in 75 | fps_out=$_arg_fps_out 76 | scale=$_arg_scale 77 | 78 | if [ $in = $out ]; then 79 | echo "Cannot overwrite input file. Please make sure video_in and video_out are different." 80 | exit 1 81 | fi 82 | 83 | # Check dependencies 84 | for command in ffmpeg base64 curl 85 | do 86 | command -v $command >/dev/null 2>&1 || { echo -en "\n$command needs to be installed but was not found.";deps=1; } 87 | done 88 | [[ $deps -ne 1 ]] || { 89 | echo -en "\nError: Install the above dependencies and rerun this script\n"; 90 | exit 1; 91 | } 92 | 93 | inference_url="$host/$model?access_token=$ROBOFLOW_KEY&format=image&confidence=$confidence&overlap=$overlap&stroke=$stroke" 94 | if [ $labels = "on" ]; then 95 | inference_url="$inference_url&labels=on" 96 | fi 97 | 98 | if [ $classes ]; then 99 | inference_url="$inference_url&classes=$classes" 100 | fi 101 | 102 | if [ ! -z "$verbose" ]; then 103 | echo "Inference URL: $inference_url" 104 | fi 105 | 106 | if [[ $(curl -s $inference_url | grep -e "not authorized" -e "does not exist") ]]; then 107 | echo "Invalid API Key or Model ID."; 108 | exit 1; 109 | fi 110 | 111 | mkdir -p $tmp/roboflow_in 112 | rm -f $tmp/roboflow_in/* 113 | 114 | mkdir -p $tmp/roboflow_out 115 | rm -f $tmp/roboflow_out/* 116 | 117 | if [[ $(find "$in" -type f -size +256c 2>/dev/null) ]]; then 118 | echo "Splitting input video ($in) into frames... this could take a while for large files." 119 | else 120 | echo "Error: Input file ($in) not found..." 121 | exit 1 122 | fi 123 | 124 | ffmpeg -i $in -r $fps_in -vf scale=iw/$scale:ih/$scale $tmp/roboflow_in/frame%05d.jpg 125 | 126 | FILES=$tmp/roboflow_in/frame*.jpg 127 | 128 | echo "Running inference on $(ls $tmp/roboflow_in | wc -l | xargs) frames..." 129 | trap 'exit' INT 130 | for x in {0..$_arg_retries} 131 | do 132 | for f in $FILES 133 | do 134 | ((i=i%$_arg_parallel)); ((i++==0)) && wait 135 | f=$(basename $f) 136 | if [[ $(find "$tmp/roboflow_out/$f" -type f -size +256c 2>/dev/null) ]]; then 137 | # this inference was already successful; no need to retry. 138 | true 139 | else 140 | if [ ! -z "$verbose" ]; then 141 | echo "Running inference on frame $f..." 142 | fi 143 | cat $tmp/roboflow_in/$f | base64 | curl -s -d @- $inference_url > "$tmp/roboflow_out/$f" & 144 | fi 145 | done 146 | done 147 | 148 | rm -f $out 149 | echo "Rendering final video ($out)." 150 | ffmpeg -i $tmp/roboflow_out/frame%05d.jpg -vf fps=$fps_out $out 151 | 152 | rm -rf $tmp/roboflow_in 153 | rm -rf $tmp/roboflow_out 154 | 155 | # ] <-- needed because of Argbash 156 | --------------------------------------------------------------------------------