parth parekh commited on
Commit
794a185
·
0 Parent(s):

working demo

Browse files
Files changed (6) hide show
  1. .gitattributes +35 -0
  2. 640m.onnx +3 -0
  3. Dockerfile +20 -0
  4. README.md +11 -0
  5. app.py +112 -0
  6. requirements.txt +8 -0
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
640m.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:04fe3d77980780c1f8297dc6d7f942fd5b3abe6942a188f742a85241e4f634eb
3
+ size 103538690
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.8-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y \
6
+ libglib2.0-0 \
7
+ libsm6 \
8
+ libxext6 \
9
+ libxrender-dev \
10
+ libgl1-mesa-glx \
11
+ wget \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ COPY requirements.txt .
15
+ RUN pip install --no-cache-dir -r requirements.txt
16
+
17
+ COPY . .
18
+
19
+
20
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "4"]
README.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: NudeNet
3
+ emoji: 👀
4
+ colorFrom: red
5
+ colorTo: indigo
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
11
+ ****
app.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ import tempfile
4
+ from io import BytesIO
5
+ from PIL import Image
6
+ from fastapi import FastAPI, File, UploadFile, HTTPException, Query
7
+ from fastapi.responses import JSONResponse
8
+ from pydantic import BaseModel
9
+ from nudenet import NudeDetector
10
+ import cv2
11
+ from moviepy.editor import VideoFileClip
12
+ from typing import Optional
13
+
14
+ app = FastAPI(
15
+ title="Nudenet API",
16
+ description="API for detecting nudity in images and videos using Nudenet",
17
+ version="1.0.0",
18
+ docs_url="/",
19
+ redoc_url="/redoc"
20
+ )
21
+
22
+ # Initialize NudeDetector with both models
23
+ detector_320n = NudeDetector()
24
+ detector_640m = NudeDetector(model_path="640m.onnx", inference_resolution=640)
25
+
26
+ class Base64Image(BaseModel):
27
+ image: str
28
+
29
+ @app.post("/detect", summary="Detect nudity in an image")
30
+ async def detect_nudity(image: UploadFile = File(...), use_640m: bool = Query(False, description="Use the 640m model instead of the default 320n model")):
31
+ if not image.content_type.startswith('image/'):
32
+ raise HTTPException(status_code=400, detail="File provided is not an image")
33
+
34
+ contents = await image.read()
35
+ img = Image.open(BytesIO(contents))
36
+
37
+ if img.mode == 'RGBA':
38
+ img = img.convert('RGB')
39
+
40
+ img_byte_arr = BytesIO()
41
+ img.save(img_byte_arr, format='JPEG')
42
+ img_byte_arr = img_byte_arr.getvalue()
43
+
44
+ detector = detector_640m if use_640m else detector_320n
45
+ result = detector.detect(img_byte_arr)
46
+
47
+ return JSONResponse(content={'result': result})
48
+
49
+ @app.post("/detect_base64", summary="Detect nudity in a base64 encoded image")
50
+ async def detect_nudity_base64(data: Base64Image, use_640m: bool = Query(False, description="Use the 640m model instead of the default 320n model")):
51
+ try:
52
+ image_data = base64.b64decode(data.image)
53
+ except:
54
+ raise HTTPException(status_code=400, detail="Invalid base64 string")
55
+
56
+ detector = detector_640m if use_640m else detector_320n
57
+ result = detector.detect(image_data)
58
+
59
+ return JSONResponse(content={'result': result})
60
+
61
+ @app.post("/detect_video", summary="Detect nudity in a video")
62
+ async def detect_nudity_video(
63
+ video: UploadFile = File(...),
64
+ frame_interval: Optional[int] = Query(default=None, description="Interval between frames to analyze. 0 for every frame, 1 for every second, 2 for every other frame, etc. If not provided, defaults to 1 second."),
65
+ use_640m: bool = Query(False, description="Use the 640m model instead of the default 320n model")
66
+ ):
67
+ if not video.content_type.startswith('video/'):
68
+ raise HTTPException(status_code=400, detail="File provided is not a video")
69
+
70
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as temp_video:
71
+ contents = await video.read()
72
+ temp_video.write(contents)
73
+ temp_video_path = temp_video.name
74
+
75
+ video_clip = VideoFileClip(temp_video_path)
76
+ fps = video_clip.fps
77
+ duration = video_clip.duration
78
+
79
+ detector = detector_640m if use_640m else detector_320n
80
+ results = []
81
+ if frame_interval is None:
82
+ # Default behavior: analyze one frame per second
83
+ for t in range(int(duration)):
84
+ frame = video_clip.get_frame(t)
85
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
86
+ detections = detector.detect(frame_rgb)
87
+ if detections:
88
+ results.append({
89
+ 'timestamp': t,
90
+ 'detections': detections
91
+ })
92
+ else:
93
+ # Custom interval behavior
94
+ for frame_number, frame in enumerate(video_clip.iter_frames()):
95
+ if frame_interval == 0 or frame_number % max(1, int(frame_interval * fps)) == 0:
96
+ timestamp = frame_number / fps
97
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
98
+ detections = detector.detect(frame_rgb)
99
+ if detections:
100
+ results.append({
101
+ 'timestamp': timestamp,
102
+ 'frame_number': frame_number,
103
+ 'detections': detections
104
+ })
105
+
106
+ os.unlink(temp_video_path)
107
+
108
+ return JSONResponse(content={'results': results})
109
+
110
+ if __name__ == '__main__':
111
+ import uvicorn
112
+ uvicorn.run(app, host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi==0.68.0
2
+ uvicorn==0.15.0
3
+ pillow==8.3.2
4
+ nudenet==3.4.2
5
+ opencv-python-headless==4.5.5.64
6
+ moviepy==1.0.3
7
+ python-multipart==0.0.5
8
+ requests==2.26.0