rein0421 commited on
Commit
82291b8
·
verified ·
1 Parent(s): 374194c

Upload 7 files

Browse files
Files changed (7) hide show
  1. 1026.pt +3 -0
  2. Dockerfile +24 -0
  3. app.py +941 -0
  4. main.png +0 -0
  5. output_vectors.json +0 -0
  6. requirements.txt +72 -0
  7. sums_data.json +0 -0
1026.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:87af30931732d6ac63bd65230b33f22ca157a3dd97e72c361b55be8a3a2dfade
3
+ size 22568035
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use the official Python 3.10.9 image
2
+ FROM python:3.10.9
3
+
4
+ # Copy the current directory contents into the container at .
5
+ COPY . .
6
+ # Install dependencies for OpenCV and other requirements
7
+ RUN apt-get update && apt-get install -y libgl1 libglib2.0-0 curl
8
+ # Set the working directory to /
9
+ WORKDIR /
10
+ # Create a user to avoid permission issues
11
+ RUN useradd -m appuser
12
+ USER appuser
13
+
14
+ # Set the working directory to /home/appuser
15
+ WORKDIR /home/appuser
16
+
17
+ # Add /home/appuser/.local/bin to PATH
18
+ ENV PATH="/home/appuser/.local/bin:${PATH}"
19
+
20
+ # Install requirements.txt
21
+ RUN pip install --no-cache-dir --upgrade -r /requirements.txt
22
+
23
+ # Start the FastAPI app on port 7860, the default port expected by Spaces
24
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,941 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Commented out IPython magic to ensure Python compatibility.
4
+
5
+
6
+
7
+
8
+ import os
9
+ import supervision as sv
10
+ from PIL import Image, ImageFilter
11
+ import numpy as np
12
+ import cv2
13
+ import pycocotools.mask as mask_util
14
+
15
+ from fastapi import FastAPI, File, UploadFile, Form
16
+ from fastapi.middleware.cors import CORSMiddleware
17
+ from fastapi.responses import FileResponse, HTMLResponse
18
+ import shutil
19
+ import json
20
+ from pathlib import Path
21
+ import nest_asyncio
22
+ import uvicorn
23
+ from pyngrok import ngrok
24
+ from diffusers import StableDiffusionInpaintPipeline
25
+ import torch
26
+ from simple_lama_inpainting import SimpleLama
27
+ from sklearn.cluster import (
28
+ KMeans, AgglomerativeClustering, DBSCAN, MiniBatchKMeans, Birch,
29
+ SpectralClustering, MeanShift, OPTICS
30
+ )
31
+ from sklearn.decomposition import PCA
32
+ from sklearn.metrics import silhouette_score
33
+ from sklearn.neighbors import KNeighborsClassifier
34
+ from torchvision import transforms
35
+ import threading
36
+ import concurrent.futures
37
+ from typing import Tuple
38
+ from types import SimpleNamespace
39
+ import subprocess
40
+ import uuid
41
+ from datetime import datetime
42
+ from ultralytics import YOLO
43
+ import math
44
+ import numpy as np
45
+ import matplotlib.pyplot as plt
46
+
47
+ #この下のコードは特定の領域をマスクしないタイプのコード
48
+ import uuid
49
+ from datetime import datetime
50
+
51
+
52
+
53
+ import cv2
54
+ import numpy as np
55
+ from datetime import datetime
56
+ from ultralytics import YOLO
57
+ from PIL import Image
58
+ app = FastAPI()
59
+ # CORSミドルウェアの追加
60
+ app.add_middleware(
61
+ CORSMiddleware,
62
+ allow_origins=["*"], # ここを適切なオリジンに設定することもできます
63
+ allow_credentials=True,
64
+ allow_methods=["*"],
65
+ allow_headers=["*"],
66
+ )
67
+
68
+
69
+ HOME = "./"
70
+
71
+ dangerarray=[10,30,90,50,80,20,40,70,100,60]#ここに各クラスターの危険度を設定しておく
72
+ #ここで認識する精度を上げたり下げたりできる
73
+
74
+ thresholds = {
75
+ 'text': 0.1,
76
+ 'name tag': 0.1,
77
+ 'license plate': 0.3,
78
+ 'Mail': 0.3,
79
+ 'Documents': 0.3,
80
+ 'QR codes': 0.4,
81
+ 'barcodes': 0.4,
82
+ 'map': 0.5,
83
+ 'digital screens': 0.6,
84
+ 'information board': 0.5,
85
+ 'signboard': 0.3,
86
+ 'poster': 0.8,
87
+ 'sign': 0.3,
88
+ 'logo': 0.3,
89
+ 'card': 0.4,
90
+ 'window': 0.2,
91
+ 'mirror': 0.2,
92
+ 'Famous landmark': 0.7,
93
+ 'cardboard': 0.6,
94
+ 'manhole': 0.6,
95
+ 'utility pole': 0.7
96
+ }
97
+
98
+ '''
99
+
100
+ '''
101
+
102
+ # Define paths
103
+
104
+ CONFIG_PATH = os.path.join(HOME, "GroundingDINO/groundingdino/config/GroundingDINO_SwinT_OGC.py")
105
+ WEIGHTS_NAME = "groundingdino_swint_ogc.pth"
106
+ WEIGHTS_PATH = os.path.join(HOME, "weights", WEIGHTS_NAME)
107
+ from PIL import Image
108
+
109
+ def is_bright(pixel):
110
+ # ピクセルの輝度を計算して明るさを判定する
111
+ r, g, b = pixel
112
+ brightness = (0.299 * r + 0.587 * g + 0.114 * b) # 輝度の計算
113
+ return brightness > 127 # 閾値を127に設定
114
+
115
+ def analyze_mask_brightness(original_image_path, mask_image_path):
116
+ # 画像を開く
117
+ original_img = Image.open(original_image_path).convert('RGB')
118
+ mask_img = Image.open(mask_image_path).convert('L') # グレースケールに変換
119
+
120
+ width, height = original_img.size
121
+
122
+ if mask_img.size != (width, height):
123
+ print("エラー: マスク画像と元画像のサイズが一致していません。")
124
+ return
125
+
126
+ # 明るいピクセルと暗いピクセルのカウント
127
+ bright_count = 0
128
+ dark_count = 0
129
+
130
+ for y in range(height):
131
+ for x in range(width):
132
+ mask_value = mask_img.getpixel((x, y))
133
+ if mask_value > 127: # マスクが白(対象領域)ならば
134
+ pixel = original_img.getpixel((x, y))
135
+ if is_bright(pixel):
136
+ bright_count += 1
137
+ else:
138
+ dark_count += 1
139
+
140
+ # 明るさの結果を判定
141
+ brightness_result = 1 if bright_count > dark_count else 2
142
+
143
+ return brightness_result
144
+
145
+ def classify_mask_size(mask_image_path, small_threshold, medium_threshold, large_threshold):
146
+ # マスク画像を開く
147
+ mask_img = Image.open(mask_image_path).convert('L') # グレースケールに変換
148
+
149
+ width, height = mask_img.size
150
+ total_pixels = width * height
151
+ white_pixel_count = 0
152
+
153
+ # マスク画像の白いピクセルをカウント
154
+ for y in range(height):
155
+ for x in range(width):
156
+ mask_value = mask_img.getpixel((x, y))
157
+ if mask_value > 127: # 白いピクセルと判断
158
+ white_pixel_count += 1
159
+
160
+ # 白いピクセルの割合を計算
161
+ mask_area_ratio = (white_pixel_count / total_pixels) * 100
162
+
163
+ # マスクサイズを分類
164
+ if mask_area_ratio <= small_threshold:
165
+ size_category = 1 # すごく小さい
166
+ elif mask_area_ratio <= medium_threshold:
167
+ size_category = 2 # 小さい
168
+ elif mask_area_ratio <= large_threshold:
169
+ size_category = 3 # 大きい
170
+ else:
171
+ size_category = 4 # すごく大きい
172
+
173
+ return size_category
174
+
175
+ def analyze_mask_combined(original_image_path, mask_image_path, small_threshold, medium_threshold, large_threshold):
176
+ # マスクの大きさを判定
177
+ size_category = classify_mask_size(mask_image_path, small_threshold, medium_threshold, large_threshold)
178
+
179
+ # マスク部分の明るさを判定
180
+ brightness_result = analyze_mask_brightness(original_image_path, mask_image_path)
181
+
182
+ # 結果を出力
183
+ size_text = {1: "すごく小さい", 2: "小さい", 3: "大きい", 4: "すごく大きい"}
184
+ print(f"マスクの大きさ: {size_text[size_category]} ({size_category})")
185
+ print(f"マスクの明るさ: {brightness_result}")
186
+ result={
187
+ 'size':size_category,
188
+ 'brightness':brightness_result
189
+ }
190
+ return result
191
+
192
+
193
+
194
+ #この下で消去対象を決定
195
+ def decide_to_object(risk_level):
196
+ '''
197
+ tex = [
198
+ 'text','Name tag', 'License plate', 'Mail', 'Documents', 'QR codes',
199
+ 'barcodes', 'Map', 'Digital screens', 'information board',
200
+ 'signboard', 'poster', 'sign', 'utility pole'
201
+
202
+ ]
203
+ '''
204
+ tex = [
205
+ 'text', 'License plate', 'Digital screens',
206
+ 'signboard', 'poster', 'sign', 'logo', 'card', 'window', 'mirror',
207
+ 'Famous landmark', 'cardboard', 'manhole', 'utility pole'
208
+
209
+ ]
210
+
211
+
212
+ #この配列の要素の順番を変えると消える順番が変わる。
213
+ risk_level = int(risk_level / 20)*(len(tex)/10)#個数決定(1/2)
214
+ return tex[:int(risk_level)+1]
215
+
216
+ def create_mask(image, x1, y1, x2, y2):
217
+ # Create a black image with the same size as the input image
218
+ mask = np.zeros((image.shape[0], image.shape[1]), dtype=np.uint8)
219
+
220
+ # Draw a white rectangle on the mask where the object is located
221
+ cv2.rectangle(mask, (int(x1), int(y1)), (int(x2), int(y2)), 255, -1)
222
+
223
+ return mask
224
+
225
+
226
+ def special_process_image_yolo(risk_level, image_path, point1, point2, thresholds=None):
227
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
228
+ print(device)
229
+ # YOLOv8モデルをロードし、GPUに移動
230
+ model = YOLO('./1026.pt') # モデルのパスを指定
231
+ model.to(device) # モデルをGPUに移動
232
+
233
+
234
+ # タイムスタンプを作成
235
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
236
+
237
+ # リスクレベルに基づいた減衰率の計算
238
+ def logistic_decay(risk_level, k=0.1, r0=50):
239
+ return 1 / (1 + np.exp(-k * (risk_level - r0)))
240
+
241
+ decay_factor = logistic_decay(risk_level)
242
+ adjusted_thresholds = {key: max(value - decay_factor + 0.8, 0.01) / 2 for key, value in thresholds.items()}
243
+
244
+
245
+
246
+ # 画像の読み込みとRGB変換
247
+ image = cv2.imread(image_path)
248
+ image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
249
+ image_np = np.array(image_rgb, dtype=np.uint8)
250
+
251
+ # 推論実行
252
+ results = model(image_rgb)
253
+
254
+ # 初期化したマスク画像
255
+ mask = np.zeros((image_np.shape[0], image_np.shape[1]), dtype=np.uint8)
256
+
257
+ # 各検出結果に基づきマスク作成
258
+ for box in results[0].boxes:
259
+ x1, y1, x2, y2 = map(int, box.xyxy[0]) # ボックスの座標
260
+ confidence = box.conf[0]
261
+ class_id = box.cls[0]
262
+ object_type = model.names[int(class_id)]
263
+
264
+ # クラス名に基づいたしきい値
265
+ threshold = adjusted_thresholds.get(object_type, 0.5)
266
+
267
+ if confidence >= threshold:
268
+ mask[y1:y2, x1:x2] = 255 # ボックス領域を白に設定
269
+
270
+ # 絶対座標に変換した点の範囲を黒に設定
271
+ p1_x, p1_y = int(point1[0] * image_np.shape[1]), int(point1[1] * image_np.shape[0])
272
+ p2_x, p2_y = int(point2[0] * image_np.shape[1]), int(point2[1] * image_np.shape[0])
273
+ x_min, y_min = max(0, min(p1_x, p2_x)), max(0, min(p1_y, p2_y))
274
+ x_max, y_max = min(image_np.shape[1], max(p1_x, p2_x)), min(image_np.shape[0], max(p1_y, p2_y))
275
+ mask[y_min:y_max, x_min:x_max] = 0 # 範囲を黒に設定
276
+
277
+ # デバッグ用に白い長方形を描画
278
+ debug_image = image_np.copy()
279
+ cv2.rectangle(debug_image, (x_min, y_min), (x_max, y_max), (255, 255, 255), 2)
280
+
281
+ # デバッグ画像とマスク画像を保存
282
+ debug_image_pil = Image.fromarray(debug_image)
283
+ debug_image_pil.save(f"./debug_image_with_rectangle_{timestamp}.jpg")
284
+
285
+ mask_image_pil = Image.fromarray(mask)
286
+ mask_image_pil.save(f"./final_mask_{timestamp}.jpg")
287
+
288
+ return f"./final_mask_{timestamp}.jpg"
289
+
290
+
291
+
292
+
293
+
294
+ def convert_image_format(input_path, output_format="png"):
295
+ """
296
+ 画像をJPGからPNGまたはPNGからJPGに変換する関数。
297
+
298
+ Parameters:
299
+ - input_path: 変換したい元画像のパス
300
+ - output_format: 出力形式 ("png" または "jpg" を指定、デフォルトは "png")
301
+
302
+ Returns:
303
+ - output_path: 変換された画像の出力パス
304
+ """
305
+ # サポートされているフォーマットかを確認
306
+ if output_format not in ["png", "jpg", "jpeg"]:
307
+ raise ValueError("サポートされている出力形式は 'png' または 'jpg' です��")
308
+
309
+ # 画像の読み込み
310
+ image = cv2.imread(input_path)
311
+ if image is None:
312
+ raise ValueError(f"画像が見つかりません: {input_path}")
313
+
314
+ # 出力パスの生成
315
+ base_name = os.path.splitext(os.path.basename(input_path))[0]
316
+ output_path = f"{base_name}.{output_format}"
317
+
318
+ # 画像の保存
319
+ if output_format == "png":
320
+ cv2.imwrite(output_path, image, [cv2.IMWRITE_PNG_COMPRESSION, 9]) # PNG形式で最高圧縮率
321
+ else:
322
+ cv2.imwrite(output_path, image, [cv2.IMWRITE_JPEG_QUALITY, 90]) # JPG形式で高画質
323
+
324
+ return output_path
325
+
326
+
327
+
328
+
329
+
330
+ #この下は、openCV
331
+ def inpaint_image_with_mask(image_path, mask_path, output_path, inpaint_radius=5, inpaint_method=cv2.INPAINT_TELEA):
332
+ """
333
+ マスク画像を使用して元画像のインペイントを行う関数。
334
+
335
+ Parameters:
336
+ - image_path: 元画像のパス
337
+ - mask_path: マスク画像のパス(修復したい領域が白、その他が黒)
338
+ - output_path: インペイント結果の出力パス
339
+ - inpaint_radius: インペイントの半径(デフォルトは5)
340
+ - inpaint_method: インペイントのアルゴリズム(デフォルトはcv2.INPAINT_TELEA)
341
+
342
+ Returns:
343
+ - inpainted_image: インペイントされた画像
344
+ """
345
+ # 画像とマスクを読み込み
346
+ image = cv2.imread(image_path)
347
+ mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) # マスクはグレースケールで読み込み
348
+
349
+ # マスク画像が正常に読み込めたかチェック
350
+ if image is None:
351
+ raise ValueError(f"元画像が見つかりません: {image_path}")
352
+ if mask is None:
353
+ raise ValueError(f"マスク画像が見つかりません: {mask_path}")
354
+
355
+ # マスク画像が元画像と同じサイズでない場合、リサイズ
356
+ if image.shape[:2] != mask.shape[:2]:
357
+ print(f"マスク画像のサイズを元画像に合わせてリサイズします: {mask.shape} -> {image.shape[:2]}")
358
+ mask = cv2.resize(mask, (image.shape[1], image.shape[0]))
359
+
360
+ # インペイント処理
361
+ inpainted_image = cv2.inpaint(image, mask, inpaint_radius, inpaint_method)
362
+
363
+ # インペイント結果を保存
364
+ cv2.imwrite(output_path, inpainted_image)
365
+
366
+ return output_path
367
+
368
+
369
+ def stamp_image_with_mask(base_image_path, mask_path,output_path,stamp_image_path='./main.png'):
370
+ """
371
+ マスク画像を使用して元画像に別の画像を埋め込む関数。
372
+
373
+ Parameters:
374
+ - base_image_path: 元画像のパス
375
+ - mask_path: マスク画像のパス(埋め込みたい領域が白、その他が黒)
376
+ - embed_image_path: 埋め込み用画像のパス
377
+ - output_path: 結果の出力パス
378
+
379
+ Returns:
380
+ - output_path: 埋め込み処理された画像の出力パス
381
+ """
382
+ # 画像とマスクを読み込み
383
+ base_image = cv2.imread(base_image_path)
384
+ mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
385
+ embed_image = cv2.imread(stamp_image_path)
386
+
387
+ # 画像が正常に読み込めたかチェック
388
+ if base_image is None:
389
+ raise ValueError(f"元画像が見つかりません: {base_image_path}")
390
+ if mask is None:
391
+ raise ValueError(f"マスク画像が見つかりません: {mask_path}")
392
+ if embed_image is None:
393
+ raise ValueError(f"埋め込み用画像が見つかりません: {stamp_image_path}")
394
+
395
+ # マスク画像と埋め込み画像を元画像と同じサイズにリサイズ
396
+ if base_image.shape[:2] != mask.shape[:2]:
397
+ print(f"マスク画像のサイズを元画像に合わせてリサイズします: {mask.shape} -> {base_image.shape[:2]}")
398
+ mask = cv2.resize(mask, (base_image.shape[1], base_image.shape[0]))
399
+ if base_image.shape[:2] != embed_image.shape[:2]:
400
+ print(f"埋め込み画像のサイズを元画像に合わせてリサイズします: {embed_image.shape[:2]} -> {base_image.shape[:2]}")
401
+ embed_image = cv2.resize(embed_image, (base_image.shape[1], base_image.shape[0]))
402
+
403
+ # マスク領域に埋め込み画像を配置
404
+ embedded_image = base_image.copy()
405
+ embedded_image[mask == 255] = embed_image[mask == 255]
406
+
407
+ # 結果を保存
408
+ cv2.imwrite(output_path, embedded_image)
409
+
410
+ return output_path
411
+ import torch
412
+ from PIL import Image, ImageFilter
413
+ import numpy as np
414
+ from simple_lama_inpainting import SimpleLama
415
+
416
+ def inpaint_image_with_mask1(img_path, mask_path, output_path, resize_factor=0.5):
417
+ print('lama')
418
+
419
+ # GPUが利用可能か確認
420
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
421
+
422
+ # 画像とマスクを読み込み
423
+ image = Image.open(img_path).convert("RGB") # 画像をRGBに変換
424
+ mask = Image.open(mask_path).convert('L') # マスクをグレースケールに変換
425
+
426
+ # 画像とマスクのサイズを合わせる
427
+ mask = mask.resize(image.size, Image.NEAREST)
428
+
429
+ # マスクのエッジをぼかす (Gaussian Blur)
430
+ blurred_mask = mask.filter(ImageFilter.GaussianBlur(radius=3)) # 半径3ピクセルでぼかし
431
+
432
+ # SimpleLama インスタンスを作成
433
+ simple_lama = SimpleLama()
434
+
435
+ # 画像とマスクをNumPy配列に変換
436
+ image_np = np.array(image)
437
+ mask_np = np.array(blurred_mask) / 255.0 # マスクを0-1範囲にスケーリング
438
+
439
+ # 入力画像とマスクをSimpleLamaに渡してインペイント
440
+ inpainted_np = simple_lama(image_np, mask_np) # NumPy配列を渡す
441
+
442
+ # 結果を画像として保存
443
+ result_image = Image.fromarray(np.uint8(inpainted_np)) # NumPy array -> PIL Image
444
+
445
+ # 出力画像をリサイズ
446
+ new_size = (int(result_image.width * resize_factor), int(result_image.height * resize_factor))
447
+ result_image = result_image.resize(new_size, Image.ANTIALIAS)
448
+
449
+ # 結果を保存
450
+ result_image.save(output_path)
451
+ print(f"Inpainted image saved at {output_path}")
452
+ return output_path
453
+
454
+
455
+
456
+
457
+
458
+
459
+
460
+
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+
469
+
470
+
471
+ # 保存先のディレクトリを指定
472
+ SAVE_DIR = Path("./saved_images")
473
+ SAVE_DIR.mkdir(parents=True, exist_ok=True)
474
+
475
+ def save_image(file, filename):
476
+ """画像ファイルを指定ディレクトリに保存"""
477
+ filepath = SAVE_DIR / filename
478
+ with open(filepath, "wb") as buffer:
479
+ shutil.copyfileobj(file, buffer)
480
+ return filepath
481
+
482
+ @app.post("/create-mask-and-inpaint-opencv")
483
+ async def create_mask_and_inpaint_opencv(image: UploadFile = File(...), risk_level: int = Form(...)):
484
+ point1 = (0.00000000000002, 0.00000000000002)
485
+ point2 = (0.00000000000001, 0.00000000000001)
486
+ input_path = save_image(image.file, "input.jpg")
487
+ mask_path = special_process_image_yolo(risk_level, input_path, point1, point2, thresholds)
488
+
489
+ output_path = SAVE_DIR / "output_opencv.jpg"
490
+ # OpenCVでインペイント
491
+ inpaint_image_with_mask(input_path, mask_path, output_path)
492
+
493
+ return FileResponse(output_path)
494
+ @app.post("/create-mask-and-inpaint-stamp")
495
+ async def create_mask_and_inpaint_opencv(image: UploadFile = File(...), risk_level: int = Form(...)):
496
+ point1 = (0.00000000000002, 0.00000000000002)
497
+ point2 = (0.00000000000001, 0.00000000000001)
498
+ input_path = save_image(image.file, "input.jpg")
499
+ mask_path = special_process_image_yolo(risk_level, input_path, point1, point2, thresholds)
500
+
501
+ output_path = SAVE_DIR / "output_opencv.jpg"
502
+ # OpenCVでインペイント
503
+ stamp_image_with_mask(input_path, mask_path, output_path)
504
+
505
+ return FileResponse(output_path)
506
+
507
+
508
+
509
+
510
+
511
+
512
+ @app.post("/create-mask-and-inpaint-simple-lama")
513
+ async def create_mask_and_inpaint_simple_lama(image: UploadFile = File(...), risk_level: int = Form(...)):
514
+ input_path = save_image(image.file, "input.jpg")
515
+ point1 = (0.00000000000002, 0.00000000000002)
516
+ point2 = (0.00000000000001, 0.00000000000001)
517
+ mask_path = special_process_image_yolo(risk_level, input_path, point1, point2, thresholds)
518
+ output_path = SAVE_DIR / "output_simple_lama.jpg"
519
+ # SimpleLamaでインペイント
520
+ inpaint_image_with_mask1(input_path, mask_path, output_path, resize_factor=1)
521
+
522
+ return FileResponse(output_path)
523
+
524
+
525
+ #下のendpointは特定領域をマスクしないタイプのもの
526
+
527
+
528
+ #下記はDeepFillv2
529
+
530
+
531
+
532
+
533
+
534
+
535
+
536
+
537
+
538
+
539
+
540
+
541
+
542
+
543
+
544
+
545
+
546
+
547
+
548
+
549
+
550
+
551
+
552
+
553
+ # ベクトル化対象のオブジェクトリスト
554
+ TEXT_PROMPTS = [
555
+ 'text','Name tag', 'License plate', 'Mail', 'Documents', 'QR codes',
556
+ 'barcodes', 'Map', 'Digital screens', 'information board',
557
+ 'signboard', 'poster', 'sign', 'logo', 'card', 'window', 'mirror',
558
+ 'Famous landmark', 'cardboard', 'manhole', 'utility pole'
559
+ ]
560
+ BOX_THRESHOLD = 0.3
561
+ TEXT_THRESHOLD = 0.3
562
+
563
+ # クラスタリング結果をJSONファイルから読み込む関数
564
+ def load_sums_from_json(filepath):
565
+ with open(filepath, 'r') as json_file:
566
+ sums = json.load(json_file)
567
+ return sums
568
+
569
+ # ベクトルデータをJSONファイルから読み込む関数
570
+ def load_vectors_from_json(filepath):
571
+ with open(filepath, 'r') as json_file:
572
+ data = json.load(json_file)
573
+ return data
574
+
575
+ # 新しい画像を分類する関数
576
+ def classify_new_image(new_image_vector, sums_data, loaded_vectors, loaded_object_names, k=1):
577
+ cluster_centers = []
578
+ for cluster in sums_data:
579
+ indices = [loaded_object_names.index(obj_name) for obj_name in cluster]
580
+ cluster_vectors = np.array([loaded_vectors[obj_name] for obj_name in cluster])
581
+ cluster_center = np.mean(cluster_vectors, axis=0)
582
+ cluster_centers.append(cluster_center)
583
+
584
+ knn = KNeighborsClassifier(n_neighbors=k)
585
+ knn.fit(cluster_centers, range(len(cluster_centers)))
586
+
587
+ new_image_label = knn.predict([new_image_vector])
588
+ return new_image_label[0]
589
+
590
+ import torch
591
+ import cv2
592
+ import numpy as np
593
+ from ultralytics import YOLO # YOLOv8ライブラリ
594
+
595
+ def process_image_vec(image_path):
596
+ # GPUを使用できるか確認
597
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
598
+ print(device)
599
+ # YOLOv8モデルをロードし、GPUに移動
600
+ model = YOLO('./1026.pt') # モデルのパスを指定
601
+ model.to(device) # モデルをGPUに移動
602
+
603
+ # 初期化
604
+ object_vector = np.zeros(len(TEXT_PROMPTS))
605
+
606
+ # 画像の読み込み
607
+ image = cv2.imread(image_path)
608
+ image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
609
+
610
+ # YOLOで推論を実行
611
+ results = model(image_rgb) # 推論を実行
612
+
613
+ # 各プロンプトごとに確認
614
+ for i, text_prompt in enumerate(TEXT_PROMPTS):
615
+ prompt_sum = 0 # 各プロンプトに対応するスコアの合計
616
+
617
+ for box in results[0].boxes:
618
+ class_id = int(box.cls[0])
619
+ confidence = box.conf[0]
620
+ detected_class = model.names[class_id]
621
+
622
+ # 検出クラス名とテキストプロンプトの一致を確認
623
+ if text_prompt.lower() == detected_class.lower():
624
+ prompt_sum += confidence # クラスが一致した場合、信頼度を加算
625
+
626
+ # object_vectorにスコアを格納
627
+ object_vector[i] = prompt_sum
628
+
629
+ print(object_vector)
630
+ return object_vector.tolist()
631
+
632
+
633
+
634
+
635
+
636
+ # APIのエンドポイント
637
+ @app.post("/classify-image/")
638
+ async def classify_image(file: UploadFile = File(...)):
639
+ image_path = "./temp_image.jpg"
640
+
641
+ # アップロードされた画像を保存
642
+ with open(image_path, "wb") as buffer:
643
+ buffer.write(await file.read())
644
+
645
+ # 画像をベクトル化
646
+ new_image_vector = process_image_vec(image_path)
647
+
648
+ # JSONファイルからデータを読み込む
649
+ json_filepath = "./output_vectors.json"
650
+ loaded_data = load_vectors_from_json(json_filepath)
651
+ loaded_vectors = {obj_name: np.array(vector) for obj_name, vector in loaded_data.items()}
652
+ loaded_object_names = list(loaded_vectors.keys())
653
+
654
+ # 既存のクラスタリング結果を読み込む
655
+ sums_data = load_sums_from_json("./sums_data.json")
656
+
657
+ # 新しい画像がどのクラスタに分類されるかを判定
658
+ new_image_cluster = classify_new_image(new_image_vector, sums_data, loaded_vectors, loaded_object_names)
659
+
660
+ return {"danger":dangerarray[int(new_image_cluster + 1)]}#バグったらここを+にして
661
+
662
+
663
+ @app.post("/create-mask-and-inpaint-simple-lama-special")
664
+ async def create_mask_and_inpaint_simple_lama(
665
+ image: UploadFile = File(...),
666
+ risk_level: int = Form(...),
667
+ x1: float = Form(...),
668
+ y1: float = Form(...),
669
+ x2: float = Form(...),
670
+ y2: float = Form(...),
671
+ ):
672
+ # Extract points from the form data
673
+ point1 = [x1, y1]
674
+ point2 = [x2, y2]
675
+
676
+ # Save the input image
677
+ input_path = save_image(image.file, "input.jpg")
678
+ print('1111',point1,point2)
679
+ # Create a mask image (using the new process_image function)
680
+ mask_path = special_process_image_yolo(risk_level, input_path, point1, point2,thresholds=thresholds)
681
+
682
+ # Define the output path for the inpainted image
683
+ output_path = "./output_simple_lama.jpg"
684
+
685
+ # Perform inpainting with SimpleLama
686
+ inpaint_image_with_mask1(input_path, mask_path, output_path, resize_factor=1)
687
+
688
+ # Return the resulting image as a response
689
+ return FileResponse(output_path, media_type="image/jpeg", filename="output_simple_lama.jpg")
690
+
691
+
692
+
693
+
694
+ from PIL import Image
695
+
696
+ def resize_mask_to_match(image_path, mask_path):
697
+ # オリジナル画像とマスク画像を読み込む
698
+ original_image = Image.open(image_path)
699
+ mask_image = Image.open(mask_path)
700
+
701
+ # マスク画像をオリジナル画像のサイズにリサイズ
702
+ resized_mask = mask_image.resize(original_image.size)
703
+
704
+ # マスク画像を上書き保存
705
+ resized_mask.save(mask_path)
706
+
707
+ @app.post("/create-mask-and-inpaint-sum")
708
+ async def create_mask_sum(image: UploadFile = File(...), risk_level: int = Form(...),
709
+ x1: float = Form(...),
710
+ y1: float = Form(...),
711
+ x2: float = Form(...),
712
+ y2: float = Form(...),):
713
+ default_x = 0.001
714
+ default_y = 0.001
715
+
716
+
717
+ point1 = [default_x if math.isnan(x1) else x1, default_y if math.isnan(y1) else y1]
718
+
719
+ point2 = [default_x if math.isnan(x2) else x2, default_y if math.isnan(y2) else y2]
720
+
721
+
722
+ input_path = save_image(image.file, "input.jpg")
723
+ mask_path = special_process_image_yolo(risk_level, input_path, point1, point2,thresholds=thresholds)
724
+ # 現在のタイムスタンプを生成
725
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
726
+ # 一意な識別子を生成
727
+ unique_id = uuid.uuid4().hex
728
+ output_path = f"./output_simple_lama_{timestamp}_{unique_id}.jpg"
729
+
730
+ # OpenCVでインペイント
731
+ inpaint_image_with_mask1(input_path, mask_path, output_path)
732
+
733
+ return FileResponse(output_path)
734
+
735
+
736
+
737
+ @app.get("/", response_class=HTMLResponse)
738
+ async def read_root():
739
+ html_content = """
740
+ <!DOCTYPE html>
741
+ <html lang="ja">
742
+ <head>
743
+ <meta charset="UTF-8">
744
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
745
+ <title>画像処理アプリ</title>
746
+ <!-- Bootstrap CSS -->
747
+ <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
748
+ <!-- jQuery UI CSS -->
749
+ <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
750
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
751
+ <style>
752
+ body {
753
+ background-color: #f0f0f5;
754
+ color: #333;
755
+ text-align: center;
756
+ padding: 40px 20px;
757
+ }
758
+ h1 {
759
+ color: #555;
760
+ margin-bottom: 30px;
761
+ font-weight: bold;
762
+ }
763
+ .image-preview, .processed-preview {
764
+ max-width: 100%;
765
+ height: auto;
766
+ border-radius: 10px;
767
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
768
+ margin-top: 20px;
769
+ }
770
+ #result {
771
+ margin-top: 40px;
772
+ display: none;
773
+ }
774
+ .slider-container {
775
+ text-align: left;
776
+ margin-top: 20px;
777
+ }
778
+ .slider-label {
779
+ font-size: 1.2rem;
780
+ color: #333;
781
+ }
782
+ #slider {
783
+ margin-top: 10px;
784
+ }
785
+ .btn-primary {
786
+ background-color: #007bff;
787
+ border-color: #007bff;
788
+ font-size: 1.2rem;
789
+ padding: 10px 20px;
790
+ border-radius: 50px;
791
+ }
792
+ .btn-primary:hover {
793
+ background-color: #0056b3;
794
+ border-color: #004085;
795
+ }
796
+ .form-control, .custom-select {
797
+ border-radius: 20px;
798
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
799
+ }
800
+ .form-control-file {
801
+ font-size: 1rem;
802
+ }
803
+ .form-group {
804
+ margin-bottom: 25px;
805
+ }
806
+ .btn-success {
807
+ padding: 10px 20px;
808
+ border-radius: 50px;
809
+ }
810
+ </style>
811
+ </head>
812
+ <body>
813
+ <div class="container">
814
+ <h1><i class="fas fa-image"></i> 画像処理アプリ - モザイクとインペイント</h1>
815
+
816
+ <div class="form-group">
817
+ <input type="file" id="uploadImage" class="form-control-file" accept="image/*" onchange="previewImage()">
818
+ </div>
819
+ <img id="uploadedImage" class="image-preview" src="#" alt="アップロードされた画像" style="display: none;">
820
+
821
+ <div class="form-group mt-4">
822
+ <label for="processingType">処理方法を選択:</label>
823
+ <select id="processingType" class="custom-select">
824
+ <option value="opencv">OpenCVインペイント</option>
825
+ <option value="simple_lama">Simple Lamaインペイント</option>
826
+ <option value="stable_diffusion">Stable Diffusionインペイント</option>
827
+ <option value="deep_fill_v2">DeepFillv2インペイント</option>
828
+ </select>
829
+ </div>
830
+
831
+ <div class="slider-container">
832
+ <label for="riskLevel" class="slider-label">リスクレベル (0-100): <span id="riskLevelLabel">50</span></label>
833
+ <div id="slider"></div>
834
+ </div>
835
+
836
+ <button class="btn btn-primary mt-4" onclick="processImage()">処理開始</button>
837
+
838
+ <div id="result" class="mt-5">
839
+ <h2>処理結果:</h2>
840
+ <img id="processedImage" class="processed-preview" src="" alt="">
841
+ <a id="downloadLink" class="btn btn-success mt-3" href="#" download="processed_image.jpg">処理された画像をダウンロード</a>
842
+ </div>
843
+ </div>
844
+
845
+ <!-- jQuery and Bootstrap JS -->
846
+ <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
847
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
848
+ <!-- jQuery UI -->
849
+ <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
850
+
851
+ <script>
852
+ $(function() {
853
+ // スライダーの設定
854
+ $("#slider").slider({
855
+ range: "min",
856
+ value: 50, // 初期値
857
+ min: 0,
858
+ max: 100,
859
+ slide: function(event, ui) {
860
+ $("#riskLevelLabel").text(ui.value);
861
+ }
862
+ });
863
+ });
864
+
865
+ function previewImage() {
866
+ const fileInput = document.getElementById('uploadImage');
867
+ const uploadedImage = document.getElementById('uploadedImage');
868
+
869
+ if (fileInput.files && fileInput.files[0]) {
870
+ const reader = new FileReader();
871
+ reader.onload = function (e) {
872
+ uploadedImage.src = e.target.result;
873
+ uploadedImage.style.display = 'block';
874
+ };
875
+ reader.readAsDataURL(fileInput.files[0]);
876
+ }
877
+ }
878
+
879
+ function processImage() {
880
+ const fileInput = document.getElementById('uploadImage');
881
+ const processingType = document.getElementById('processingType').value;
882
+ const riskLevel = $("#slider").slider("value"); // スライ���ーから値を取得
883
+ const resultDiv = document.getElementById('result');
884
+ const processedImage = document.getElementById('processedImage');
885
+ const downloadLink = document.getElementById('downloadLink');
886
+
887
+ if (fileInput.files.length === 0) {
888
+ alert("画像を選択してください。");
889
+ return;
890
+ }
891
+
892
+ const file = fileInput.files[0];
893
+ const formData = new FormData();
894
+ formData.append('image', file);
895
+ formData.append('risk_level', riskLevel); // リスクレベルを追加
896
+
897
+ let apiEndpoint;
898
+ if (processingType === "opencv") {
899
+ apiEndpoint = "/create-mask-and-inpaint-opencv";
900
+ } else if (processingType === "simple_lama") {
901
+ apiEndpoint = "/create-mask-and-inpaint-simple-lama";
902
+ } else if (processingType === "stable_diffusion") {
903
+ apiEndpoint = "/create-mask-and-inpaint-stable-diffusion";
904
+ } else if (processingType=="deep_fill_v2"){
905
+ apiEndpoint = "/create-mask-and-inpaint-deepfillv2";
906
+ }
907
+
908
+ const url = "https://wired-kitten-adequately.ngrok-free.app" + apiEndpoint;
909
+
910
+ fetch(url, {
911
+ method: 'POST',
912
+ body: formData
913
+ })
914
+ .then(response => {
915
+ if (!response.ok) {
916
+ throw new Error("Network response was not ok");
917
+ }
918
+ return response.blob();
919
+ })
920
+ .then(blob => {
921
+ const objectURL = URL.createObjectURL(blob);
922
+ processedImage.src = objectURL;
923
+ downloadLink.href = objectURL;
924
+ resultDiv.style.display = "block";
925
+ })
926
+ .catch(error => {
927
+ console.error("画像処理に失敗しました。", error);
928
+ alert("画像処理に失敗しました。");
929
+ });
930
+ }
931
+ </script>
932
+ </body>
933
+ </html>
934
+
935
+
936
+ """
937
+ return HTMLResponse(content=html_content)
938
+ if __name__ == "__main__":
939
+
940
+ app.run(host="0.0.0.0", port=7860)
941
+
main.png ADDED
output_vectors.json ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ annotated-types==0.7.0
2
+ anyio==4.6.2.post1
3
+ certifi==2024.8.30
4
+ charset-normalizer==3.4.0
5
+ click==8.1.7
6
+ colorama==0.4.6
7
+ contourpy==1.3.0
8
+ cycler==0.12.1
9
+ diffusers==0.31.0
10
+ fastapi==0.115.4
11
+ filelock==3.16.1
12
+ fire==0.5.0
13
+ fonttools==4.54.1
14
+ fsspec==2024.10.0
15
+ futures==3.0.5
16
+ h11==0.14.0
17
+ huggingface-hub==0.26.2
18
+ idna==3.10
19
+ importlib_metadata==8.5.0
20
+ Jinja2==3.1.4
21
+ joblib==1.4.2
22
+ kiwisolver==1.4.7
23
+ MarkupSafe==3.0.2
24
+ matplotlib==3.9.2
25
+ mpmath==1.3.0
26
+ nest-asyncio==1.6.0
27
+ networkx==3.4.2
28
+ numpy==1.26.4
29
+ opencv-python==4.10.0.84
30
+ opencv-python-headless==4.10.0.84
31
+ packaging==24.1
32
+ pandas==2.2.3
33
+ Pillow==9.5.0
34
+ psutil==6.1.0
35
+ py-cpuinfo==9.0.0
36
+ pycocotools==2.0.8
37
+ pydantic==2.9.2
38
+ pydantic_core==2.23.4
39
+ pyngrok==7.2.1
40
+ pyparsing==3.2.0
41
+ python-dateutil==2.9.0.post0
42
+ python-multipart==0.0.17
43
+ pytz==2024.2
44
+ PyYAML==6.0.2
45
+ regex==2024.9.11
46
+ requests==2.32.3
47
+ safetensors==0.4.5
48
+ scikit-learn==1.5.2
49
+ scipy==1.14.1
50
+ seaborn==0.13.2
51
+ setuptools==75.3.0
52
+ simple-lama-inpainting==0.1.2
53
+ six==1.16.0
54
+ sniffio==1.3.1
55
+ starlette==0.41.2
56
+ supervision==0.9.0
57
+ sympy==1.13.1
58
+ termcolor==2.5.0
59
+ threadpoolctl==3.5.0
60
+ tokenizers==0.20.3
61
+ torch==2.5.1
62
+ torchvision==0.20.1
63
+ tqdm==4.66.6
64
+ transformers==4.46.2
65
+ typing_extensions==4.12.2
66
+ tzdata==2024.2
67
+ ultralytics==8.3.23
68
+ ultralytics-thop==2.0.10
69
+ urllib3==2.2.3
70
+ uvicorn==0.32.0
71
+ zipp==3.20.2
72
+ supervision
sums_data.json ADDED
The diff for this file is too large to render. See raw diff