Pujan-Dev commited on
Commit
0b8f50d
Β·
1 Parent(s): ec0654b

feat: updated detector using Ela fft and meta

Browse files
Files changed (45) hide show
  1. .env-example +0 -0
  2. .gitignore +0 -0
  3. Dockerfile +0 -0
  4. Procfile +0 -0
  5. README.md +0 -9
  6. app.py +4 -0
  7. config.py +0 -0
  8. docs/api_endpoints.md +0 -0
  9. docs/deployment.md +0 -0
  10. docs/detector/ELA.md +76 -0
  11. docs/detector/fft.md +141 -0
  12. docs/detector/meta.md +0 -0
  13. docs/detector/note-for-backend.md +92 -0
  14. docs/functions.md +0 -0
  15. docs/nestjs_integration.md +0 -0
  16. docs/security.md +0 -0
  17. docs/setup.md +0 -0
  18. docs/structure.md +0 -0
  19. features/image_classifier/__init__.py +0 -0
  20. features/image_classifier/controller.py +0 -0
  21. features/image_classifier/inferencer.py +0 -0
  22. features/image_classifier/model_loader.py +0 -0
  23. features/image_classifier/preprocess.py +0 -0
  24. features/image_classifier/routes.py +0 -0
  25. features/image_edit_detector/controller.py +49 -0
  26. features/image_edit_detector/detectors/ela.py +32 -0
  27. features/image_edit_detector/detectors/fft.py +40 -0
  28. features/image_edit_detector/detectors/metadata.py +82 -0
  29. features/image_edit_detector/preprocess.py +9 -0
  30. features/image_edit_detector/routes.py +53 -0
  31. features/nepali_text_classifier/__init__.py +0 -0
  32. features/nepali_text_classifier/controller.py +0 -0
  33. features/nepali_text_classifier/inferencer.py +0 -0
  34. features/nepali_text_classifier/model_loader.py +0 -0
  35. features/nepali_text_classifier/preprocess.py +0 -0
  36. features/nepali_text_classifier/routes.py +0 -0
  37. features/text_classifier/__init__.py +0 -0
  38. features/text_classifier/controller.py +0 -0
  39. features/text_classifier/inferencer.py +0 -0
  40. features/text_classifier/model_loader.py +0 -0
  41. features/text_classifier/preprocess.py +0 -0
  42. features/text_classifier/routes.py +0 -0
  43. readme.md +6 -32
  44. requirements.txt +0 -0
  45. test.md +31 -0
.env-example CHANGED
File without changes
.gitignore CHANGED
File without changes
Dockerfile CHANGED
File without changes
Procfile CHANGED
File without changes
README.md DELETED
@@ -1,9 +0,0 @@
1
- ---
2
- title: Ai-Checker
3
- emoji: πŸš€
4
- colorFrom: yellow
5
- colorTo: blue
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -7,6 +7,8 @@ from fastapi.responses import JSONResponse
7
  from features.text_classifier.routes import router as text_classifier_router
8
  from features.nepali_text_classifier.routes import router as nepali_text_classifier_router
9
  from features.image_classifier.routes import router as image_classifier_router
 
 
10
  from config import ACCESS_RATE
11
 
12
  import requests
@@ -30,6 +32,8 @@ app.add_middleware(SlowAPIMiddleware)
30
  app.include_router(text_classifier_router, prefix="/text")
31
  app.include_router(nepali_text_classifier_router,prefix="/NP")
32
  app.include_router(image_classifier_router,prefix="/AI-image")
 
 
33
  @app.get("/")
34
  @limiter.limit(ACCESS_RATE)
35
  async def root(request: Request):
 
7
  from features.text_classifier.routes import router as text_classifier_router
8
  from features.nepali_text_classifier.routes import router as nepali_text_classifier_router
9
  from features.image_classifier.routes import router as image_classifier_router
10
+ from features.image_edit_detector.routes import router as image_edit_detector_router
11
+
12
  from config import ACCESS_RATE
13
 
14
  import requests
 
32
  app.include_router(text_classifier_router, prefix="/text")
33
  app.include_router(nepali_text_classifier_router,prefix="/NP")
34
  app.include_router(image_classifier_router,prefix="/AI-image")
35
+ app.include_router(image_edit_detector_router,prefix="/detect")
36
+
37
  @app.get("/")
38
  @limiter.limit(ACCESS_RATE)
39
  async def root(request: Request):
config.py CHANGED
File without changes
docs/api_endpoints.md CHANGED
File without changes
docs/deployment.md CHANGED
File without changes
docs/detector/ELA.md ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Error Level Analysis (ELA) Detector
2
+
3
+ This module provides a function to perform Error Level Analysis (ELA) on images to detect potential manipulations or edits.
4
+
5
+
6
+ ## Function: `run_ela`
7
+
8
+ ```python
9
+ def run_ela(image: Image.Image, quality: int = 90, threshold: int = 15) -> bool:
10
+ ```
11
+
12
+
13
+ ### Description
14
+
15
+ Error Level Analysis (ELA) works by recompressing an image at a specified JPEG quality level and comparing it to the original image. Differences between the two images reveal areas with inconsistent compression artifacts β€” often indicating image manipulation.
16
+
17
+ The function computes the maximum pixel difference across all color channels and uses a threshold to determine if the image is likely edited.
18
+
19
+ ### Parameters
20
+
21
+ | Parameter | Type | Default | Description |
22
+ | ----------- | ----------- | ------- | ------------------------------------------------------------------------------------------- |
23
+ | `image` | `PIL.Image` | N/A | Input image in RGB mode to analyze. |
24
+ | `quality` | `int` | 90 | JPEG compression quality used for recompression during analysis (lower = more compression). |
25
+ | `threshold` | `int` | 15 | Pixel difference threshold to flag the image as edited. |
26
+
27
+
28
+ ### Returns
29
+
30
+ `bool`
31
+
32
+ * `True` if the image is likely edited (max pixel difference > threshold).
33
+ * `False` if the image appears unedited.
34
+
35
+
36
+ ### Usage Example
37
+
38
+ ```python
39
+ from PIL import Image
40
+ from detectors.ela import run_ela
41
+
42
+ # Open and convert image to RGB
43
+ img = Image.open("example.jpg").convert("RGB")
44
+
45
+ # Run ELA detection
46
+ is_edited = run_ela(img, quality=90, threshold=15)
47
+
48
+ print("Image edited:", is_edited)
49
+ ```
50
+
51
+
52
+ ### Notes
53
+
54
+ * The input image **must** be in RGB mode for accurate analysis.
55
+ * ELA is a heuristic technique; combining it with other detection methods increases reliability.
56
+ * Visualizing the enhanced difference image can help identify edited regions (not returned by this function but possible to add).
57
+
58
+
59
+ ### Installation
60
+
61
+ Make sure you have Pillow installed:
62
+
63
+ ```bash
64
+ pip install pillow
65
+ ```
66
+
67
+
68
+ ### Running Locally
69
+
70
+ Just put the function in a notebook or script file and run it with your image. It works well for basic images.
71
+
72
+
73
+ ### Developer
74
+
75
+ Pujan Neupane
76
+
docs/detector/fft.md ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # Fast Fourier Transform (FFT) Detector
3
+
4
+ ```python
5
+ def run_fft(image: Image.Image, threshold: float = 0.92) -> bool:
6
+ ```
7
+
8
+ ## **Overview**
9
+
10
+ The `run_fft` function performs a frequency domain analysis on an image using the **Fast Fourier Transform (FFT)** to detect possible **AI generation or digital manipulation**. It leverages the fact that artificially generated or heavily edited images often exhibit a distinct high-frequency pattern.
11
+
12
+ ---
13
+
14
+ ## **Parameters**
15
+
16
+ | Parameter | Type | Description |
17
+ | ----------- | ----------------- | --------------------------------------------------------------------------------------- |
18
+ | `image` | `PIL.Image.Image` | Input image to analyze. It will be converted to grayscale and resized. |
19
+ | `threshold` | `float` | Proportion threshold of high-frequency components to flag the image. Default is `0.92`. |
20
+
21
+ ---
22
+
23
+ ## **Returns**
24
+
25
+ | Type | Description |
26
+ | ------ | ---------------------------------------------------------------------- |
27
+ | `bool` | `True` if image is likely AI-generated/manipulated; otherwise `False`. |
28
+
29
+ ---
30
+
31
+ ## **Step-by-Step Explanation**
32
+
33
+ ### 1. **Grayscale Conversion**
34
+
35
+ All images are converted to grayscale:
36
+
37
+ ```python
38
+ gray_image = image.convert("L")
39
+ ```
40
+
41
+ ### 2. **Resize**
42
+
43
+ The image is resized to a fixed $512 \times 512$ for uniformity:
44
+
45
+ ```python
46
+ resized_image = gray_image.resize((512, 512))
47
+ ```
48
+
49
+ ### 3. **FFT Calculation**
50
+
51
+ Compute the 2D Discrete Fourier Transform:
52
+
53
+ $$
54
+ F(u, v) = \sum_{x=0}^{M-1} \sum_{y=0}^{N-1} f(x, y) \cdot e^{-2\pi i \left( \frac{ux}{M} + \frac{vy}{N} \right)}
55
+ $$
56
+
57
+ ```python
58
+ fft_result = fft2(image_array)
59
+ ```
60
+
61
+ ### 4. **Shift Zero Frequency to Center**
62
+
63
+ Use `fftshift` to center the zero-frequency component:
64
+
65
+ ```python
66
+ fft_shifted = fftshift(fft_result)
67
+ ```
68
+
69
+ ### 5. **Magnitude Spectrum**
70
+
71
+ $$
72
+ |F(u, v)| = \sqrt{\Re^2 + \Im^2}
73
+ $$
74
+
75
+ ```python
76
+ magnitude_spectrum = np.abs(fft_shifted)
77
+ ```
78
+
79
+ ### 6. **Normalization**
80
+
81
+ Normalize the spectrum to avoid scale issues:
82
+
83
+ $$
84
+ \text{Normalized}(u,v) = \frac{|F(u,v)|}{\max(|F(u,v)|)}
85
+ $$
86
+
87
+ ```python
88
+ normalized_spectrum = magnitude_spectrum / max_magnitude
89
+ ```
90
+
91
+ ### 7. **High-Frequency Detection**
92
+
93
+ High-frequency components are defined as:
94
+
95
+ $$
96
+ \text{Mask}(u,v) =
97
+ \begin{cases}
98
+ 1 & \text{if } \text{Normalized}(u,v) > 0.5 \\
99
+ 0 & \text{otherwise}
100
+ \end{cases}
101
+ $$
102
+
103
+ ```python
104
+ high_freq_mask = normalized_spectrum > 0.5
105
+ ```
106
+
107
+ ### 8. **Proportion Calculation**
108
+
109
+ $$
110
+ \text{Ratio} = \frac{\sum \text{Mask}}{\text{Total pixels}}
111
+ $$
112
+
113
+ ```python
114
+ high_freq_ratio = np.sum(high_freq_mask) / normalized_spectrum.size
115
+ ```
116
+
117
+ ### 9. **Threshold Decision**
118
+
119
+ If the ratio exceeds the threshold:
120
+
121
+ $$
122
+ \text{is\_fake} = (\text{Ratio} > \text{Threshold})
123
+ $$
124
+
125
+ ```python
126
+ is_fake = high_freq_ratio > threshold
127
+ ```
128
+
129
+ it is implemented in the api
130
+
131
+ ### Running Locally
132
+
133
+ Just put the function in a notebook or script file and run it with your image. It works well for basic images.
134
+
135
+
136
+ ### Worked By
137
+
138
+ Pujan Neupane
139
+
140
+
141
+
docs/detector/meta.md ADDED
File without changes
docs/detector/note-for-backend.md ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # πŸ“¦API integration note
3
+
4
+ ## Overview
5
+
6
+ This system integrates **three image forensics methods**β€”**ELA**, **FFT**, and **Metadata analysis**β€”into a single detection pipeline to determine whether an image is AI-generated, manipulated, or authentic.
7
+
8
+ ---
9
+
10
+ ## πŸ” Detection Modules
11
+
12
+ ### 1. **ELA (Error Level Analysis)**
13
+
14
+ * **Purpose:** Detects tampering or editing by analyzing compression error levels.
15
+ * **Accuracy:** βœ… *Most accurate method*
16
+ * **Performance:** ❗ *Slowest method*
17
+ * **Output:** `True` (edited) or `False` (authentic)
18
+
19
+ ### 2. **FFT (Fast Fourier Transform)**
20
+
21
+ * **Purpose:** Identifies high-frequency patterns typical of AI-generated images.
22
+ * **Accuracy:** ⚠️ *Moderately accurate*
23
+ * **Performance:** ❗ *Moderate to slow*
24
+ * **Output:** `True` (likely AI-generated) or `False` (authentic)
25
+
26
+ ### 3. **Metadata Analysis**
27
+
28
+ * **Purpose:** Detects traces of AI tools or editors in image metadata or binary content.
29
+ * **Accuracy:** ⚠️ *Fast but weaker signal*
30
+ * **Performance:** πŸš€ *Fastest method*
31
+ * **Output:** One of:
32
+
33
+ * `"ai_generated"` – AI tool or generator identified
34
+ * `"edited"` – Edited using known software
35
+ * `"undetermined"` – No signature found
36
+
37
+ ---
38
+
39
+ ## 🧩 Integration Plan
40
+
41
+ ### βž• Combine all three APIs into one unified endpoint:
42
+
43
+ ```bash
44
+ POST /api/detect-image
45
+ ```
46
+
47
+ ### Input:
48
+
49
+ * `image`: Image file (binary, any format supported by Pillow)
50
+
51
+ ### Output:
52
+
53
+ ```json
54
+ {
55
+ "ela_result": true,
56
+ "fft_result": false,
57
+ "metadata_result": "ai_generated",
58
+ "final_decision": "ai_generated"
59
+ }
60
+ ```
61
+ > NOTE:Optionally recommending a default logic (e.g., trust ELA > FFT > Metadata).
62
+
63
+ ## Result implementation
64
+ | `ela_result` | `fft_result` | `metadata_result` | Suggested Final Decision | Notes |
65
+ | ------------ | ------------ | ----------------- | ------------------------ | ----------------------------------------------------------------------- |
66
+ | `true` | `true` | `"ai_generated"` | `ai_generated` | Strong evidence from all three modules |
67
+ | `true` | `false` | `"edited"` | `edited` | ELA confirms editing, no AI signals |
68
+ | `true` | `false` | `"undetermined"` | `edited` | ELA indicates manipulation |
69
+ | `false` | `true` | `"ai_generated"` | `ai_generated` | No edits, but strong AI frequency & metadata signature |
70
+ | `false` | `true` | `"undetermined"` | `possibly_ai_generated` | Weak metadata, but FFT indicates possible AI generation |
71
+ | `false` | `false` | `"ai_generated"` | `ai_generated` | Metadata alone shows AI use |
72
+ | `false` | `false` | `"edited"` | `possibly_edited` | Weak signalβ€”metadata shows editing but no structural or frequency signs |
73
+ | `false` | `false` | `"undetermined"` | `authentic` | No detectable manipulation or AI indicators |
74
+
75
+
76
+ ### Decision Logic:
77
+
78
+ * Use **ELA** as the **primary indicator** for manipulation.
79
+ * Supplement with **FFT** and **Metadata** to improve reliability.
80
+ * Combine using a simple rule-based or voting system.
81
+
82
+ ---
83
+
84
+ ## βš™οΈ Performance Consideration
85
+
86
+ | Method | Speed | Strength |
87
+ | -------- | ----------- | -------------------- |
88
+ | ELA | ❗ Slow | βœ… Highly accurate |
89
+ | FFT | ⚠️ Moderate | ⚠️ Somewhat reliable |
90
+ | Metadata | πŸš€ Fast | ⚠️ Low confidence |
91
+
92
+ > For high-throughput systems, consider running Metadata first and conditionally applying ELA/FFT if suspicious.
docs/functions.md CHANGED
File without changes
docs/nestjs_integration.md CHANGED
File without changes
docs/security.md CHANGED
File without changes
docs/setup.md CHANGED
File without changes
docs/structure.md CHANGED
File without changes
features/image_classifier/__init__.py CHANGED
File without changes
features/image_classifier/controller.py CHANGED
File without changes
features/image_classifier/inferencer.py CHANGED
File without changes
features/image_classifier/model_loader.py CHANGED
File without changes
features/image_classifier/preprocess.py CHANGED
File without changes
features/image_classifier/routes.py CHANGED
File without changes
features/image_edit_detector/controller.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ import io
3
+ from io import BytesIO
4
+ from .detectors.fft import run_fft
5
+ from .detectors.metadata import run_metadata
6
+ from .detectors.ela import run_ela
7
+ from .preprocess import preprocess_image
8
+ from fastapi import HTTPException,status,Depends
9
+ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
10
+ security=HTTPBearer()
11
+ import os
12
+ async def process_image_ela(image_bytes: bytes, quality: int=90):
13
+ image = Image.open(io.BytesIO(image_bytes))
14
+
15
+ if image.mode != "RGB":
16
+ image = image.convert("RGB")
17
+
18
+ compressed_image = preprocess_image(image, quality)
19
+ ela_result = run_ela(compressed_image, quality)
20
+
21
+ return {
22
+ "is_edited": ela_result,
23
+ "ela_score": ela_result
24
+ }
25
+
26
+ async def process_fft_image(image_bytes: bytes,threshold:float=0.95) -> dict:
27
+ image = Image.open(BytesIO(image_bytes)).convert("RGB")
28
+ result = run_fft(image,threshold)
29
+ return {"edited": bool(result)}
30
+
31
+
32
+ async def process_meta_image(image_bytes: bytes) -> dict:
33
+ try:
34
+ result = run_metadata(image_bytes)
35
+ return {"source": result} # e.g. "edited", "phone_capture", "unknown"
36
+ except Exception as e:
37
+ # Handle errors gracefully, return useful message or raise HTTPException if preferred
38
+ return {"error": str(e)}
39
+
40
+
41
+ async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
42
+ token = credentials.credentials
43
+ expected_token = os.getenv("MY_SECRET_TOKEN")
44
+ if token != expected_token:
45
+ raise HTTPException(
46
+ status_code=status.HTTP_403_FORBIDDEN,
47
+ detail="Invalid or expired token"
48
+ )
49
+ return token
features/image_edit_detector/detectors/ela.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image, ImageChops, ImageEnhance
2
+ import io
3
+
4
+ def run_ela(image: Image.Image, quality: int = 90, threshold: int = 15) -> bool:
5
+ """
6
+ Perform Error Level Analysis to detect image manipulation.
7
+
8
+ Parameters:
9
+ image (PIL.Image): Input image (should be RGB).
10
+ quality (int): JPEG compression quality for ELA.
11
+ threshold (int): Maximum pixel difference threshold to classify as edited.
12
+
13
+ Returns:
14
+ bool: True if image appears edited, False otherwise.
15
+ """
16
+
17
+ # Recompress the image into JPEG format in memory
18
+ buffer = io.BytesIO()
19
+ image.save(buffer, format='JPEG', quality=quality)
20
+ buffer.seek(0)
21
+ recompressed = Image.open(buffer)
22
+
23
+ # Compute the pixel-wise difference
24
+ diff = ImageChops.difference(image, recompressed)
25
+ extrema = diff.getextrema()
26
+ max_diff = max([ex[1] for ex in extrema])
27
+
28
+ # Enhance difference image for debug (not returned)
29
+ _ = ImageEnhance.Brightness(diff).enhance(10)
30
+
31
+ return max_diff > threshold
32
+
features/image_edit_detector/detectors/fft.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from PIL import Image
3
+ from scipy.fft import fft2, fftshift
4
+
5
+
6
+ def run_fft(image: Image.Image, threshold: float = 0.92) -> bool:
7
+ """
8
+ Detects potential image manipulation or generation using FFT-based high-frequency analysis.
9
+
10
+ Parameters:
11
+ image (PIL.Image.Image): The input image.
12
+ threshold (float): Proportion of high-frequency components above which the image is flagged.
13
+
14
+ Returns:
15
+ bool: True if the image is likely AI-generated or manipulated, False otherwise.
16
+ """
17
+ gray_image = image.convert("L")
18
+
19
+ resized_image = gray_image.resize((512, 512))
20
+
21
+
22
+ image_array = np.array(resized_image)
23
+
24
+ fft_result = fft2(image_array)
25
+
26
+ fft_shifted = fftshift(fft_result)
27
+
28
+ magnitude_spectrum = np.abs(fft_shifted)
29
+ max_magnitude = np.max(magnitude_spectrum)
30
+ if max_magnitude == 0:
31
+ return False # Avoid division by zero if image is blank
32
+ normalized_spectrum = magnitude_spectrum / max_magnitude
33
+
34
+ high_freq_mask = normalized_spectrum > 0.5
35
+
36
+ high_freq_ratio = np.sum(high_freq_mask) / normalized_spectrum.size
37
+
38
+ is_fake = high_freq_ratio > threshold
39
+ return is_fake
40
+
features/image_edit_detector/detectors/metadata.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image, UnidentifiedImageError
2
+ import io
3
+
4
+ # Common AI metadata identifiers in image files.
5
+ AI_INDICATORS = [
6
+ b'c2pa', b'claim_generator', b'claim_generator_info',
7
+ b'created_software_agent', b'actions.v2', b'assertions',
8
+ b'urn:c2pa', b'jumd', b'jumb', b'jumdcbor', b'jumdc2ma',
9
+ b'jumdc2as', b'jumdc2cl', b'cbor', b'convertedsfwareagent',b'c2pa.version',
10
+ b'c2pa.assertions', b'c2pa.actions',
11
+ b'c2pa.thumbnail', b'c2pa.signature', b'c2pa.manifest',
12
+ b'c2pa.manifest_store', b'c2pa.ingredient', b'c2pa.parent',
13
+ b'c2pa.provenance', b'c2pa.claim', b'c2pa.hash', b'c2pa.authority',
14
+ b'jumdc2pn', b'jumdrefs', b'jumdver', b'jumdmeta',
15
+
16
+
17
+ 'midjourney'.encode('utf-8'),
18
+ 'stable-diffusion'.encode('utf-8'),
19
+ 'stable diffusion'.encode('utf-8'),
20
+ 'stable_diffusion'.encode('utf-8'),
21
+ 'artbreeder'.encode('utf-8'),
22
+ 'runwayml'.encode('utf-8'),
23
+ 'remix.ai'.encode('utf-8'),
24
+ 'firefly'.encode('utf-8'),
25
+ 'adobe_firefly'.encode('utf-8'),
26
+
27
+ # OpenAI / DALLΒ·E indicators (all encoded to bytes)
28
+ 'openai'.encode('utf-8'),
29
+ 'dalle'.encode('utf-8'),
30
+ 'dalle2'.encode('utf-8'),
31
+ 'DALL-E'.encode('utf-8'),
32
+ 'DALLΒ·E'.encode('utf-8'),
33
+ 'created_by: openai'.encode('utf-8'),
34
+ 'tool: dalle'.encode('utf-8'),
35
+ 'tool: dalle2'.encode('utf-8'),
36
+ 'creator: openai'.encode('utf-8'),
37
+ 'creator: dalle'.encode('utf-8'),
38
+ 'openai.com'.encode('utf-8'),
39
+ 'api.openai.com'.encode('utf-8'),
40
+ 'openai_model'.encode('utf-8'),
41
+ 'openai_gpt'.encode('utf-8'),
42
+
43
+ #Further possible AI-Generation Indicators
44
+ 'generated_by'.encode('utf-8'),
45
+ 'model_id'.encode('utf-8'),
46
+ 'model_version'.encode('utf-8'),
47
+ 'model_info'.encode('utf-8'),
48
+ 'tool_name'.encode('utf-8'),
49
+ 'tool_creator'.encode('utf-8'),
50
+ 'tool_version'.encode('utf-8'),
51
+ 'model_signature'.encode('utf-8'),
52
+ 'ai_model'.encode('utf-8'),
53
+ 'ai_tool'.encode('utf-8'),
54
+ 'generator'.encode('utf-8'),
55
+ 'generated_by_ai'.encode('utf-8'),
56
+ 'ai_generated'.encode('utf-8'),
57
+ 'ai_art'.encode('utf-8')
58
+ ]
59
+
60
+
61
+ def run_metadata(image_bytes: bytes) -> str:
62
+ try:
63
+ img = Image.open(io.BytesIO(image_bytes))
64
+ img.load()
65
+
66
+ exif = img.getexif()
67
+ software = str(exif.get(305, "")).strip()
68
+
69
+ suspicious_editors = ["Photoshop", "GIMP", "Snapseed", "Pixlr", "VSCO", "Editor", "Adobe", "Luminar"]
70
+
71
+ if any(editor.lower() in software.lower() for editor in suspicious_editors):
72
+ return "edited"
73
+
74
+ if any(indicator in image_bytes for indicator in AI_INDICATORS):
75
+ return "ai_generated"
76
+
77
+ return "undetermined"
78
+
79
+ except UnidentifiedImageError:
80
+ return "error: invalid image format"
81
+ except Exception as e:
82
+ return f"error: {str(e)}"
features/image_edit_detector/preprocess.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ import io
3
+
4
+ def preprocess_image(img: Image.Image, quality: int) -> Image.Image:
5
+ buffer = io.BytesIO()
6
+ img.save(buffer, format="JPEG", quality=quality)
7
+ buffer.seek(0)
8
+ return Image.open(buffer)
9
+
features/image_edit_detector/routes.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from slowapi import Limiter
2
+ from config import ACCESS_RATE
3
+ from fastapi import APIRouter, File, Request, Depends, HTTPException, UploadFile
4
+ from fastapi.security import HTTPBearer
5
+ from slowapi import Limiter
6
+ from slowapi.util import get_remote_address
7
+ from io import BytesIO
8
+ from .controller import process_image_ela , verify_token,process_fft_image, process_meta_image
9
+ import requests
10
+ router = APIRouter()
11
+ limiter = Limiter(key_func=get_remote_address)
12
+ security = HTTPBearer()
13
+
14
+
15
+
16
+ @router.post("/ela")
17
+ @limiter.limit(ACCESS_RATE)
18
+ async def detect_ela(request:Request,file: UploadFile = File(...), quality: int = 90 ,token: str = Depends(verify_token)):
19
+ # Check file extension
20
+ allowed_types = ["image/jpeg", "image/png"]
21
+
22
+ if file.content_type not in allowed_types:
23
+ raise HTTPException(
24
+ status_code=400,
25
+ detail="Unsupported file type. Only JPEG and PNG images are allowed."
26
+ )
27
+
28
+ content = await file.read()
29
+ result = await process_image_ela(content, quality)
30
+ return result
31
+
32
+ @router.post("/fft")
33
+ @limiter.limit(ACCESS_RATE)
34
+ async def detect_fft(request:Request,file:UploadFile =File(...),threshold:float=0.95,token:str=Depends(verify_token)):
35
+ if file.content_type not in ["image/jpeg", "image/png"]:
36
+ raise HTTPException(status_code=400, detail="Unsupported image type.")
37
+
38
+ content = await file.read()
39
+ result = await process_fft_image(content,threshold)
40
+ return result
41
+
42
+ @router.post("/meta")
43
+ @limiter.limit(ACCESS_RATE)
44
+ async def detect_meta(request:Request,file:UploadFile=File(...),token:str=Depends(verify_token)):
45
+ if file.content_type not in ["image/jpeg", "image/png"]:
46
+ raise HTTPException(status_code=400, detail="Unsupported image type.")
47
+ content = await file.read()
48
+ result = await process_meta_image(content)
49
+ return result
50
+ @router.post("/health")
51
+ @limiter.limit(ACCESS_RATE)
52
+ def heath(request:Request):
53
+ return {"status":"ok"}
features/nepali_text_classifier/__init__.py CHANGED
File without changes
features/nepali_text_classifier/controller.py CHANGED
File without changes
features/nepali_text_classifier/inferencer.py CHANGED
File without changes
features/nepali_text_classifier/model_loader.py CHANGED
File without changes
features/nepali_text_classifier/preprocess.py CHANGED
File without changes
features/nepali_text_classifier/routes.py CHANGED
File without changes
features/text_classifier/__init__.py CHANGED
File without changes
features/text_classifier/controller.py CHANGED
File without changes
features/text_classifier/inferencer.py CHANGED
File without changes
features/text_classifier/model_loader.py CHANGED
File without changes
features/text_classifier/preprocess.py CHANGED
File without changes
features/text_classifier/routes.py CHANGED
File without changes
readme.md CHANGED
@@ -1,35 +1,9 @@
1
- # πŸš€ FastAPI AI Detector
2
-
3
- A production-ready FastAPI app for detecting AI vs. human-written text in English and Nepali. It uses GPT-2 and SentencePiece-based models, with Bearer token security.
4
-
5
- ## πŸ“‚ Documentation
6
-
7
- - [Project Structure](docs/structure.md)
8
- - [API Endpoints](docs/api_endpoints.md)
9
- - [Setup & Installation](docs/setup.md)
10
- - [Deployment](docs/deployment.md)
11
- - [Security](docs/security.md)
12
- - [NestJS Integration](docs/nestjs_integration.md)
13
- - [Core Functions](docs/functions.md)
14
-
15
- ## ⚑ Quick Start
16
- ```bash
17
- uvicorn app:app --host 0.0.0.0 --port 8000
18
- ```
19
- ## πŸš€ Deployment
20
-
21
- - **Local**: Use `uvicorn` as above.
22
- - **Railway/Heroku**: Use the provided `Procfile`.
23
- - **Hugging Face Spaces**: Use the `Dockerfile` for container deployment.
24
-
25
  ---
26
-
27
- ## πŸ’‘ Tips
28
-
29
- - **Model files auto-download at first start** if not found.
30
- - **Keep `requirements.txt` up-to-date** after adding dependencies.
31
- - **All endpoints require the correct `Authorization` header**.
32
- - **For security**: Avoid committing `.env` to public repos.
33
-
34
  ---
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Ai-Checker
3
+ emoji: πŸš€
4
+ colorFrom: yellow
5
+ colorTo: blue
6
+ sdk: docker
7
+ pinned: false
 
 
8
  ---
9
 
requirements.txt CHANGED
File without changes
test.md ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ **Update: Edited & AI-Generated Content Detection – Project Plan**
3
+
4
+ ### πŸ” Phase 1: Rule-Based Image Detection (In Progress)
5
+
6
+ We're implementing three core techniques to individually flag edited or AI-generated images:
7
+
8
+ * **ELA (Error Level Analysis):** Highlights inconsistencies via JPEG recompression.
9
+ * **FFT (Frequency Analysis):** Uses 2D Fourier Transform to detect unnatural image frequency patterns.
10
+ * **Metadata Analysis:** Parses EXIF data to catch clues like editing software tags.
11
+
12
+ These give us visual + interpretable results for each image, and currently offer \~60–70% accuracy on typical AI-edited content.
13
+
14
+ ---
15
+
16
+ ### Phase 2: AI vs Human Detection System (Coming Soon)
17
+
18
+ **Goal:** Build an AI model that classifies whether content is AI- or human-made β€” initially focusing on **images**, and later expanding to **text**.
19
+
20
+ **Data Strategy:**
21
+
22
+ * Scraping large volumes of recent AI-gen images (e.g. SDXL, Gibbli, MidJourney).
23
+ * Balancing with high-quality human images.
24
+
25
+ **Model Plan:**
26
+
27
+ * Use ELA, FFT, and metadata as feature extractors.
28
+ * Feed these into a CNN or ensemble model.
29
+ * Later, unify into a full web-based platform (upload β†’ get AI/human probability).
30
+
31
+