views.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. # # background_remover/views.py
  2. # import io
  3. # from PIL import Image
  4. # from django.http import HttpResponse
  5. # from rest_framework.views import APIView
  6. # from rest_framework.parsers import MultiPartParser
  7. # from .services import BiRefNetService
  8. # from django.shortcuts import render
  9. # import zipfile
  10. # import time
  11. # class BackgroundRemovalView(APIView):
  12. # parser_classes = [MultiPartParser]
  13. # def get(self, request):
  14. # return render(request, "bg_remover_index.html")
  15. # def post(self, request, *args, **kwargs):
  16. # if 'image' not in request.FILES:
  17. # return HttpResponse("No image provided", status=400)
  18. # # 1. Load image from request
  19. # input_file = request.FILES['image']
  20. # input_image = Image.open(input_file)
  21. # # 2. Process via Service
  22. # service = BiRefNetService()
  23. # output_image = service.remove_background(input_image)
  24. # # 3. Prepare response
  25. # buffer = io.BytesIO()
  26. # output_image.save(buffer, format="PNG")
  27. # return HttpResponse(buffer.getvalue(), content_type="image/png")
  28. # class BulkBackgroundRemovalAPI(APIView):
  29. # parser_classes = [MultiPartParser]
  30. # def post(self, request):
  31. # files = request.FILES.getlist("images")
  32. # if not files:
  33. # return HttpResponse({"error": "No images provided"}, status=400)
  34. # service = BiRefNetService()
  35. # start_time = time.time()
  36. # zip_buffer = io.BytesIO()
  37. # # with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
  38. # # for file in files:
  39. # # try:
  40. # # image = Image.open(file).convert("RGB")
  41. # # output = service.remove_background(image)
  42. # # img_buffer = io.BytesIO()
  43. # # output.save(img_buffer, format="PNG")
  44. # # name = file.name.rsplit(".", 1)[0] + "_whitebg.png"
  45. # # zip_file.writestr(name, img_buffer.getvalue())
  46. # # except Exception as e:
  47. # # print(f"Error processing {file.name}: {e}")
  48. # with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as master_zip:
  49. # for file in files:
  50. # # CHECK IF FILE IS A ZIP
  51. # if file.name.lower().endswith('.zip'):
  52. # with zipfile.ZipFile(file, 'r') as user_zip:
  53. # for inner_file_name in user_zip.namelist():
  54. # # Skip directories and non-image files
  55. # if inner_file_name.endswith(('.png', '.jpg', '.jpeg', '.webp')):
  56. # with user_zip.open(inner_file_name) as inner_file:
  57. # img_data = inner_file.read()
  58. # image = Image.open(io.BytesIO(img_data)).convert("RGB")
  59. # output = service.remove_background(image)
  60. # img_io = io.BytesIO()
  61. # output.save(img_io, format="PNG")
  62. # # Create clean name for the output zip
  63. # clean_name = inner_file_name.rsplit(".", 1)[0].split('/')[-1] + "_no_bg.png"
  64. # master_zip.writestr(clean_name, img_io.getvalue())
  65. # # PROCESS INDIVIDUAL IMAGES (Existing logic)
  66. # else:
  67. # try:
  68. # image = Image.open(file).convert("RGB")
  69. # output = service.remove_background(image)
  70. # img_io = io.BytesIO()
  71. # output.save(img_io, format="PNG")
  72. # name = file.name.rsplit(".", 1)[0] + "_no_bg.png"
  73. # master_zip.writestr(name, img_io.getvalue())
  74. # except Exception as e:
  75. # print(f"Error processing {file.name}: {e}")
  76. # zip_buffer.seek(0)
  77. # total_time = round(time.time() - start_time, 2)
  78. # response = HttpResponse(
  79. # zip_buffer.getvalue(),
  80. # content_type="application/zip"
  81. # )
  82. # response["Content-Disposition"] = "attachment; filename=processed_images.zip"
  83. # response["X-Processing-Time"] = f"{total_time}s"
  84. # return response
  85. import uuid
  86. import threading
  87. import os
  88. import io
  89. import zipfile
  90. import csv
  91. from django.shortcuts import render
  92. from django.http import HttpResponse, JsonResponse
  93. from django.conf import settings
  94. from rest_framework.views import APIView
  95. from rest_framework.parsers import MultiPartParser
  96. from PIL import Image
  97. from .models import BackgroundTask
  98. from .services import BiRefNetService
  99. class BackgroundRemovalView(APIView):
  100. parser_classes = [MultiPartParser]
  101. def get(self, request):
  102. return render(request, "bg_remover_index.html")
  103. def post(self, request, *args, **kwargs):
  104. if 'image' not in request.FILES:
  105. return HttpResponse("No image provided", status=400)
  106. # 1. Load image from request
  107. input_file = request.FILES['image']
  108. input_image = Image.open(input_file)
  109. # 2. Process via Service
  110. service = BiRefNetService()
  111. output_image = service.remove_background(input_image)
  112. # 3. Prepare response
  113. buffer = io.BytesIO()
  114. output_image.save(buffer, format="PNG")
  115. return HttpResponse(buffer.getvalue(), content_type="image/png")
  116. class BulkBackgroundRemovalAPI(APIView):
  117. parser_classes = [MultiPartParser]
  118. def post(self, request):
  119. files = request.FILES.getlist("images")
  120. if not files:
  121. return JsonResponse({"error": "No images provided"}, status=400)
  122. # 1. Create a database record for the task
  123. task_id = uuid.uuid4()
  124. BackgroundTask.objects.create(task_id=task_id, status='PROCESSING')
  125. # 2. Read files into memory to pass to the thread
  126. file_list = []
  127. for f in files:
  128. file_list.append({
  129. "name": f.name,
  130. "content": f.read()
  131. })
  132. # 3. Start background thread
  133. thread = threading.Thread(
  134. target=self.process_images_logic,
  135. args=(task_id, file_list)
  136. )
  137. thread.start()
  138. return JsonResponse({
  139. "task_id": str(task_id),
  140. "message": "Task started",
  141. "status_url": f"/api/status/{task_id}/"
  142. }, status=202)
  143. def process_images_logic(self, task_id, file_list):
  144. """Heavy AI logic running in background"""
  145. service = BiRefNetService()
  146. log_data = []
  147. # Prepare folders
  148. folder_path = os.path.join(settings.MEDIA_ROOT, 'bulk_results', str(task_id))
  149. os.makedirs(folder_path, exist_ok=True)
  150. zip_filename = f"result_{task_id}.zip"
  151. zip_abs_path = os.path.join(folder_path, zip_filename)
  152. try:
  153. with zipfile.ZipFile(zip_abs_path, "w", zipfile.ZIP_DEFLATED) as master_zip:
  154. for item in file_list:
  155. name, content = item["name"], item["content"]
  156. try:
  157. # Process if it's a zip
  158. if name.lower().endswith('.zip'):
  159. with zipfile.ZipFile(io.BytesIO(content)) as user_zip:
  160. for inner_name in user_zip.namelist():
  161. if inner_name.lower().endswith(('.png', '.jpg', '.jpeg', '.webp')):
  162. with user_zip.open(inner_name) as inner_file:
  163. img = Image.open(io.BytesIO(inner_file.read())).convert("RGB")
  164. out = service.remove_background(img)
  165. buf = io.BytesIO()
  166. out.save(buf, format="PNG")
  167. master_zip.writestr(f"no_bg_{inner_name.split('/')[-1]}", buf.getvalue())
  168. log_data.append([inner_name, "SUCCESS", "Done"])
  169. # Process if it's an image
  170. else:
  171. img = Image.open(io.BytesIO(content)).convert("RGB")
  172. out = service.remove_background(img)
  173. buf = io.BytesIO()
  174. out.save(buf, format="PNG")
  175. clean_name = name.rsplit(".", 1)[0] + "_no_bg.png"
  176. master_zip.writestr(clean_name, buf.getvalue())
  177. log_data.append([name, "SUCCESS", "Done"])
  178. except Exception as e:
  179. log_data.append([name, "FAIL", str(e)])
  180. # Create CSV Log inside the Zip
  181. csv_buf = io.StringIO()
  182. writer = csv.writer(csv_buf)
  183. writer.writerow(["Filename", "Status", "Message"])
  184. writer.writerows(log_data)
  185. master_zip.writestr("processing_report.csv", csv_buf.getvalue())
  186. # 4. Update model to COMPLETED
  187. task = BackgroundTask.objects.get(task_id=task_id)
  188. task.status = 'COMPLETED'
  189. # Save the relative path for the FileField
  190. task.zip_file = f"bulk_results/{task_id}/{zip_filename}"
  191. task.save()
  192. except Exception as e:
  193. task = BackgroundTask.objects.get(task_id=task_id)
  194. task.status = 'FAILED'
  195. task.error_message = str(e)
  196. task.save()
  197. class TaskStatusAPI(APIView):
  198. def get(self, request, task_id):
  199. try:
  200. task = BackgroundTask.objects.get(task_id=task_id)
  201. return JsonResponse({
  202. "task_id": task.task_id,
  203. "status": task.status,
  204. "download_url": task.zip_file.url if task.zip_file else None,
  205. "error": task.error_message
  206. })
  207. except BackgroundTask.DoesNotExist:
  208. return JsonResponse({"error": "Task not found"}, status=404)
  209. class TaskHistoryListView(APIView):
  210. def get(self, request):
  211. # Fetch latest 20 tasks
  212. tasks = BackgroundTask.objects.all().order_by('-created_at')[:20]
  213. data = []
  214. for t in tasks:
  215. data.append({
  216. "task_id": str(t.task_id),
  217. "status": t.status,
  218. "created_at": t.created_at.strftime("%Y-%m-%d %H:%M"),
  219. "download_url": t.zip_file.url if t.zip_file else None
  220. })
  221. return HttpResponse(data)
  222. # Make sure this name matches what you put in urls.py
  223. class TaskHistoryAPI(APIView):
  224. def get(self, request):
  225. # Fetch latest 20 tasks
  226. tasks = BackgroundTask.objects.all().order_by('-created_at')[:20]
  227. data = []
  228. for t in tasks:
  229. data.append({
  230. "task_id": str(t.task_id),
  231. "status": t.status,
  232. # Using 'date' key to match the JS 't.date' in the history page
  233. "date": t.created_at.strftime("%Y-%m-%d %H:%M"),
  234. "url": t.zip_file.url if t.zip_file else None
  235. })
  236. # Use JsonResponse so the frontend can read it as an object
  237. return JsonResponse(data, safe=False)
  238. class TaskHistoryPageView(APIView):
  239. def get(self, request):
  240. return render(request, "bg_remover_history.html")