sugarknight commited on
Commit
1d7cf5a
·
1 Parent(s): 18d6271

Add application file

Browse files
Files changed (5) hide show
  1. app.py +49 -0
  2. launch.py +139 -0
  3. mozaikukun.py +208 -0
  4. package.txt +3 -0
  5. requirements.txt +0 -0
app.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import gradio as gr
3
+ from PIL import Image
4
+
5
+ import mozaikukun as moza
6
+ from ultralytics import YOLO
7
+
8
+ object_detector = YOLO("yolov8x.pt")
9
+ segmenter = YOLO("myseg3.pt")
10
+
11
+
12
+ def mosaic_process(input_img, pussy, penis, sex, anus, nipple):
13
+ img = Image.fromarray(np.uint8(input_img))
14
+ img = img.convert("RGBA")
15
+
16
+ process_mode = {
17
+ "pussy": pussy,
18
+ "penis": penis,
19
+ "sex": sex,
20
+ "anus": anus,
21
+ "nipple": nipple,
22
+ }
23
+
24
+ result = moza.process_and_analyze_image(img, object_detector, segmenter)
25
+ for key in result.keys():
26
+ if key not in process_mode:
27
+ continue
28
+
29
+ if process_mode.get(key) == "mosaic":
30
+ for mosaic_img in result[key]:
31
+ mosaic_img = mosaic_img.convert("RGBA")
32
+ img = Image.alpha_composite(img, mosaic_img)
33
+
34
+ return img
35
+
36
+
37
+ demo = gr.Interface(
38
+ fn=mosaic_process,
39
+ inputs=[
40
+ gr.Image(),
41
+ gr.Radio(choices=['raw', 'mosaic'], value='mosaic'),
42
+ gr.Radio(choices=['raw', 'mosaic'], value='mosaic'),
43
+ gr.Radio(choices=['raw', 'mosaic'], value='mosaic'),
44
+ gr.Radio(choices=['raw', 'mosaic'], value='raw'),
45
+ gr.Radio(choices=['raw', 'mosaic'], value='raw'),
46
+ ],
47
+ outputs=["image"])
48
+
49
+ demo.launch(server_name='0.0.0.0')
launch.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import importlib.util
2
+ import os
3
+ import shlex
4
+ import subprocess
5
+ import sys
6
+
7
+ commandline_args = os.environ.get("COMMANDLINE_ARGS", "")
8
+ sys.argv += shlex.split(commandline_args)
9
+
10
+ python = sys.executable
11
+ git = os.environ.get("GIT", "git")
12
+ index_url = os.environ.get("INDEX_URL", "")
13
+ stored_commit_hash = None
14
+ skip_install = False
15
+
16
+
17
+ def run(command, desc=None, errdesc=None, custom_env=None):
18
+ if desc is not None:
19
+ print(desc)
20
+
21
+ result = subprocess.run(
22
+ command,
23
+ stdout=subprocess.PIPE,
24
+ stderr=subprocess.PIPE,
25
+ shell=True,
26
+ env=os.environ if custom_env is None else custom_env,
27
+ )
28
+
29
+ if result.returncode != 0:
30
+ message = f"""{errdesc or 'Error running command'}.
31
+ Command: {command}
32
+ Error code: {result.returncode}
33
+ stdout: {result.stdout.decode(encoding="utf8", errors="ignore") if len(result.stdout)>0 else '<empty>'}
34
+ stderr: {result.stderr.decode(encoding="utf8", errors="ignore") if len(result.stderr)>0 else '<empty>'}
35
+ """
36
+ raise RuntimeError(message)
37
+
38
+ return result.stdout.decode(encoding="utf8", errors="ignore")
39
+
40
+
41
+ def check_run(command):
42
+ result = subprocess.run(
43
+ command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
44
+ )
45
+ return result.returncode == 0
46
+
47
+
48
+ def is_installed(package):
49
+ try:
50
+ spec = importlib.util.find_spec(package)
51
+ except ModuleNotFoundError:
52
+ return False
53
+
54
+ return spec is not None
55
+
56
+
57
+ def commit_hash():
58
+ global stored_commit_hash
59
+
60
+ if stored_commit_hash is not None:
61
+ return stored_commit_hash
62
+
63
+ try:
64
+ stored_commit_hash = run(f"{git} rev-parse HEAD").strip()
65
+ except Exception:
66
+ stored_commit_hash = "<none>"
67
+
68
+ return stored_commit_hash
69
+
70
+
71
+ def run_pip(args, desc=None):
72
+ if skip_install:
73
+ return
74
+
75
+ index_url_line = f" --index-url {index_url}" if index_url != "" else ""
76
+ return run(
77
+ f'"{python}" -m pip {args} --prefer-binary{index_url_line}',
78
+ desc=f"Installing {desc}",
79
+ errdesc=f"Couldn't install {desc}",
80
+ )
81
+
82
+
83
+ def run_python(code, desc=None, errdesc=None):
84
+ return run(f'"{python}" -c "{code}"', desc, errdesc)
85
+
86
+
87
+ def extract_arg(args, name):
88
+ return [x for x in args if x != name], name in args
89
+
90
+
91
+ def prepare_environment():
92
+ commit = commit_hash()
93
+
94
+ print(f"Python {sys.version}")
95
+ print(f"Commit hash: {commit}")
96
+
97
+ torch_command = os.environ.get(
98
+ "TORCH_COMMAND",
99
+ "pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118",
100
+ )
101
+
102
+ sys.argv, skip_install = extract_arg(sys.argv, "--skip-install")
103
+ if skip_install:
104
+ return
105
+
106
+ sys.argv, reinstall_torch = extract_arg(sys.argv, "--reinstall-torch")
107
+ ngrok = "--ngrok" in sys.argv
108
+
109
+ if reinstall_torch or not is_installed("torch") or not is_installed("torchaudio"):
110
+ run(
111
+ f'"{python}" -m {torch_command}',
112
+ "Installing torch and torchaudio",
113
+ "Couldn't install torch",
114
+ )
115
+
116
+ if not is_installed("pyngrok") and ngrok:
117
+ run_pip("install pyngrok", "ngrok")
118
+
119
+ run(
120
+ f'"{python}" -m pip install -r requirements.txt',
121
+ desc=f"Installing requirements",
122
+ errdesc=f"Couldn't install requirements",
123
+ )
124
+
125
+
126
+ def start():
127
+ os.environ["PATH"] = (
128
+ os.path.join(os.path.dirname(__file__), "bin")
129
+ + os.pathsep
130
+ + os.environ.get("PATH", "")
131
+ )
132
+ subprocess.run(
133
+ [python, "webui.py", *sys.argv[1:]],
134
+ )
135
+
136
+
137
+ if __name__ == "__main__":
138
+ prepare_environment()
139
+ start()
mozaikukun.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import os
3
+ from glob import glob
4
+ import threading
5
+ from multiprocessing import Process, Queue
6
+ import json
7
+ import time
8
+
9
+ from PIL.Image import Image
10
+ from ultralytics import YOLO
11
+ from PIL import Image, ImageDraw
12
+ from typing import Dict, Tuple
13
+
14
+ BLOCK_SIZE_RATIO = 100
15
+ MARGIN_FACTOR = 3
16
+ MARGIN_EXTRA = 20
17
+ DEVICE='cpu' # Specify the device number when using CUDA. Example: DEVICE='0'
18
+
19
+
20
+ def calculate_pixel_block_and_margin(image: Image.Image) -> Tuple[int, int]:
21
+ """
22
+ Calculate the pixel block size and margin based on image dimension.
23
+ """
24
+ block_size = math.ceil(max(image.width, image.height) / BLOCK_SIZE_RATIO)
25
+ margin = block_size * MARGIN_FACTOR + MARGIN_EXTRA
26
+ return block_size, margin
27
+
28
+
29
+ def resize_bounding_box(bounding_box: Dict[str, int], margin: int, block_size: int) -> Dict[str, int]:
30
+ """
31
+ Resize the bounding box by adding the margin.
32
+ """
33
+ bounding_box["x1"] = int((bounding_box["x1"] - margin) / block_size) * block_size
34
+ bounding_box["x2"] = int(math.ceil((bounding_box["x2"] + margin) / block_size) * block_size)
35
+
36
+ bounding_box["y1"] = int((bounding_box["y1"] - margin) / block_size) * block_size
37
+ bounding_box["y2"] = int(math.ceil((bounding_box["y2"] + margin) / block_size) * block_size)
38
+
39
+ return bounding_box
40
+
41
+
42
+ def create_transparent_mask(segment_box: Dict[str, int], segments: Dict[str, list]) -> tuple[Image, Image]:
43
+ """
44
+ Create a transparent mask for image segmentation.
45
+ """
46
+ empty_mask = Image.new("RGBA", (
47
+ int(segment_box["x2"] - segment_box["x1"]), int(segment_box["y2"] - segment_box["y1"])), (0, 0, 0, 0))
48
+ mask = Image.new('RGBA', (
49
+ int(segment_box["x2"] - segment_box["x1"]), int(segment_box["y2"] - segment_box["y1"])), (0, 0, 0, 0))
50
+ mask_draw = ImageDraw.Draw(mask)
51
+
52
+ adjusted_x_segments = [math.ceil(segment_x) - segment_box["x1"] for segment_x in segments['x']]
53
+ adjusted_y_segments = [math.ceil(segment_y) - segment_box["y1"] for segment_y in segments['y']]
54
+
55
+ polygon_points = list(zip(adjusted_x_segments, adjusted_y_segments))
56
+ mask_draw.polygon(polygon_points, fill=(255, 255, 255, 255))
57
+
58
+ for i in range(len(polygon_points) - 1):
59
+ mask_draw.line([polygon_points[i], polygon_points[i + 1]], fill=(255, 255, 255, 255), width=5)
60
+
61
+ return mask, empty_mask
62
+
63
+
64
+ def apply_mask_on_image(cropped_region: Image.Image, segment_box: Dict[str, int], mask: Image.Image,
65
+ empty_mask: Image.Image) -> Image.Image:
66
+ """
67
+ Apply the created mask on the image.
68
+ """
69
+ segment_region = cropped_region.crop(
70
+ (segment_box["x1"], segment_box["y1"], segment_box["x2"], segment_box["y2"]))
71
+ empty_mask.paste(segment_region)
72
+
73
+ masked_image = Image.new("RGBA", empty_mask.size)
74
+ masked_image.paste(empty_mask, mask=mask)
75
+ return masked_image
76
+
77
+
78
+ def generate_mosaic(masked_image: Image.Image, block_size: int) -> Image.Image:
79
+ """
80
+ Generate a mosaic image from the masked image and block size.
81
+ """
82
+ small_masked_image = masked_image.resize(
83
+ (int(masked_image.size[0] // block_size), int(masked_image.size[1] // block_size)), Image.BILINEAR)
84
+ mosaic_masked_image = small_masked_image.resize(masked_image.size, Image.NEAREST)
85
+ return mosaic_masked_image
86
+
87
+
88
+ def image_detection_worker(process_id: int, input_queue: Queue, result_queue: Queue):
89
+ print(f"start subprocess {process_id}")
90
+ object_detector = YOLO("yolov8x.pt")
91
+ segmenter = YOLO("myseg3.pt")
92
+ while True:
93
+ img, name = input_queue.get()
94
+ print(f"{process_id}: get {name}")
95
+ start_time = time.time()
96
+ result_queue.put((process_and_analyze_image(img, object_detector, segmenter), name))
97
+ end_time = time.time()
98
+ elapsed_time = end_time - start_time
99
+ print(f"{process_id}: put ({elapsed_time} sec) {name}")
100
+
101
+
102
+ def process_and_analyze_image(image: Image.Image, object_detector: YOLO, segmenter: YOLO) -> Dict:
103
+ """
104
+ Process a single image, analyze it and save the result.
105
+ """
106
+ original_image = image.convert("RGBA")
107
+
108
+ block_size, margin = calculate_pixel_block_and_margin(original_image)
109
+
110
+ detection_results = object_detector(original_image, save=False, device=DEVICE, project="yolov8x", name="pname1",
111
+ verbose=False)
112
+
113
+ result = {}
114
+ sensitive_areas = ["pussy", "penis", "sex"]
115
+
116
+ for detection in detection_results:
117
+ for detected_object in json.loads(detection.tojson()):
118
+ if detected_object["name"] != "person":
119
+ continue
120
+ bounding_box = resize_bounding_box(detected_object["box"], margin, block_size)
121
+
122
+ cropped_region = original_image.crop(
123
+ (bounding_box["x1"], bounding_box["y1"], bounding_box["x2"], bounding_box["y2"]))
124
+
125
+ segmentation_results = segmenter(cropped_region, save=False, device=DEVICE, project="myseg2", name="pname2",
126
+ verbose=False)
127
+ for segmentation in segmentation_results:
128
+ for segmented_object in json.loads(segmentation.tojson()):
129
+ name = segmented_object["name"]
130
+ #if name not in sensitive_areas:
131
+ # continue
132
+ segments = segmented_object["segments"]
133
+ segment_box = resize_bounding_box(segmented_object["box"], margin, block_size)
134
+
135
+ if len(segments.get("x", [])) <= 2:
136
+ continue
137
+ if len(segments.get("y", [])) <= 2:
138
+ continue
139
+
140
+ mask, empty_mask = create_transparent_mask(segment_box, segments)
141
+ masked_image = apply_mask_on_image(cropped_region, segment_box, mask, empty_mask)
142
+ mosaic_masked_image = generate_mosaic(masked_image, block_size)
143
+
144
+ final_image = Image.new('RGBA', original_image.size)
145
+ mosaic_position = (
146
+ int(bounding_box["x1"] + segment_box["x1"]), int(bounding_box["y1"] + segment_box["y1"]))
147
+ final_image.paste(mosaic_masked_image, mosaic_position)
148
+ if name not in result:
149
+ result[name] = []
150
+ result[name].append(increase_image_opacity(final_image))
151
+
152
+ return result
153
+
154
+
155
+ def increase_image_opacity(img: Image.Image, rate: int = 10) -> Image.Image:
156
+ img = img.convert("RGBA") # ensure image has alpha channel
157
+
158
+ datas = img.getdata()
159
+ new_data = []
160
+ for item in datas:
161
+ if item[3] != 0:
162
+ new_data.append((item[0], item[1], item[2], min(255, int(item[3] * rate))))
163
+ else:
164
+ new_data.append(item) # leave fully transparent pixel as it is
165
+ img.putdata(new_data)
166
+ return img
167
+
168
+
169
+ def main():
170
+ print("Start processing...")
171
+ worker_count = 3
172
+
173
+ worker_processes = []
174
+ worker_queue = {}
175
+ task_queue = Queue()
176
+ result_queue = Queue()
177
+
178
+ for worker_number in range(worker_count):
179
+ worker_queue[worker_number] = Queue()
180
+ p = Process(
181
+ target=image_detection_worker,
182
+ args=(worker_number, task_queue, result_queue, worker_queue[worker_number]))
183
+ p.start()
184
+ worker_processes.append(p)
185
+
186
+ output_dir = "output"
187
+
188
+ os.makedirs(output_dir, exist_ok=True)
189
+
190
+ for image_path in glob("input/*.jpg"):
191
+ task_queue.put((Image.open(image_path), image_path, {}))
192
+
193
+ sensitive_areas = ["pussy", "penis", "sex"]
194
+
195
+ while True:
196
+ result_data, image_name = result_queue.get()
197
+ for sensitive_area in sensitive_areas:
198
+ image_number = 0
199
+ for result_image in result_data[sensitive_area]:
200
+ new_image_filename = (f"{os.path.splitext(os.path.basename(image_name))[0]}_"
201
+ f"{sensitive_area}_{image_number}.png")
202
+ image_save_path = os.path.join(output_dir, new_image_filename)
203
+ result_image.save(image_save_path)
204
+ image_number += 1
205
+
206
+
207
+ if __name__ == '__main__':
208
+ main()
package.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ libgl1-mesa-glx
2
+ libgl1-mesa-dri
3
+
requirements.txt ADDED
Binary file (108 Bytes). View file