diff --git a/apps/fair-detr/.gitignore b/apps/fair-detr/.gitignore new file mode 100644 index 000000000..b676087e9 --- /dev/null +++ b/apps/fair-detr/.gitignore @@ -0,0 +1 @@ +__pycache__/model.cpython-37.pyc diff --git a/apps/fair-detr/Procfile b/apps/fair-detr/Procfile new file mode 100644 index 000000000..e40ede09b --- /dev/null +++ b/apps/fair-detr/Procfile @@ -0,0 +1 @@ +web: gunicorn app:server --workers 2 diff --git a/apps/fair-detr/README.md b/apps/fair-detr/README.md new file mode 100644 index 000000000..1788921cd --- /dev/null +++ b/apps/fair-detr/README.md @@ -0,0 +1,50 @@ +# Dash DETR Detection App + +*A User Interface for DETR built with Dash. 100% Python. [Click here for a demo](https://dash-gallery.plotly.host/dash-detr/).* + +The release of [*DETR: End-to-End Object Detection with Transformers*](https://github.com/facebookresearch/detr) showed significant improvement in real-time object detection and panoptic segmentation (PS), while greatly simplifying the architecture. As a mean to test the model, we decided to build a simple [Dash](https://plotly.com/dash/) app that let you experiment and play with the model through a user interface. + +![demo](assets/dash_detr.gif) + +## Overview + +* The code is open-source and ready to be forked. +* Everything is in pure Python - not a single line of HTML, CSS, or JavaScript required. +* The app was written 200 lines of code (in addition to modelling), and only took a few hours. +* From development to production in minutes with [Dash Kubernetes](https://plotly.com/dash/kubernetes/) - no need to spend hours figuring out deployment. + + +## Usage + +1. Clone this repo: +``` +git clone https://github.com/plotly/dash-detr +cd dash-detr +``` + +2. Create a fresh venv (with `conda` or `virtualenv`) and activate it: +``` +conda create -n dash-detr python=3.7 +conda activate dash-detr +``` + +3. Install the requirements: +``` +pip install -r requirements.txt +``` + +4. Start the app: +``` +python app.py +``` + +5. Try the app at `localhost:8050`! + +## Modifying or extending the app + +To make it easy for you to extend the app by adding a custom model, we placed everything related to PyTorch and modeling in `model.py`, so you can add your own models without modifying the app. If you want to customize the layout or create new interactions, you can edit `app.py` (if you are not familiar with Dash, read the [tutorials](https://dash.plotly.com/installation) first.) + +## Productionizing Object Detection? + +If you are interested in deploy apps like this one for production, check out our article on [productionizing object detection models with Dash Enterprise](https://medium.com/plotly/productionizing-object-detection-models-with-dash-enterprise-dba1c9402c2f), or [reach out to us](https://plotly.com/get-demo/). + diff --git a/apps/fair-detr/app.py b/apps/fair-detr/app.py new file mode 100644 index 000000000..7ac613267 --- /dev/null +++ b/apps/fair-detr/app.py @@ -0,0 +1,301 @@ +import base64 +from io import BytesIO +import time + +import dash +from dash.dependencies import Input, Output, State +import dash_core_components as dcc +import dash_html_components as html +import plotly.graph_objects as go +from PIL import Image +import requests + +from model import detect, filter_boxes, detr, transform +from model import CLASSES, DEVICE + + +# Dash component wrappers +def Row(children=None, **kwargs): + return html.Div(children, className="row", **kwargs) + + +def Column(children=None, width=1, **kwargs): + nb_map = { + 1: "one", + 2: "two", + 3: "three", + 4: "four", + 5: "five", + 6: "six", + 7: "seven", + 8: "eight", + 9: "nine", + 10: "ten", + 11: "eleven", + 12: "twelve", + } + + return html.Div(children, className=f"{nb_map[width]} columns", **kwargs) + + +# plotly.py helper functions +def pil_to_b64(im, enc="png"): + io_buf = BytesIO() + im.save(io_buf, format=enc) + encoded = base64.b64encode(io_buf.getvalue()).decode("utf-8") + return f"data:img/{enc};base64, " + encoded + + +def pil_to_fig(im, showlegend=False, title=None): + img_width, img_height = im.size + fig = go.Figure() + # This trace is added to help the autoresize logic work. + fig.add_trace( + go.Scatter( + x=[img_width * 0.05, img_width * 0.95], + y=[img_height * 0.95, img_height * 0.05], + showlegend=False, + mode="markers", + marker_opacity=0, + hoverinfo="none", + legendgroup="Image", + ) + ) + + fig.add_layout_image( + dict( + source=pil_to_b64(im), + sizing="stretch", + opacity=1, + layer="below", + x=0, + y=0, + xref="x", + yref="y", + sizex=img_width, + sizey=img_height, + ) + ) + + # Adapt axes to the right width and height, lock aspect ratio + fig.update_xaxes( + showgrid=False, visible=False, constrain="domain", range=[0, img_width] + ) + + fig.update_yaxes( + showgrid=False, + visible=False, + scaleanchor="x", + scaleratio=1, + range=[img_height, 0], + ) + + fig.update_layout(title=title, showlegend=showlegend) + + return fig + + +def add_bbox( + fig, + x0, + y0, + x1, + y1, + showlegend=True, + name=None, + color=None, + opacity=0.5, + group=None, + text=None, +): + fig.add_trace( + go.Scatter( + x=[x0, x1, x1, x0, x0], + y=[y0, y0, y1, y1, y0], + mode="lines", + fill="toself", + opacity=opacity, + marker_color=color, + hoveron="fills", + name=name, + hoverlabel_namelength=0, + text=text, + legendgroup=group, + showlegend=showlegend, + ) + ) + + +# colors for visualization +COLORS = [ + "#fe938c", + "#86e7b8", + "#f9ebe0", + "#208aae", + "#fe4a49", + "#291711", + "#5f4b66", + "#b98b82", + "#87f5fb", + "#63326e", +] * 50 + +RANDOM_URLS = open("random_urls.txt").read().split("\n")[:-1] +print("Running on:", DEVICE) + +# Start Dash +app = dash.Dash(__name__) +server = app.server # Expose the server variable for deployments + +app.layout = html.Div( + className="container", + children=[ + Row(html.H1("Dash DETR Detection App")), + Row(html.P("Input Image URL:")), + Row( + [ + Column( + width=8, + children=[ + dcc.Input( + id="input-url", + style={"width": "100%"}, + placeholder="Insert URL...", + ), + ], + ), + Column(html.Button("Run DETR", id="button-run", n_clicks=0), width=2), + Column( + html.Button("Random Image", id="button-random", n_clicks=0), width=2 + ), + ] + ), + Row(dcc.Graph(id="model-output", style={"height": "70vh"})), + Row( + [ + Column( + width=7, + children=[ + html.P("Non-maximum suppression (IoU):"), + Row( + [ + Column( + width=3, + children=dcc.Checklist( + id="checklist-nms", + options=[ + {"label": "Enabled", "value": "enabled"} + ], + value=[], + ), + ), + Column( + width=9, + children=dcc.Slider( + id="slider-iou", + min=0, + max=1, + step=0.05, + value=0.5, + marks={0: "0", 1: "1"}, + ), + ), + ] + ), + ], + ), + Column( + width=5, + children=[ + html.P("Confidence Threshold:"), + dcc.Slider( + id="slider-confidence", + min=0, + max=1, + step=0.05, + value=0.7, + marks={0: "0", 1: "1"}, + ), + ], + ), + ] + ), + ], +) + + +@app.callback( + [Output("button-run", "n_clicks"), Output("input-url", "value")], + [Input("button-random", "n_clicks")], + [State("button-run", "n_clicks")], +) +def randomize(random_n_clicks, run_n_clicks): + return run_n_clicks + 1, RANDOM_URLS[random_n_clicks % len(RANDOM_URLS)] + + +@app.callback( + [Output("model-output", "figure"), Output("slider-iou", "disabled")], + [ + Input("button-run", "n_clicks"), + Input("input-url", "n_submit"), + Input("slider-iou", "value"), + Input("slider-confidence", "value"), + Input("checklist-nms", "value"), + ], + [State("input-url", "value")], +) +def run_model(n_clicks, n_submit, iou, confidence, checklist, url): + apply_nms = "enabled" in checklist + try: + im = Image.open(requests.get(url, stream=True).raw) + except: + return go.Figure().update_layout(title="Incorrect URL") + + tstart = time.time() + + scores, boxes = detect(im, detr, transform, device=DEVICE) + scores, boxes = filter_boxes( + scores, boxes, confidence=confidence, iou=iou, apply_nms=apply_nms + ) + + scores = scores.data.numpy() + boxes = boxes.data.numpy() + + tend = time.time() + + fig = pil_to_fig( + im, showlegend=True, title=f"DETR Predictions ({tend-tstart:.2f}s)" + ) + existing_classes = set() + + for i in range(boxes.shape[0]): + class_id = scores[i].argmax() + label = CLASSES[class_id] + confidence = scores[i].max() + x0, y0, x1, y1 = boxes[i] + + # only display legend when it's not in the existing classes + showlegend = label not in existing_classes + text = f"class={label}
confidence={confidence:.3f}" + + add_bbox( + fig, + x0, + y0, + x1, + y1, + opacity=0.7, + group=label, + name=label, + color=COLORS[class_id], + showlegend=showlegend, + text=text, + ) + + existing_classes.add(label) + + return fig, not apply_nms + + +if __name__ == "__main__": + app.run_server(debug=True) diff --git a/apps/fair-detr/assets/dash_detr.gif b/apps/fair-detr/assets/dash_detr.gif new file mode 100644 index 000000000..f6104de88 Binary files /dev/null and b/apps/fair-detr/assets/dash_detr.gif differ diff --git a/apps/fair-detr/assets/default.min.css b/apps/fair-detr/assets/default.min.css new file mode 100644 index 000000000..c3f8fb77d --- /dev/null +++ b/apps/fair-detr/assets/default.min.css @@ -0,0 +1,415 @@ +/* Table of contents +–––––––––––––––––––––––––––––––––––––––––––––––––– +- Plotly.js +- Grid +- Base Styles +- Typography +- Links +- Buttons +- Forms +- Lists +- Code +- Tables +- Spacing +- Utilities +- Clearing +- Media Queries +*/ + +/* PLotly.js +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +/* plotly.js's modebar's z-index is 1001 by default + * https://github.com/plotly/plotly.js/blob/7e4d8ab164258f6bd48be56589dacd9bdd7fded2/src/css/_modebar.scss#L5 + * In case a dropdown is above the graph, the dropdown's options + * will be rendered below the modebar + * Increase the select option's z-index + */ + +/* This was actually not quite right - + dropdowns were overlapping each other (edited October 26) + +.Select { + z-index: 1002; +}*/ + + +/* Grid +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +.container { + position: relative; + width: 100%; + max-width: 960px; + margin: 0 auto; + padding: 0 20px; + box-sizing: border-box; } +.column, +.columns { + width: 100%; + float: left; + box-sizing: border-box; } + +/* For devices larger than 400px */ +@media (min-width: 400px) { + .container { + width: 85%; + padding: 0; } +} + +/* For devices larger than 550px */ +@media (min-width: 550px) { + .container { + width: 80%; } + .column, + .columns { + margin-left: 4%; } + .column:first-child, + .columns:first-child { + margin-left: 0; } + + .one.column, + .one.columns { width: 4.66666666667%; } + .two.columns { width: 13.3333333333%; } + .three.columns { width: 22%; } + .four.columns { width: 30.6666666667%; } + .five.columns { width: 39.3333333333%; } + .six.columns { width: 48%; } + .seven.columns { width: 56.6666666667%; } + .eight.columns { width: 65.3333333333%; } + .nine.columns { width: 74.0%; } + .ten.columns { width: 82.6666666667%; } + .eleven.columns { width: 91.3333333333%; } + .twelve.columns { width: 100%; margin-left: 0; } + + .one-third.column { width: 30.6666666667%; } + .two-thirds.column { width: 65.3333333333%; } + + .one-half.column { width: 48%; } + + /* Offsets */ + .offset-by-one.column, + .offset-by-one.columns { margin-left: 8.66666666667%; } + .offset-by-two.column, + .offset-by-two.columns { margin-left: 17.3333333333%; } + .offset-by-three.column, + .offset-by-three.columns { margin-left: 26%; } + .offset-by-four.column, + .offset-by-four.columns { margin-left: 34.6666666667%; } + .offset-by-five.column, + .offset-by-five.columns { margin-left: 43.3333333333%; } + .offset-by-six.column, + .offset-by-six.columns { margin-left: 52%; } + .offset-by-seven.column, + .offset-by-seven.columns { margin-left: 60.6666666667%; } + .offset-by-eight.column, + .offset-by-eight.columns { margin-left: 69.3333333333%; } + .offset-by-nine.column, + .offset-by-nine.columns { margin-left: 78.0%; } + .offset-by-ten.column, + .offset-by-ten.columns { margin-left: 86.6666666667%; } + .offset-by-eleven.column, + .offset-by-eleven.columns { margin-left: 95.3333333333%; } + + .offset-by-one-third.column, + .offset-by-one-third.columns { margin-left: 34.6666666667%; } + .offset-by-two-thirds.column, + .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } + + .offset-by-one-half.column, + .offset-by-one-half.columns { margin-left: 52%; } + +} + + +/* Base Styles +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +/* NOTE +html is set to 62.5% so that all the REM measurements throughout Skeleton +are based on 10px sizing. So basically 1.5rem = 15px :) */ +html { + font-size: 62.5%; } +body { + font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ + line-height: 1.6; + font-weight: 400; + font-family: "Open Sans", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: rgb(50, 50, 50); } + + +/* Typography +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0; + font-weight: 300; } +h1 { font-size: 4.5rem; line-height: 1.2; letter-spacing: -.1rem; margin-bottom: 2rem; } +h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; margin-bottom: 1.8rem; margin-top: 1.8rem;} +h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; margin-bottom: 1.5rem; margin-top: 1.5rem;} +h4 { font-size: 2.6rem; line-height: 1.35; letter-spacing: -.08rem; margin-bottom: 1.2rem; margin-top: 1.2rem;} +h5 { font-size: 2.2rem; line-height: 1.5; letter-spacing: -.05rem; margin-bottom: 0.6rem; margin-top: 0.6rem;} +h6 { font-size: 2.0rem; line-height: 1.6; letter-spacing: 0; margin-bottom: 0.75rem; margin-top: 0.75rem;} + +p { + margin-top: 0; } + + +/* Blockquotes +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +blockquote { + border-left: 4px lightgrey solid; + padding-left: 1rem; + margin-top: 2rem; + margin-bottom: 2rem; + margin-left: 0rem; +} + + +/* Links +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +a { + color: #1EAEDB; + text-decoration: underline; + cursor: pointer;} +a:hover { + color: #0FA0CE; } + + +/* Buttons +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +.button, +button, +input[type="submit"], +input[type="reset"], +input[type="button"] { + display: inline-block; + height: 38px; + padding: 0 30px; + color: #555; + text-align: center; + font-size: 11px; + font-weight: 600; + line-height: 38px; + letter-spacing: .1rem; + text-transform: uppercase; + text-decoration: none; + white-space: nowrap; + background-color: transparent; + border-radius: 4px; + border: 1px solid #bbb; + cursor: pointer; + box-sizing: border-box; } +.button:hover, +button:hover, +input[type="submit"]:hover, +input[type="reset"]:hover, +input[type="button"]:hover, +.button:focus, +button:focus, +input[type="submit"]:focus, +input[type="reset"]:focus, +input[type="button"]:focus { + color: #333; + border-color: #888; + outline: 0; } +.button.button-primary, +button.button-primary, +input[type="submit"].button-primary, +input[type="reset"].button-primary, +input[type="button"].button-primary { + color: #FFF; + background-color: #33C3F0; + border-color: #33C3F0; } +.button.button-primary:hover, +button.button-primary:hover, +input[type="submit"].button-primary:hover, +input[type="reset"].button-primary:hover, +input[type="button"].button-primary:hover, +.button.button-primary:focus, +button.button-primary:focus, +input[type="submit"].button-primary:focus, +input[type="reset"].button-primary:focus, +input[type="button"].button-primary:focus { + color: #FFF; + background-color: #1EAEDB; + border-color: #1EAEDB; } + + +/* Forms +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +input[type="email"], +input[type="number"], +input[type="search"], +input[type="text"], +input[type="tel"], +input[type="url"], +input[type="password"], +textarea, +select { + height: 38px; + padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ + background-color: #fff; + border: 1px solid #D1D1D1; + border-radius: 4px; + box-shadow: none; + box-sizing: border-box; + font-family: inherit; + font-size: inherit; /*https://stackoverflow.com/questions/6080413/why-doesnt-input-inherit-the-font-from-body*/} +/* Removes awkward default styles on some inputs for iOS */ +input[type="email"], +input[type="number"], +input[type="search"], +input[type="text"], +input[type="tel"], +input[type="url"], +input[type="password"], +textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } +textarea { + min-height: 65px; + padding-top: 6px; + padding-bottom: 6px; } +input[type="email"]:focus, +input[type="number"]:focus, +input[type="search"]:focus, +input[type="text"]:focus, +input[type="tel"]:focus, +input[type="url"]:focus, +input[type="password"]:focus, +textarea:focus, +select:focus { + border: 1px solid #33C3F0; + outline: 0; } +label, +legend { + display: block; + margin-bottom: 0px; } +fieldset { + padding: 0; + border-width: 0; } +input[type="checkbox"], +input[type="radio"] { + display: inline; } +label > .label-body { + display: inline-block; + margin-left: .5rem; + font-weight: normal; } + + +/* Lists +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +ul { + list-style: circle inside; } +ol { + list-style: decimal inside; } +ol, ul { + padding-left: 0; + margin-top: 0; } +ul ul, +ul ol, +ol ol, +ol ul { + margin: 1.5rem 0 1.5rem 3rem; + font-size: 90%; } +li { + margin-bottom: 1rem; } + + +/* Tables +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +table { + border-collapse: collapse; +} +th:not(.CalendarDay), +td:not(.CalendarDay) { + padding: 12px 15px; + text-align: left; + border-bottom: 1px solid #E1E1E1; } +th:first-child:not(.CalendarDay), +td:first-child:not(.CalendarDay) { + padding-left: 0; } +th:last-child:not(.CalendarDay), +td:last-child:not(.CalendarDay) { + padding-right: 0; } + + +/* Spacing +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +button, +.button { + margin-bottom: 0rem; } +input, +textarea, +select, +fieldset { + margin-bottom: 0rem; } +pre, +dl, +figure, +table, +form { + margin-bottom: 0rem; } +p, +ul, +ol { + margin-bottom: 0.75rem; } + +/* Utilities +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +.u-full-width { + width: 100%; + box-sizing: border-box; } +.u-max-full-width { + max-width: 100%; + box-sizing: border-box; } +.u-pull-right { + float: right; } +.u-pull-left { + float: left; } + + +/* Misc +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +hr { + margin-top: 3rem; + margin-bottom: 3.5rem; + border-width: 0; + border-top: 1px solid #E1E1E1; } + + +/* Clearing +–––––––––––––––––––––––––––––––––––––––––––––––––– */ + +/* Self Clearing Goodness */ +.container:after, +.row:after, +.u-cf { + content: ""; + display: table; + clear: both; } + + +/* Media Queries +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +/* +Note: The best way to structure the use of media queries is to create the queries +near the relevant code. For example, if you wanted to change the styles for buttons +on small devices, paste the mobile query code up in the buttons section and style it +there. +*/ + + +/* Larger than mobile */ +@media (min-width: 400px) {} + +/* Larger than phablet (also point when grid becomes active) */ +@media (min-width: 550px) {} + +/* Larger than tablet */ +@media (min-width: 750px) {} + +/* Larger than desktop */ +@media (min-width: 1000px) {} + +/* Larger than Desktop HD */ +@media (min-width: 1200px) {} \ No newline at end of file diff --git a/apps/fair-detr/model.py b/apps/fair-detr/model.py new file mode 100644 index 000000000..780714778 --- /dev/null +++ b/apps/fair-detr/model.py @@ -0,0 +1,158 @@ +import numpy as np +import torch +from torchvision.ops.boxes import batched_nms +import torchvision.transforms as T + + +# for output bounding box post-processing +def box_cxcywh_to_xyxy(x): + x_c, y_c, w, h = x.unbind(1) + b = [(x_c - 0.5 * w), (y_c - 0.5 * h), (x_c + 0.5 * w), (y_c + 0.5 * h)] + return torch.stack(b, dim=1) + + +def rescale_bboxes(out_bbox, size): + img_w, img_h = size + b = box_cxcywh_to_xyxy(out_bbox) + b = b * torch.tensor([img_w, img_h, img_w, img_h], dtype=torch.float32) + return b + + +def detect(im, model, transform, device="cpu"): + # mean-std normalize the input image (batch-size: 1) + img = transform(im).unsqueeze(0).to(device) + model.to(device) + # propagate through the model + outputs = model(img) + # keep only predictions with confidence above threshold + probas = outputs["pred_logits"].softmax(-1)[0, :, :-1].cpu() + # convert boxes from [0; 1] to image scales + bboxes_scaled = rescale_bboxes(outputs["pred_boxes"][0,].cpu(), im.size) + return probas, bboxes_scaled + + +def filter_boxes(scores, boxes, confidence=0.7, apply_nms=True, iou=0.5): + keep = scores.max(-1).values > confidence + scores, boxes = scores[keep], boxes[keep] + + if apply_nms: + top_scores, labels = scores.max(-1) + keep = batched_nms(boxes, top_scores, labels, iou) + scores, boxes = scores[keep], boxes[keep] + + return scores, boxes + + +# COCO classes +CLASSES = [ + "N/A", + "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", +] + + +# Load model +DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") +detr = torch.hub.load("facebookresearch/detr", "detr_resnet50", pretrained=True) +detr.eval().to(DEVICE) + +# standard PyTorch mean-std input image normalization +transform = T.Compose( + [ + T.Resize(500), + T.ToTensor(), + T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), + ] +) + + +# The following are imported in app: +# >> detect, filter_boxes, detr, transform, CLASSES, DEVICE diff --git a/apps/fair-detr/random_urls.txt b/apps/fair-detr/random_urls.txt new file mode 100644 index 000000000..dfc4f8484 --- /dev/null +++ b/apps/fair-detr/random_urls.txt @@ -0,0 +1,18 @@ +http://farm5.staticflickr.com/4115/4808627642_46feddf8c3_z.jpg +http://farm6.staticflickr.com/5060/5580489202_d43295ea0b_z.jpg +http://farm5.staticflickr.com/4103/5200926590_b29e3d62fb_z.jpg +http://farm1.staticflickr.com/40/124035573_3f564a25aa_z.jpg +http://farm9.staticflickr.com/8468/8097377641_595d31201b_z.jpg +http://farm8.staticflickr.com/7237/7000138695_2513fc8eda_z.jpg +http://farm4.staticflickr.com/3034/2576825166_233687201d_z.jpg +http://farm5.staticflickr.com/4044/4482245176_782b8932f7_z.jpg +http://farm1.staticflickr.com/38/83651631_9224bb6450_z.jpg +http://farm4.staticflickr.com/3651/3470749993_a1d0338644_z.jpg +http://farm8.staticflickr.com/7443/9890081356_295a05b332_z.jpg +http://farm4.staticflickr.com/3185/3018575673_65a91be272_z.jpg +http://farm9.staticflickr.com/8244/8513526968_27242c043b_z.jpg +http://farm7.staticflickr.com/6076/6154897961_0d21ef0efe_z.jpg +http://farm4.staticflickr.com/3212/4052516930_d8bc24404c_z.jpg +http://farm5.staticflickr.com/4041/4604523114_6f06dcba15_z.jpg +http://farm4.staticflickr.com/3140/2574052691_a596cf3d08_z.jpg +http://farm3.staticflickr.com/2426/3904552439_363f28aa68_z.jpg diff --git a/apps/fair-detr/requirements.txt b/apps/fair-detr/requirements.txt new file mode 100644 index 000000000..515120f87 --- /dev/null +++ b/apps/fair-detr/requirements.txt @@ -0,0 +1,31 @@ +Brotli==1.0.7 +certifi==2020.4.5.1 +chardet==3.0.4 +click==7.1.2 +dash==1.12.0 +dash-core-components==1.10.0 +dash-html-components==1.0.3 +dash-renderer==1.4.1 +dash-table==4.7.0 +Flask==1.1.2 +Flask-Compress==1.5.0 +future==0.18.2 +gunicorn==20.0.4 +idna==2.9 +itsdangerous==1.1.0 +Jinja2==2.11.2 +MarkupSafe==1.1.1 +numpy==1.18.4 +pandas==1.0.3 +Pillow==7.1.2 +plotly==4.8.0 +python-dateutil==2.8.1 +pytz==2020.1 +requests==2.23.0 +retrying==1.3.3 +scipy==1.4.1 +six==1.15.0 +torch==1.5.0 +torchvision==0.6.0 +urllib3==1.25.9 +Werkzeug==1.0.1 diff --git a/apps/fair-detr/runtime.txt b/apps/fair-detr/runtime.txt new file mode 100644 index 000000000..257b314f5 --- /dev/null +++ b/apps/fair-detr/runtime.txt @@ -0,0 +1 @@ +python-3.7.6 \ No newline at end of file