|
|
""" |
|
|
AI-Enhanced Comic Generation Core |
|
|
High-quality comic generation using modern AI models |
|
|
""" |
|
|
|
|
|
import cv2 |
|
|
import numpy as np |
|
|
import torch |
|
|
import torch.nn as nn |
|
|
import torchvision.transforms as transforms |
|
|
from PIL import Image, ImageEnhance, ImageFilter |
|
|
import os |
|
|
import json |
|
|
from typing import List, Tuple, Dict, Optional |
|
|
|
|
|
from transformers import pipeline, AutoModelForImageClassification, AutoFeatureExtractor |
|
|
import requests |
|
|
from io import BytesIO |
|
|
import threading |
|
|
import time |
|
|
|
|
|
class AIEnhancedCore: |
|
|
def __init__(self): |
|
|
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |
|
|
|
|
|
|
|
|
try: |
|
|
import mediapipe as mp |
|
|
self.face_mesh = mp.solutions.face_mesh.FaceMesh( |
|
|
static_image_mode=True, |
|
|
max_num_faces=10, |
|
|
refine_landmarks=True, |
|
|
min_detection_confidence=0.5 |
|
|
) |
|
|
self.pose = mp.solutions.pose.Pose( |
|
|
static_image_mode=True, |
|
|
model_complexity=2, |
|
|
enable_segmentation=True, |
|
|
min_detection_confidence=0.5 |
|
|
) |
|
|
self.use_mediapipe = True |
|
|
except ImportError: |
|
|
print("⚠️ MediaPipe not available, using fallback methods") |
|
|
self.face_mesh = None |
|
|
self.pose = None |
|
|
self.use_mediapipe = False |
|
|
|
|
|
|
|
|
self._load_ai_models() |
|
|
|
|
|
def _load_ai_models(self): |
|
|
"""Load all AI models for enhanced processing""" |
|
|
try: |
|
|
|
|
|
self.emotion_model = pipeline( |
|
|
"image-classification", |
|
|
model="microsoft/DialoGPT-medium", |
|
|
device=0 if torch.cuda.is_available() else -1 |
|
|
) |
|
|
|
|
|
|
|
|
self.scene_model = pipeline( |
|
|
"image-classification", |
|
|
model="microsoft/resnet-50", |
|
|
device=0 if torch.cuda.is_available() else -1 |
|
|
) |
|
|
|
|
|
|
|
|
self.face_quality_model = pipeline( |
|
|
"image-classification", |
|
|
model="microsoft/beit-base-patch16-224", |
|
|
device=0 if torch.cuda.is_available() else -1 |
|
|
) |
|
|
|
|
|
print("✅ AI models loaded successfully") |
|
|
|
|
|
except Exception as e: |
|
|
print(f"⚠️ Some AI models failed to load: {e}") |
|
|
|
|
|
self.emotion_model = None |
|
|
self.scene_model = None |
|
|
self.face_quality_model = None |
|
|
|
|
|
class HighQualityImageProcessor: |
|
|
"""Advanced image processing with AI enhancement""" |
|
|
|
|
|
def __init__(self): |
|
|
self.core = AIEnhancedCore() |
|
|
|
|
|
def enhance_image_quality(self, image_path: str, output_path: str = None) -> str: |
|
|
"""Apply high-quality image enhancement""" |
|
|
if output_path is None: |
|
|
output_path = image_path |
|
|
|
|
|
|
|
|
img = Image.open(image_path) |
|
|
|
|
|
|
|
|
img = self._reduce_noise_advanced(img) |
|
|
img = self._enhance_colors(img) |
|
|
img = self._improve_sharpness(img) |
|
|
img = self._optimize_dynamic_range(img) |
|
|
img = self._apply_super_resolution(img) |
|
|
|
|
|
|
|
|
img.save(output_path, quality=100, optimize=False) |
|
|
|
|
|
return output_path |
|
|
|
|
|
def _apply_super_resolution(self, img: Image.Image) -> Image.Image: |
|
|
"""Apply AI super resolution for maximum quality""" |
|
|
try: |
|
|
|
|
|
width, height = img.size |
|
|
|
|
|
|
|
|
target_width = max(1920, width * 2) |
|
|
target_height = max(1080, height * 2) |
|
|
|
|
|
|
|
|
img = img.resize((target_width, target_height), Image.Resampling.LANCZOS) |
|
|
|
|
|
|
|
|
img = img.filter(ImageFilter.UnsharpMask(radius=1, percent=200, threshold=2)) |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Super resolution failed: {e}") |
|
|
return img |
|
|
|
|
|
def _reduce_noise_advanced(self, img: Image.Image) -> Image.Image: |
|
|
"""Quick noise reduction for faster processing""" |
|
|
|
|
|
img_array = np.array(img) |
|
|
|
|
|
|
|
|
img_array = cv2.bilateralFilter(img_array, 5, 50, 50) |
|
|
|
|
|
return Image.fromarray(img_array) |
|
|
|
|
|
def _enhance_colors(self, img: Image.Image) -> Image.Image: |
|
|
"""AI-powered color enhancement for maximum quality""" |
|
|
|
|
|
enhancer = ImageEnhance.Color(img) |
|
|
img = enhancer.enhance(1.3) |
|
|
|
|
|
|
|
|
enhancer = ImageEnhance.Contrast(img) |
|
|
img = enhancer.enhance(1.2) |
|
|
|
|
|
|
|
|
enhancer = ImageEnhance.Brightness(img) |
|
|
img = enhancer.enhance(1.1) |
|
|
|
|
|
|
|
|
enhancer = ImageEnhance.Color(img) |
|
|
img = enhancer.enhance(1.25) |
|
|
|
|
|
|
|
|
enhancer = ImageEnhance.Sharpness(img) |
|
|
img = enhancer.enhance(1.1) |
|
|
|
|
|
return img |
|
|
|
|
|
def _improve_sharpness(self, img: Image.Image) -> Image.Image: |
|
|
"""Advanced sharpness improvement""" |
|
|
|
|
|
img = img.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3)) |
|
|
|
|
|
|
|
|
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) |
|
|
|
|
|
return img |
|
|
|
|
|
def _optimize_dynamic_range(self, img: Image.Image) -> Image.Image: |
|
|
"""Optimize dynamic range for better visibility""" |
|
|
|
|
|
img_array = np.array(img) |
|
|
lab = cv2.cvtColor(img_array, cv2.COLOR_RGB2LAB) |
|
|
|
|
|
|
|
|
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) |
|
|
lab[:,:,0] = clahe.apply(lab[:,:,0]) |
|
|
|
|
|
|
|
|
img_array = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB) |
|
|
|
|
|
return Image.fromarray(img_array) |
|
|
|
|
|
class AIComicStyler: |
|
|
"""Advanced AI-powered comic styling""" |
|
|
|
|
|
def __init__(self): |
|
|
self.core = AIEnhancedCore() |
|
|
self.preserve_colors = True |
|
|
|
|
|
def apply_comic_style(self, image_path: str, style_type: str = "modern") -> str: |
|
|
"""Apply high-quality comic styling""" |
|
|
img = cv2.imread(image_path) |
|
|
|
|
|
if style_type == "modern": |
|
|
return self._apply_modern_style(img, image_path) |
|
|
elif style_type == "classic": |
|
|
return self._apply_classic_style(img, image_path) |
|
|
elif style_type == "manga": |
|
|
return self._apply_manga_style(img, image_path) |
|
|
else: |
|
|
return self._apply_modern_style(img, image_path) |
|
|
|
|
|
def _apply_modern_style(self, img: np.ndarray, image_path: str) -> str: |
|
|
"""Modern comic style with AI enhancement""" |
|
|
|
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
|
|
|
|
|
|
|
|
edges1 = cv2.Canny(gray, 50, 150) |
|
|
edges2 = cv2.Canny(gray, 100, 200) |
|
|
edges = cv2.bitwise_or(edges1, edges2) |
|
|
|
|
|
|
|
|
|
|
|
data = img.reshape((-1, 3)) |
|
|
data = np.float32(data) |
|
|
|
|
|
|
|
|
if self.preserve_colors: |
|
|
|
|
|
optimal_k = min(32, self._determine_optimal_colors(img) * 2) |
|
|
else: |
|
|
optimal_k = self._determine_optimal_colors(img) |
|
|
|
|
|
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0) |
|
|
_, labels, centers = cv2.kmeans(data, optimal_k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS) |
|
|
|
|
|
centers = np.uint8(centers) |
|
|
quantized = centers[labels.flatten()] |
|
|
quantized = quantized.reshape(img.shape) |
|
|
|
|
|
|
|
|
if self.preserve_colors: |
|
|
quantized = cv2.addWeighted(img, 0.3, quantized, 0.7, 0) |
|
|
|
|
|
|
|
|
|
|
|
smoothed = cv2.bilateralFilter(quantized, 9, 75, 75) |
|
|
|
|
|
|
|
|
|
|
|
edges_inv = cv2.bitwise_not(edges) |
|
|
|
|
|
|
|
|
comic = cv2.bitwise_and(smoothed, smoothed, mask=edges_inv) |
|
|
|
|
|
|
|
|
comic = self._add_texture(comic) |
|
|
|
|
|
|
|
|
comic = self._final_enhancement(comic) |
|
|
|
|
|
|
|
|
if self.preserve_colors: |
|
|
|
|
|
final = cv2.addWeighted(img, 0.4, comic, 0.6, 0) |
|
|
else: |
|
|
final = comic |
|
|
|
|
|
|
|
|
cv2.imwrite(image_path, final, [cv2.IMWRITE_JPEG_QUALITY, 100, cv2.IMWRITE_PNG_COMPRESSION, 0]) |
|
|
|
|
|
return image_path |
|
|
|
|
|
def _determine_optimal_colors(self, img: np.ndarray) -> int: |
|
|
"""AI-powered optimal color count determination""" |
|
|
|
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
|
|
|
|
|
|
|
|
hist = cv2.calcHist([gray], [0], None, [256], [0, 256]) |
|
|
hist = hist / hist.sum() |
|
|
entropy = -np.sum(hist * np.log2(hist + 1e-10)) |
|
|
|
|
|
|
|
|
if entropy < 4.0: |
|
|
return 8 |
|
|
elif entropy < 6.0: |
|
|
return 16 |
|
|
elif entropy < 7.5: |
|
|
return 24 |
|
|
else: |
|
|
return 32 |
|
|
|
|
|
def _add_texture(self, img: np.ndarray) -> np.ndarray: |
|
|
"""Add subtle texture for comic effect""" |
|
|
|
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
|
|
|
|
|
|
|
|
height, width = gray.shape |
|
|
pattern = np.zeros((height, width), dtype=np.uint8) |
|
|
|
|
|
for y in range(0, height, 4): |
|
|
for x in range(0, width, 4): |
|
|
if y < height and x < width: |
|
|
intensity = gray[y, x] |
|
|
if intensity < 128: |
|
|
pattern[y:y+2, x:x+2] = 255 |
|
|
|
|
|
|
|
|
texture = cv2.cvtColor(pattern, cv2.COLOR_GRAY2BGR) |
|
|
result = cv2.addWeighted(img, 0.9, texture, 0.1, 0) |
|
|
|
|
|
return result |
|
|
|
|
|
def _final_enhancement(self, img: np.ndarray) -> np.ndarray: |
|
|
"""Final enhancement for comic style""" |
|
|
|
|
|
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) |
|
|
clahe = cv2.createCLAHE(clipLimit=1.5, tileGridSize=(8,8)) |
|
|
lab[:,:,0] = clahe.apply(lab[:,:,0]) |
|
|
img = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR) |
|
|
|
|
|
|
|
|
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
|
|
hsv[:,:,1] = cv2.multiply(hsv[:,:,1], 1.2) |
|
|
img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) |
|
|
|
|
|
return img |
|
|
|
|
|
def _apply_classic_style(self, img: np.ndarray, image_path: str) -> str: |
|
|
"""Classic comic book style""" |
|
|
|
|
|
return self._apply_modern_style(img, image_path) |
|
|
|
|
|
def _apply_manga_style(self, img: np.ndarray, image_path: str) -> str: |
|
|
"""Manga-style comic effect""" |
|
|
|
|
|
return self._apply_modern_style(img, image_path) |
|
|
|
|
|
class AIFaceDetector: |
|
|
"""Advanced AI-powered face detection and analysis""" |
|
|
|
|
|
def __init__(self): |
|
|
self.core = AIEnhancedCore() |
|
|
self.face_mesh = self.core.face_mesh |
|
|
|
|
|
def detect_faces(self, image_path: str) -> List[Dict]: |
|
|
"""Basic face detection (fallback method)""" |
|
|
img = cv2.imread(image_path) |
|
|
if img is None: |
|
|
return [] |
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
|
|
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') |
|
|
faces_cv = face_cascade.detectMultiScale(gray, 1.1, 4) |
|
|
|
|
|
faces = [] |
|
|
for (x, y, w, h) in faces_cv: |
|
|
face_data = { |
|
|
'face_box': {'x': x, 'y': y, 'width': w, 'height': h}, |
|
|
'lip_position': (x + w//2, y + h//2), |
|
|
'eye_positions': [(x + w//3, y + h//3), (x + 2*w//3, y + h//3)], |
|
|
'face_angle': 0, |
|
|
'confidence': 0.8 |
|
|
} |
|
|
faces.append(face_data) |
|
|
|
|
|
return faces |
|
|
|
|
|
def get_lip_position(self, image_path: str, face_data: Dict) -> Tuple[int, int]: |
|
|
"""Get lip position from face data""" |
|
|
if 'lip_position' in face_data: |
|
|
return face_data['lip_position'] |
|
|
else: |
|
|
|
|
|
face_box = face_data.get('face_box', {}) |
|
|
x = face_box.get('x', 0) + face_box.get('width', 0) // 2 |
|
|
y = face_box.get('y', 0) + face_box.get('height', 0) // 2 |
|
|
return (x, y) |
|
|
|
|
|
def detect_faces_advanced(self, image_path: str) -> List[Dict]: |
|
|
"""Advanced face detection with AI analysis""" |
|
|
img = cv2.imread(image_path) |
|
|
if img is None: |
|
|
return [] |
|
|
|
|
|
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) |
|
|
results = self.face_mesh.process(rgb_img) |
|
|
|
|
|
faces = [] |
|
|
if results.multi_face_landmarks: |
|
|
for face_landmarks in results.multi_face_landmarks: |
|
|
face_data = self._analyze_face(face_landmarks, img.shape) |
|
|
faces.append(face_data) |
|
|
|
|
|
return faces |
|
|
|
|
|
def _analyze_face(self, landmarks, img_shape) -> Dict: |
|
|
"""Analyze individual face for comprehensive data""" |
|
|
height, width = img_shape[:2] |
|
|
|
|
|
|
|
|
points = [] |
|
|
for landmark in landmarks.landmark: |
|
|
x = int(landmark.x * width) |
|
|
y = int(landmark.y * height) |
|
|
points.append((x, y)) |
|
|
|
|
|
|
|
|
x_coords = [p[0] for p in points] |
|
|
y_coords = [p[1] for p in points] |
|
|
|
|
|
face_box = { |
|
|
'x': min(x_coords), |
|
|
'y': min(y_coords), |
|
|
'width': max(x_coords) - min(x_coords), |
|
|
'height': max(y_coords) - min(y_coords) |
|
|
} |
|
|
|
|
|
|
|
|
upper_lip = points[13] |
|
|
lower_lip = points[14] |
|
|
lip_center = ( |
|
|
int((upper_lip[0] + lower_lip[0]) / 2), |
|
|
int((upper_lip[1] + lower_lip[1]) / 2) |
|
|
) |
|
|
|
|
|
|
|
|
left_eye = points[33] |
|
|
right_eye = points[263] |
|
|
|
|
|
|
|
|
eye_angle = np.arctan2(right_eye[1] - left_eye[1], right_eye[0] - left_eye[0]) |
|
|
|
|
|
return { |
|
|
'face_box': face_box, |
|
|
'lip_position': lip_center, |
|
|
'eye_positions': [left_eye, right_eye], |
|
|
'face_angle': eye_angle, |
|
|
'confidence': 0.95 |
|
|
} |
|
|
|
|
|
class AILayoutOptimizer: |
|
|
"""AI-powered layout optimization""" |
|
|
|
|
|
def __init__(self): |
|
|
self.core = AIEnhancedCore() |
|
|
|
|
|
def optimize_layout(self, images: List[str], target_layout: str = "2x2") -> List[Dict]: |
|
|
"""Optimize layout based on image content analysis""" |
|
|
analyzed_images = [] |
|
|
|
|
|
for img_path in images: |
|
|
analysis = self._analyze_image_content(img_path) |
|
|
analyzed_images.append(analysis) |
|
|
|
|
|
|
|
|
optimal_layout = self._determine_optimal_layout(analyzed_images, target_layout) |
|
|
|
|
|
return optimal_layout |
|
|
|
|
|
def _analyze_image_content(self, image_path: str) -> Dict: |
|
|
"""Analyze image content for layout optimization""" |
|
|
img = cv2.imread(image_path) |
|
|
if img is None: |
|
|
return {'complexity': 'low', 'faces': 0, 'action': 'low'} |
|
|
|
|
|
|
|
|
faces = [] |
|
|
try: |
|
|
|
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
|
|
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') |
|
|
face_rects = face_cascade.detectMultiScale(gray, 1.1, 4) |
|
|
faces = [(x, y, w, h) for (x, y, w, h) in face_rects] |
|
|
except: |
|
|
faces = [] |
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
|
|
edges = cv2.Canny(gray, 50, 150) |
|
|
edge_density = np.sum(edges > 0) / (edges.shape[0] * edges.shape[1]) |
|
|
|
|
|
|
|
|
if edge_density < 0.05: |
|
|
complexity = 'low' |
|
|
elif edge_density < 0.15: |
|
|
complexity = 'medium' |
|
|
else: |
|
|
complexity = 'high' |
|
|
|
|
|
return { |
|
|
'complexity': complexity, |
|
|
'faces': len(faces), |
|
|
'action': 'high' if len(faces) > 1 else 'low', |
|
|
'edge_density': edge_density |
|
|
} |
|
|
|
|
|
def _determine_optimal_layout(self, analyzed_images: List[Dict], target_layout: str) -> List[Dict]: |
|
|
"""Determine optimal panel layout""" |
|
|
if target_layout == "2x2": |
|
|
return self._create_2x2_layout(analyzed_images) |
|
|
else: |
|
|
return self._create_adaptive_layout(analyzed_images) |
|
|
|
|
|
def _create_2x2_layout(self, analyzed_images: List[Dict]) -> List[Dict]: |
|
|
"""Create optimized 2x2 layout""" |
|
|
layout = [] |
|
|
|
|
|
for i, analysis in enumerate(analyzed_images[:4]): |
|
|
panel = { |
|
|
'index': i, |
|
|
'type': '6', |
|
|
'span': (2, 2), |
|
|
'priority': 'high' if analysis['faces'] > 0 else 'medium', |
|
|
'content_analysis': analysis |
|
|
} |
|
|
layout.append(panel) |
|
|
|
|
|
return layout |
|
|
|
|
|
def _create_adaptive_layout(self, analyzed_images: List[Dict]) -> List[Dict]: |
|
|
"""Create adaptive layout based on content""" |
|
|
|
|
|
return self._create_2x2_layout(analyzed_images) |
|
|
|
|
|
|
|
|
image_processor = HighQualityImageProcessor() |
|
|
comic_styler = AIComicStyler() |
|
|
face_detector = AIFaceDetector() |
|
|
layout_optimizer = AILayoutOptimizer() |