Spaces:
Running
Running
push to testing branch
Browse files- .gitignore +3 -1
- README.md +149 -6
- app.py +33 -15
- docs/api_endpoints.md +22 -22
- docs/deployment.md +1 -0
- docs/detector/ELA.md +6 -17
- docs/detector/fft.md +1 -6
- docs/detector/meta.md +20 -0
- docs/detector/note-for-backend.md +2 -0
- docs/features/image_classifier.md +31 -0
- docs/features/nepali_text_classifier.md +30 -0
- docs/features/text_classifier.md +30 -0
- docs/functions.md +2 -3
- docs/nestjs_integration.md +1 -0
- docs/security.md +1 -0
- docs/setup.md +1 -0
- docs/status_code.md +68 -0
- docs/structure.md +51 -31
- features/image_classifier/controller.py +10 -5
- features/image_classifier/inferencer.py +40 -20
- features/image_classifier/model_loader.py +41 -26
- features/image_classifier/preprocess.py +20 -12
- features/image_edit_detector/detectors/ela.py +2 -2
- license.md +20 -0
- requirements.txt +1 -0
.gitignore
CHANGED
@@ -60,4 +60,6 @@ models/.gitattributes #<-- This line can stay if you only want to ignore that f
|
|
60 |
|
61 |
todo.md
|
62 |
np_text_model
|
63 |
-
|
|
|
|
|
|
60 |
|
61 |
todo.md
|
62 |
np_text_model
|
63 |
+
IMG_Models
|
64 |
+
notebooks
|
65 |
+
static
|
README.md
CHANGED
@@ -1,9 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
---
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# AI-Contain-Checker
|
2 |
+
|
3 |
+
A modular AI content detection system with support for **image classification**, **image edit detection**, **Nepali text classification**, and **general text classification**. Built for performance and extensibility, it is ideal for detecting AI-generated content in both visual and textual forms.
|
4 |
+
|
5 |
+
|
6 |
+
## 🌟 Features
|
7 |
+
|
8 |
+
### 🖼️ Image Classifier
|
9 |
+
|
10 |
+
* **Purpose**: Classifies whether an image is AI-generated or a real-life photo.
|
11 |
+
* **Model**: Fine-tuned **InceptionV3** CNN.
|
12 |
+
* **Dataset**: Custom curated dataset with **\~79,950 images** for binary classification.
|
13 |
+
* **Location**: [`features/image_classifier`](features/image_classifier)
|
14 |
+
* **Docs**: [`docs/features/image_classifier.md`](docs/features/image_classifier.md)
|
15 |
+
|
16 |
+
### 🖌️ Image Edit Detector
|
17 |
+
|
18 |
+
* **Purpose**: Detects image tampering or post-processing.
|
19 |
+
* **Techniques Used**:
|
20 |
+
|
21 |
+
* **Error Level Analysis (ELA)**: Visualizes compression artifacts.
|
22 |
+
* **Fast Fourier Transform (FFT)**: Detects unnatural frequency patterns.
|
23 |
+
* **Location**: [`features/image_edit_detector`](features/image_edit_detector)
|
24 |
+
* **Docs**:
|
25 |
+
|
26 |
+
* [ELA](docs/detector/ELA.md)
|
27 |
+
* [FFT](docs/detector/fft.md )
|
28 |
+
* [Metadata Analysis](docs/detector/meta.md)
|
29 |
+
* [Backend Notes](docs/detector/note-for-backend.md)
|
30 |
+
|
31 |
+
### 📝 Nepali Text Classifier
|
32 |
+
|
33 |
+
* **Purpose**: Determines if Nepali text content is AI-generated or written by a human.
|
34 |
+
* **Model**: Based on `XLMRClassifier` fine-tuned on Nepali language data.
|
35 |
+
* **Dataset**: Scraped dataset of **\~18,000** Nepali texts.
|
36 |
+
* **Location**: [`features/nepali_text_classifier`](features/nepali_text_classifier)
|
37 |
+
* **Docs**: [`docs/features/nepali_text_classifier.md`](docs/features/nepali_text_classifier.md)
|
38 |
+
|
39 |
+
### 🌐 English Text Classifier
|
40 |
+
|
41 |
+
* **Purpose**: Detects if English text is AI-generated or human-written.
|
42 |
+
* **Pipeline**:
|
43 |
+
|
44 |
+
* Uses **GPT2 tokenizer** for input preprocessing.
|
45 |
+
* Custom binary classifier to differentiate between AI and human-written content.
|
46 |
+
* **Location**: [`features/text_classifier`](features/text_classifier)
|
47 |
+
* **Docs**: [`docs/features/text_classifier.md`](docs/features/text_classifier.md)
|
48 |
+
|
49 |
---
|
50 |
+
|
51 |
+
## 🗂️ Project Structure
|
52 |
+
|
53 |
+
```bash
|
54 |
+
AI-Checker/
|
55 |
+
│
|
56 |
+
├── app.py # Main FastAPI entry point
|
57 |
+
├── config.py # Configuration settings
|
58 |
+
├── Dockerfile # Docker build script
|
59 |
+
├── Procfile # Deployment file for Heroku or similar
|
60 |
+
├── requirements.txt # Python dependencies
|
61 |
+
├── README.md # You are here 📘
|
62 |
+
│
|
63 |
+
├── features/ # Core detection modules
|
64 |
+
│ ├── image_classifier/
|
65 |
+
│ ├── image_edit_detector/
|
66 |
+
│ ├── nepali_text_classifier/
|
67 |
+
│ └── text_classifier/
|
68 |
+
│
|
69 |
+
├── docs/ # Internal and API documentation
|
70 |
+
│ ├── api_endpoints.md
|
71 |
+
│ ├── deployment.md
|
72 |
+
│ ├── detector/
|
73 |
+
│ │ ├── ELA.md
|
74 |
+
│ │ ├── fft.md
|
75 |
+
│ │ ├── meta.md
|
76 |
+
│ │ └── note-for-backend.md
|
77 |
+
│ ├── functions.md
|
78 |
+
│ ├── nestjs_integration.md
|
79 |
+
│ ├── security.md
|
80 |
+
│ ├── setup.md
|
81 |
+
│ └── structure.md
|
82 |
+
│
|
83 |
+
├── IMG_Models/ # Saved image classifier model(s)
|
84 |
+
│ └── latest-my_cnn_model.h5
|
85 |
+
│
|
86 |
+
├── notebooks/ # Experimental and debug notebooks
|
87 |
+
├── static/ # Static assets if needed
|
88 |
+
└── test.md # Test notes
|
89 |
+
````
|
90 |
+
|
91 |
---
|
92 |
|
93 |
+
## 📚 Documentation Links
|
94 |
+
|
95 |
+
* [API Endpoints](docs/api_endpoints.md)
|
96 |
+
* [Deployment Guide](docs/deployment.md)
|
97 |
+
* [Detector Documentation](docs/detector/)
|
98 |
+
|
99 |
+
* [Error Level Analysis (ELA)](docs/detector/ELA.md)
|
100 |
+
* [Fast Fourier Transform (FFT)](docs/detector/fft.md)
|
101 |
+
* [Metadata Analysis](docs/detector/meta.md)
|
102 |
+
* [Backend Notes](docs/detector/note-for-backend.md)
|
103 |
+
* [Functions Overview](docs/functions.md)
|
104 |
+
* [NestJS Integration Guide](docs/nestjs_integration.md)
|
105 |
+
* [Security Details](docs/security.md)
|
106 |
+
* [Setup Instructions](docs/setup.md)
|
107 |
+
* [Project Structure](docs/structure.md)
|
108 |
+
|
109 |
+
---
|
110 |
+
|
111 |
+
## 🚀 Usage
|
112 |
+
|
113 |
+
1. **Install dependencies**
|
114 |
+
|
115 |
+
```bash
|
116 |
+
pip install -r requirements.txt
|
117 |
+
```
|
118 |
+
|
119 |
+
2. **Run the API**
|
120 |
+
|
121 |
+
```bash
|
122 |
+
uvicorn app:app --reload
|
123 |
+
```
|
124 |
+
|
125 |
+
3. **Build Docker (optional)**
|
126 |
+
|
127 |
+
```bash
|
128 |
+
docker build -t ai-contain-checker .
|
129 |
+
docker run -p 8000:8000 ai-contain-checker
|
130 |
+
```
|
131 |
+
|
132 |
+
---
|
133 |
+
|
134 |
+
## 🔐 Security & Integration
|
135 |
+
|
136 |
+
* **Token Authentication** and **IP Whitelisting** supported.
|
137 |
+
* NestJS integration guide: [`docs/nestjs_integration.md`](docs/nestjs_integration.md)
|
138 |
+
* Rate limiting handled using `slowapi`.
|
139 |
+
|
140 |
+
---
|
141 |
+
|
142 |
+
## 🛡️ Future Plans
|
143 |
+
|
144 |
+
* Add **video classifier** module.
|
145 |
+
* Expand dataset for **multilingual** AI content detection.
|
146 |
+
* Add **fine-tuning UI** for models.
|
147 |
+
|
148 |
+
---
|
149 |
+
|
150 |
+
## 📄 License
|
151 |
+
|
152 |
+
See full license terms here: [`LICENSE.md`](license.md)
|
app.py
CHANGED
@@ -1,44 +1,62 @@
|
|
1 |
from fastapi import FastAPI, Request
|
2 |
from slowapi import Limiter, _rate_limit_exceeded_handler
|
|
|
3 |
from slowapi.middleware import SlowAPIMiddleware
|
4 |
from slowapi.errors import RateLimitExceeded
|
5 |
from slowapi.util import get_remote_address
|
6 |
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
|
|
|
|
|
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
|
|
|
15 |
limiter = Limiter(key_func=get_remote_address, default_limits=[ACCESS_RATE])
|
16 |
|
17 |
app = FastAPI()
|
18 |
-
|
19 |
# Set up SlowAPI
|
20 |
app.state.limiter = limiter
|
21 |
-
app.add_exception_handler(
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
29 |
app.add_middleware(SlowAPIMiddleware)
|
30 |
|
31 |
# Include your routes
|
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):
|
40 |
return {
|
41 |
"message": "API is working",
|
42 |
-
"endpoints": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
}
|
44 |
-
|
|
|
1 |
from fastapi import FastAPI, Request
|
2 |
from slowapi import Limiter, _rate_limit_exceeded_handler
|
3 |
+
from fastapi.responses import FileResponse
|
4 |
from slowapi.middleware import SlowAPIMiddleware
|
5 |
from slowapi.errors import RateLimitExceeded
|
6 |
from slowapi.util import get_remote_address
|
7 |
from fastapi.responses import JSONResponse
|
8 |
from features.text_classifier.routes import router as text_classifier_router
|
9 |
+
from features.nepali_text_classifier.routes import (
|
10 |
+
router as nepali_text_classifier_router,
|
11 |
+
)
|
12 |
from features.image_classifier.routes import router as image_classifier_router
|
13 |
from features.image_edit_detector.routes import router as image_edit_detector_router
|
14 |
+
from fastapi.staticfiles import StaticFiles
|
15 |
|
16 |
from config import ACCESS_RATE
|
17 |
|
18 |
import requests
|
19 |
+
|
20 |
limiter = Limiter(key_func=get_remote_address, default_limits=[ACCESS_RATE])
|
21 |
|
22 |
app = FastAPI()
|
23 |
+
# added the robots.txt
|
24 |
# Set up SlowAPI
|
25 |
app.state.limiter = limiter
|
26 |
+
app.add_exception_handler(
|
27 |
+
RateLimitExceeded,
|
28 |
+
lambda request, exc: JSONResponse(
|
29 |
+
status_code=429,
|
30 |
+
content={
|
31 |
+
"status_code": 429,
|
32 |
+
"error": "Rate limit exceeded",
|
33 |
+
"message": "Too many requests. Chill for a bit and try again",
|
34 |
+
},
|
35 |
+
),
|
36 |
+
)
|
37 |
app.add_middleware(SlowAPIMiddleware)
|
38 |
|
39 |
# Include your routes
|
40 |
app.include_router(text_classifier_router, prefix="/text")
|
41 |
+
app.include_router(nepali_text_classifier_router, prefix="/NP")
|
42 |
+
app.include_router(image_classifier_router, prefix="/AI-image")
|
43 |
+
app.include_router(image_edit_detector_router, prefix="/detect")
|
44 |
+
|
45 |
|
46 |
@app.get("/")
|
47 |
@limiter.limit(ACCESS_RATE)
|
48 |
async def root(request: Request):
|
49 |
return {
|
50 |
"message": "API is working",
|
51 |
+
"endpoints": [
|
52 |
+
"/text/analyse",
|
53 |
+
"/text/upload",
|
54 |
+
"/text/analyse-sentences",
|
55 |
+
"/text/analyse-sentance-file",
|
56 |
+
"/NP/analyse",
|
57 |
+
"/NP/upload",
|
58 |
+
"/NP/analyse-sentences",
|
59 |
+
"/NP/file-sentences-analyse",
|
60 |
+
"/AI-image/analyse",
|
61 |
+
],
|
62 |
}
|
|
docs/api_endpoints.md
CHANGED
@@ -2,13 +2,13 @@
|
|
2 |
|
3 |
### English (GPT-2) - `/text/`
|
4 |
|
5 |
-
| Endpoint
|
6 |
-
|
|
7 |
-
| `/text/analyse`
|
8 |
-
| `/text/analyse-sentences`
|
9 |
-
| `/text/analyse-sentance-file`
|
10 |
-
| `/text/upload`
|
11 |
-
| `/text/health`
|
12 |
|
13 |
#### Example: Classify English text
|
14 |
|
@@ -20,6 +20,7 @@ curl -X POST http://localhost:8000/text/analyse \
|
|
20 |
```
|
21 |
|
22 |
**Response:**
|
|
|
23 |
```json
|
24 |
{
|
25 |
"result": "AI-generated",
|
@@ -40,13 +41,13 @@ curl -X POST http://localhost:8000/text/upload \
|
|
40 |
|
41 |
### Nepali (SentencePiece) - `/NP/`
|
42 |
|
43 |
-
| Endpoint
|
44 |
-
|
|
45 |
-
| `/NP/analyse`
|
46 |
-
| `/NP/analyse-sentences`
|
47 |
-
| `/NP/upload`
|
48 |
-
| `/NP/file-sentences-analyse`
|
49 |
-
| `/NP/health`
|
50 |
|
51 |
#### Example: Nepali text classification
|
52 |
|
@@ -58,6 +59,7 @@ curl -X POST http://localhost:8000/NP/analyse \
|
|
58 |
```
|
59 |
|
60 |
**Response:**
|
|
|
61 |
```json
|
62 |
{
|
63 |
"label": "Human",
|
@@ -73,20 +75,18 @@ curl -X POST http://localhost:8000/NP/upload \
|
|
73 |
-F 'file=@NepaliText.pdf;type=application/pdf'
|
74 |
```
|
75 |
|
76 |
-
|
77 |
### Image-Classification -`/verify-image/`
|
78 |
-
|
79 |
-
| Endpoint | Method | Description |
|
80 |
-
| --------------------------------- | ------ | ----------------------------------------- |
|
81 |
-
| `/verify-image/analyse` | POST | Classify Image using ML |
|
82 |
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
-
#### Example: Image-Classification
|
85 |
```bash
|
86 |
curl -X POST http://localhost:8000/verify-image/analyse \
|
87 |
-H "Authorization: Bearer <SECRET_TOKEN>" \
|
88 |
-F 'file=@test1.png'
|
89 |
```
|
90 |
|
91 |
-
|
92 |
-
|
|
|
2 |
|
3 |
### English (GPT-2) - `/text/`
|
4 |
|
5 |
+
| Endpoint | Method | Description |
|
6 |
+
| ----------------------------- | ------ | -------------------------------------- |
|
7 |
+
| `/text/analyse` | POST | Classify raw English text |
|
8 |
+
| `/text/analyse-sentences` | POST | Sentence-by-sentence breakdown |
|
9 |
+
| `/text/analyse-sentance-file` | POST | Upload file, per-sentence breakdown |
|
10 |
+
| `/text/upload` | POST | Upload file for overall classification |
|
11 |
+
| `/text/health` | GET | Health check |
|
12 |
|
13 |
#### Example: Classify English text
|
14 |
|
|
|
20 |
```
|
21 |
|
22 |
**Response:**
|
23 |
+
|
24 |
```json
|
25 |
{
|
26 |
"result": "AI-generated",
|
|
|
41 |
|
42 |
### Nepali (SentencePiece) - `/NP/`
|
43 |
|
44 |
+
| Endpoint | Method | Description |
|
45 |
+
| ---------------------------- | ------ | ------------------------------------ |
|
46 |
+
| `/NP/analyse` | POST | Classify Nepali text |
|
47 |
+
| `/NP/analyse-sentences` | POST | Sentence-by-sentence breakdown |
|
48 |
+
| `/NP/upload` | POST | Upload Nepali PDF for classification |
|
49 |
+
| `/NP/file-sentences-analyse` | POST | PDF upload, per-sentence breakdown |
|
50 |
+
| `/NP/health` | GET | Health check |
|
51 |
|
52 |
#### Example: Nepali text classification
|
53 |
|
|
|
59 |
```
|
60 |
|
61 |
**Response:**
|
62 |
+
|
63 |
```json
|
64 |
{
|
65 |
"label": "Human",
|
|
|
75 |
-F 'file=@NepaliText.pdf;type=application/pdf'
|
76 |
```
|
77 |
|
|
|
78 |
### Image-Classification -`/verify-image/`
|
|
|
|
|
|
|
|
|
79 |
|
80 |
+
| Endpoint | Method | Description |
|
81 |
+
| ----------------------- | ------ | ----------------------- |
|
82 |
+
| `/verify-image/analyse` | POST | Classify Image using ML |
|
83 |
+
|
84 |
+
#### Example: Image-Classification
|
85 |
|
|
|
86 |
```bash
|
87 |
curl -X POST http://localhost:8000/verify-image/analyse \
|
88 |
-H "Authorization: Bearer <SECRET_TOKEN>" \
|
89 |
-F 'file=@test1.png'
|
90 |
```
|
91 |
|
92 |
+
[🔙 Back to Main README](../README.md)
|
|
docs/deployment.md
CHANGED
@@ -105,3 +105,4 @@ Happy deploying!
|
|
105 |
**P.S.** Try not to break stuff. 😅
|
106 |
|
107 |
|
|
|
|
105 |
**P.S.** Try not to break stuff. 😅
|
106 |
|
107 |
|
108 |
+
[🔙 Back to Main README](../README.md)
|
docs/detector/ELA.md
CHANGED
@@ -2,14 +2,12 @@
|
|
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.
|
@@ -24,14 +22,12 @@ The function computes the maximum pixel difference across all color channels and
|
|
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 |
-
|
33 |
-
|
34 |
-
|
35 |
|
36 |
### Usage Example
|
37 |
|
@@ -48,13 +44,11 @@ is_edited = run_ela(img, quality=90, threshold=15)
|
|
48 |
print("Image edited:", is_edited)
|
49 |
```
|
50 |
|
51 |
-
|
52 |
### Notes
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
|
59 |
### Installation
|
60 |
|
@@ -64,13 +58,8 @@ Make sure you have Pillow installed:
|
|
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 |
-
|
|
|
2 |
|
3 |
This module provides a function to perform Error Level Analysis (ELA) on images to detect potential manipulations or edits.
|
4 |
|
|
|
5 |
## Function: `run_ela`
|
6 |
|
7 |
```python
|
8 |
def run_ela(image: Image.Image, quality: int = 90, threshold: int = 15) -> bool:
|
9 |
```
|
10 |
|
|
|
11 |
### Description
|
12 |
|
13 |
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.
|
|
|
22 |
| `quality` | `int` | 90 | JPEG compression quality used for recompression during analysis (lower = more compression). |
|
23 |
| `threshold` | `int` | 15 | Pixel difference threshold to flag the image as edited. |
|
24 |
|
|
|
25 |
### Returns
|
26 |
|
27 |
`bool`
|
28 |
|
29 |
+
- `True` if the image is likely edited (max pixel difference > threshold).
|
30 |
+
- `False` if the image appears unedited.
|
|
|
31 |
|
32 |
### Usage Example
|
33 |
|
|
|
44 |
print("Image edited:", is_edited)
|
45 |
```
|
46 |
|
|
|
47 |
### Notes
|
48 |
|
49 |
+
- The input image **must** be in RGB mode for accurate analysis.
|
50 |
+
- ELA is a heuristic technique; combining it with other detection methods increases reliability.
|
51 |
+
- Visualizing the enhanced difference image can help identify edited regions (not returned by this function but possible to add).
|
|
|
52 |
|
53 |
### Installation
|
54 |
|
|
|
58 |
pip install pillow
|
59 |
```
|
60 |
|
|
|
61 |
### Running Locally
|
62 |
|
63 |
Just put the function in a notebook or script file and run it with your image. It works well for basic images.
|
64 |
|
65 |
+
[🔙 Back to Main README](../README.md)
|
|
|
|
|
|
|
|
docs/detector/fft.md
CHANGED
@@ -133,9 +133,4 @@ it is implemented in the api
|
|
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 |
-
|
137 |
-
|
138 |
-
Pujan Neupane
|
139 |
-
|
140 |
-
|
141 |
-
|
|
|
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 |
+
[🔙 Back to Main README](../README.md)
|
|
|
|
|
|
|
|
|
|
docs/detector/meta.md
CHANGED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Metadata Analysis for Image Edit Detection
|
2 |
+
|
3 |
+
This module inspects image metadata to detect possible signs of AI-generation or post-processing edits.
|
4 |
+
|
5 |
+
## Overview
|
6 |
+
|
7 |
+
- Many AI-generated images and edited images leave identifiable traces in their metadata.
|
8 |
+
- This detector scans image EXIF metadata and raw bytes for known AI generation indicators and common photo editing software signatures.
|
9 |
+
- It classifies images as `"ai_generated"`, `"edited"`, or `"undetermined"` based on detected markers.
|
10 |
+
- Handles invalid image formats gracefully by reporting errors.
|
11 |
+
|
12 |
+
## How It Works
|
13 |
+
|
14 |
+
- Opens the image from raw bytes using the Python Pillow library (`PIL`).
|
15 |
+
- Reads EXIF metadata and specifically looks for the "Software" tag that often contains the editing app name.
|
16 |
+
- Checks for common image editors such as Photoshop, GIMP, Snapseed, etc.
|
17 |
+
- Scans the entire raw byte content of the image for embedded AI generation identifiers like "midjourney", "stable-diffusion", "openai", etc.
|
18 |
+
- Returns a status string indicating the metadata classification.
|
19 |
+
|
20 |
+
[🔙 Back to Main README](../README.md)
|
docs/detector/note-for-backend.md
CHANGED
@@ -90,3 +90,5 @@ POST /api/detect-image
|
|
90 |
| Metadata | 🚀 Fast | ⚠️ Low confidence |
|
91 |
|
92 |
> For high-throughput systems, consider running Metadata first and conditionally applying ELA/FFT if suspicious.
|
|
|
|
|
|
90 |
| Metadata | 🚀 Fast | ⚠️ Low confidence |
|
91 |
|
92 |
> For high-throughput systems, consider running Metadata first and conditionally applying ELA/FFT if suspicious.
|
93 |
+
|
94 |
+
[🔙 Back to Main README](../README.md)
|
docs/features/image_classifier.md
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Image Classifier
|
2 |
+
|
3 |
+
## Overview
|
4 |
+
|
5 |
+
This module classifies whether an input image is AI-generated or a real-life photograph.
|
6 |
+
|
7 |
+
## Model
|
8 |
+
|
9 |
+
- Architecture: InceptionV3
|
10 |
+
- Type: Binary Classifier (AI vs Real)
|
11 |
+
- Format: H5 model (`latest-my_cnn_model.h5`)
|
12 |
+
|
13 |
+
## Dataset
|
14 |
+
|
15 |
+
- Total images: ~79,950
|
16 |
+
- Balanced between real and generated images
|
17 |
+
- Preprocessing: Resizing, normalization
|
18 |
+
|
19 |
+
## Code Location
|
20 |
+
|
21 |
+
- Controller: `features/image_classifier/controller.py`
|
22 |
+
- Model Loader: `features/image_classifier/model_loader.py`
|
23 |
+
- Preprocessor: `features/image_classifier/preprocess.py`
|
24 |
+
|
25 |
+
## API
|
26 |
+
|
27 |
+
- Endpoint: [ENDPOINTS](../api_endpoints.md)
|
28 |
+
- Input: Image file (PNG/JPG)
|
29 |
+
- Output: JSON response with classification result and confidence
|
30 |
+
|
31 |
+
[🔙 Back to Main README](../README.md)
|
docs/features/nepali_text_classifier.md
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Nepali Text Classifier
|
2 |
+
|
3 |
+
## Overview
|
4 |
+
|
5 |
+
This classifier identifies whether Nepali-language text content is written by a human or AI.
|
6 |
+
|
7 |
+
## Model
|
8 |
+
|
9 |
+
- Base Model: XLM-Roberta (XLMRClassifier)
|
10 |
+
- Language: Nepali (Multilingual model)
|
11 |
+
- Fine-tuned with scraped web content (~18,000 samples)
|
12 |
+
|
13 |
+
## Dataset
|
14 |
+
|
15 |
+
- Custom scraped dataset with manual labeling
|
16 |
+
- Includes news, blogs, and synthetic content from various LLMs
|
17 |
+
|
18 |
+
## Code Location
|
19 |
+
|
20 |
+
- Controller: `features/nepali_text_classifier/controller.py`
|
21 |
+
- Inference: `features/nepali_text_classifier/inferencer.py`
|
22 |
+
- Model Loader: `features/nepali_text_classifier/model_loader.py`
|
23 |
+
|
24 |
+
## API
|
25 |
+
|
26 |
+
- Endpoint: [ENDPOINTS](../api_endpoints.md)
|
27 |
+
- Input: Raw text
|
28 |
+
- Output: JSON classification with label and confidence score
|
29 |
+
|
30 |
+
[🔙 Back to Main README](../README.md)
|
docs/features/text_classifier.md
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# English Text Classifier
|
2 |
+
|
3 |
+
## Overview
|
4 |
+
|
5 |
+
Detects whether English-language text is AI-generated or human-written.
|
6 |
+
|
7 |
+
## Model Pipeline
|
8 |
+
|
9 |
+
- Tokenizer: GPT-2 Tokenizer
|
10 |
+
- Model: Custom trained binary classifier
|
11 |
+
|
12 |
+
## Dataset
|
13 |
+
|
14 |
+
- Balanced dataset: Human vs AI-generated (ChatGPT, Claude, etc.)
|
15 |
+
- Tokenized and fed into the model using PyTorch/TensorFlow
|
16 |
+
|
17 |
+
## Code Location
|
18 |
+
|
19 |
+
- Controller: `features/text_classifier/controller.py`
|
20 |
+
- Inference: `features/text_classifier/inferencer.py`
|
21 |
+
- Model Loader: `features/text_classifier/model_loader.py`
|
22 |
+
- Preprocessor: `features/text_classifier/preprocess.py`
|
23 |
+
|
24 |
+
## API
|
25 |
+
|
26 |
+
- Endpoint: [ENDPOINTS](../api_endpoints.md)
|
27 |
+
- Input: Raw English text
|
28 |
+
- Output: Prediction result with probability/confidence
|
29 |
+
|
30 |
+
[🔙 Back to Main README](../README.md)
|
docs/functions.md
CHANGED
@@ -52,12 +52,11 @@
|
|
52 |
---
|
53 |
## for image_classifier
|
54 |
|
55 |
-
|
56 |
-
|
57 |
- **`Classify_Image_router()`** – Handles image classification requests by routing and coordinating preprocessing and inference.
|
58 |
- **`classify_image()`** – Performs AI vs human image classification using the loaded model.
|
59 |
- **`load_model()`** – Loads the pretrained model from Hugging Face at server startup.
|
60 |
- **`preprocess_image()`** – Applies all required preprocessing steps to the input image.
|
61 |
|
|
|
62 |
|
63 |
-
|
|
|
52 |
---
|
53 |
## for image_classifier
|
54 |
|
|
|
|
|
55 |
- **`Classify_Image_router()`** – Handles image classification requests by routing and coordinating preprocessing and inference.
|
56 |
- **`classify_image()`** – Performs AI vs human image classification using the loaded model.
|
57 |
- **`load_model()`** – Loads the pretrained model from Hugging Face at server startup.
|
58 |
- **`preprocess_image()`** – Applies all required preprocessing steps to the input image.
|
59 |
|
60 |
+
> Note: While many functions mirror those in the text classifier, the image classifier primarily uses TensorFlow rather than PyTorch.
|
61 |
|
62 |
+
[🔙 Back to Main README](../README.md)
|
docs/nestjs_integration.md
CHANGED
@@ -80,3 +80,4 @@ export class AppController {
|
|
80 |
}
|
81 |
}
|
82 |
```
|
|
|
|
80 |
}
|
81 |
}
|
82 |
```
|
83 |
+
[🔙 Back to Main README](../README.md)
|
docs/security.md
CHANGED
@@ -7,3 +7,4 @@ All endpoints require authentication via Bearer token:
|
|
7 |
|
8 |
Unauthorized requests receive `403 Forbidden`.
|
9 |
|
|
|
|
7 |
|
8 |
Unauthorized requests receive `403 Forbidden`.
|
9 |
|
10 |
+
[🔙 Back to Main README](../README.md)
|
docs/setup.md
CHANGED
@@ -21,3 +21,4 @@ SECRET_TOKEN=your_secret_token_here
|
|
21 |
```bash
|
22 |
uvicorn app:app --host 0.0.0.0 --port 8000
|
23 |
```
|
|
|
|
21 |
```bash
|
22 |
uvicorn app:app --host 0.0.0.0 --port 8000
|
23 |
```
|
24 |
+
[🔙 Back to Main README](../README.md)
|
docs/status_code.md
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Error Codes Reference
|
2 |
+
|
3 |
+
## 🔹 Summary Table
|
4 |
+
|
5 |
+
| Code | Message | Description |
|
6 |
+
| ---- | ----------------------------------------------------- | ------------------------------------------ |
|
7 |
+
| 400 | Text must contain at least two words | Input text too short |
|
8 |
+
| 400 | Text should be less than 10,000 characters | Input text too long |
|
9 |
+
| 404 | The file is empty or only contains whitespace | File has no usable content |
|
10 |
+
| 404 | Invalid file type. Only .docx, .pdf, and .txt allowed | Unsupported file format |
|
11 |
+
| 403 | Invalid or expired token | Authentication token is invalid or expired |
|
12 |
+
| 413 | Text must contain at least two words | Text too short (alternative condition) |
|
13 |
+
| 413 | Text must be less than 10,000 characters | Text too long (alternative condition) |
|
14 |
+
| 413 | The image error (preprocessing) | Image size/content issue |
|
15 |
+
| 500 | Error processing the file | Internal server error while processing |
|
16 |
+
|
17 |
+
---
|
18 |
+
|
19 |
+
## 🔍 Error Details
|
20 |
+
|
21 |
+
### `400` - Bad Request
|
22 |
+
|
23 |
+
- **Text must contain at least two words**
|
24 |
+
The input text field is too short. Submit at least two words to proceed.
|
25 |
+
|
26 |
+
- **Text should be less than 10,000 characters**
|
27 |
+
Input text exceeds the maximum allowed character limit. Consider truncating or summarizing the content.
|
28 |
+
|
29 |
+
---
|
30 |
+
|
31 |
+
### `404` - Not Found
|
32 |
+
|
33 |
+
- **The file is empty or only contains whitespace**
|
34 |
+
The uploaded file is invalid due to lack of meaningful content. Ensure the file has readable, non-empty text.
|
35 |
+
|
36 |
+
- **Invalid file type. Only .docx, .pdf, and .txt are allowed**
|
37 |
+
The file format is not supported. Convert the file to one of the allowed formats before uploading.
|
38 |
+
|
39 |
+
---
|
40 |
+
|
41 |
+
### `403` - Forbidden
|
42 |
+
|
43 |
+
- **Invalid or expired token**
|
44 |
+
Your access token is either expired or incorrect. Try logging in again or refreshing the token.
|
45 |
+
|
46 |
+
---
|
47 |
+
|
48 |
+
### `413` - Payload Too Large
|
49 |
+
|
50 |
+
- **Text must contain at least two words**
|
51 |
+
The text payload is too small or malformed under a large upload context. Add more content.
|
52 |
+
|
53 |
+
- **Text must be less than 10,000 characters**
|
54 |
+
The payload exceeds the allowed character limit for a single request. Break it into smaller chunks if needed.
|
55 |
+
|
56 |
+
- **The image error**
|
57 |
+
The uploaded image is too large or corrupted. Try resizing or compressing it before retrying.
|
58 |
+
|
59 |
+
---
|
60 |
+
|
61 |
+
### `500` - Internal Server Error
|
62 |
+
|
63 |
+
- **Error processing the file**
|
64 |
+
An unexpected server-side failure occurred during file analysis. Retry later or contact support if persistent.
|
65 |
+
|
66 |
+
---
|
67 |
+
|
68 |
+
> 📌 **Note:** Always validate inputs, check token status, and follow file guidelines before making requests.
|
docs/structure.md
CHANGED
@@ -1,36 +1,58 @@
|
|
1 |
## 🏗️ Project Structure
|
2 |
|
3 |
-
```
|
4 |
-
|
5 |
-
|
6 |
-
├──
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
│ │ ├── controller.py
|
9 |
│ │ ├── inferencer.py
|
10 |
│ │ ├── model_loader.py
|
11 |
-
│ │
|
12 |
-
│
|
13 |
-
│ └── nepali_text_classifier/ # Nepali (sentencepiece) classifier
|
14 |
│ ├── controller.py
|
15 |
│ ├── inferencer.py
|
16 |
│ ├── model_loader.py
|
17 |
-
│
|
18 |
-
│
|
19 |
-
├──
|
20 |
-
│ ├──
|
21 |
-
│
|
22 |
-
│
|
23 |
-
├──
|
24 |
-
│ ├──
|
25 |
-
│ ├──
|
26 |
-
│ └──
|
27 |
-
├──
|
28 |
-
├──
|
29 |
-
├──
|
30 |
-
|
31 |
-
├──
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
```
|
|
|
34 |
### 🌟 Key Files and Their Roles
|
35 |
|
36 |
- **`app.py`**: Entry point initializing FastAPI app and routes.
|
@@ -39,16 +61,14 @@
|
|
39 |
- **`__init__.py`**: Package initializer for the root module and submodules.
|
40 |
- **`features/text_classifier/`**
|
41 |
- **`controller.py`**: Handles logic between routes and the model.
|
42 |
-
- **`inferencer.py`**: Runs inference and returns predictions as well as file system
|
43 |
-
|
44 |
- **`features/NP/`**
|
45 |
- **`controller.py`**: Handles logic between routes and the model.
|
46 |
-
- **`inferencer.py`**: Runs inference and returns predictions as well as file system
|
47 |
-
|
48 |
- **`model_loader.py`**: Loads the ML model and tokenizer.
|
49 |
- **`preprocess.py`**: Prepares input text for the model.
|
50 |
- **`routes.py`**: Defines API routes for text classification.
|
51 |
|
52 |
-
|
53 |
-
|
54 |
-
-[Main](../README.md)
|
|
|
1 |
## 🏗️ Project Structure
|
2 |
|
3 |
+
```bash
|
4 |
+
AI-Checker/
|
5 |
+
│
|
6 |
+
├── app.py # Main FastAPI entry point
|
7 |
+
├── config.py # Configuration settings
|
8 |
+
├── Dockerfile # Docker build script
|
9 |
+
├── Procfile # Deployment entry for platforms like Heroku/Railway
|
10 |
+
├── requirements.txt # Python dependency list
|
11 |
+
├── README.md # Main project overview 📘
|
12 |
+
│
|
13 |
+
├── features/ # Core AI content detection modules
|
14 |
+
│ ├── image_classifier/ # Classifies AI vs Real images
|
15 |
+
│ │ ├── controller.py
|
16 |
+
│ │ ├── model_loader.py
|
17 |
+
│ │ └── preprocess.py
|
18 |
+
│ ├── image_edit_detector/ # Detects tampered or edited images
|
19 |
+
│ ├── nepali_text_classifier/ # Classifies Nepali text as AI or Human
|
20 |
│ │ ├── controller.py
|
21 |
│ │ ├── inferencer.py
|
22 |
│ │ ├── model_loader.py
|
23 |
+
│ │ └── preprocess.py
|
24 |
+
│ └── text_classifier/ # Classifies English text as AI or Human
|
|
|
25 |
│ ├── controller.py
|
26 |
│ ├── inferencer.py
|
27 |
│ ├── model_loader.py
|
28 |
+
│ └── preprocess.py
|
29 |
+
│
|
30 |
+
├── docs/ # Internal documentation and API references
|
31 |
+
│ ├── api_endpoints.md
|
32 |
+
│ ├── deployment.md
|
33 |
+
│ ├── detector/
|
34 |
+
│ │ ├── ELA.md
|
35 |
+
│ │ ├── fft.md
|
36 |
+
│ │ ├── meta.md
|
37 |
+
│ │ └── note-for-backend.md
|
38 |
+
│ ├── features/
|
39 |
+
│ │ ├── image_classifier.md
|
40 |
+
│ │ ├── nepali_text_classifier.md
|
41 |
+
│ │ └── text_classifier.md
|
42 |
+
│ ├── functions.md
|
43 |
+
│ ├── nestjs_integration.md
|
44 |
+
│ ├── security.md
|
45 |
+
│ ├── setup.md
|
46 |
+
│ └── structure.md
|
47 |
+
│
|
48 |
+
├── IMG_Models/ # Stored model weights
|
49 |
+
│ └── latest-my_cnn_model.h5
|
50 |
+
│
|
51 |
+
├── notebooks/ # Experimental/debug Jupyter notebooks
|
52 |
+
├── static/ # Static files (e.g., UI assets, test inputs)
|
53 |
+
└── test.md # Test usage notes
|
54 |
```
|
55 |
+
|
56 |
### 🌟 Key Files and Their Roles
|
57 |
|
58 |
- **`app.py`**: Entry point initializing FastAPI app and routes.
|
|
|
61 |
- **`__init__.py`**: Package initializer for the root module and submodules.
|
62 |
- **`features/text_classifier/`**
|
63 |
- **`controller.py`**: Handles logic between routes and the model.
|
64 |
+
- **`inferencer.py`**: Runs inference and returns predictions as well as file system
|
65 |
+
utilities.
|
66 |
- **`features/NP/`**
|
67 |
- **`controller.py`**: Handles logic between routes and the model.
|
68 |
+
- **`inferencer.py`**: Runs inference and returns predictions as well as file system
|
69 |
+
utilities.
|
70 |
- **`model_loader.py`**: Loads the ML model and tokenizer.
|
71 |
- **`preprocess.py`**: Prepares input text for the model.
|
72 |
- **`routes.py`**: Defines API routes for text classification.
|
73 |
|
74 |
+
[🔙 Back to Main README](../README.md)
|
|
|
|
features/image_classifier/controller.py
CHANGED
@@ -1,11 +1,16 @@
|
|
1 |
-
from fastapi import HTTPException,File,UploadFile
|
2 |
from .preprocess import preprocess_image
|
3 |
from .inferencer import classify_image
|
|
|
|
|
4 |
async def Classify_Image_router(file: UploadFile = File(...)):
|
5 |
try:
|
6 |
image_array = preprocess_image(file)
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
|
|
11 |
|
|
|
|
|
|
1 |
+
from fastapi import HTTPException, File, UploadFile
|
2 |
from .preprocess import preprocess_image
|
3 |
from .inferencer import classify_image
|
4 |
+
|
5 |
+
|
6 |
async def Classify_Image_router(file: UploadFile = File(...)):
|
7 |
try:
|
8 |
image_array = preprocess_image(file)
|
9 |
+
try:
|
10 |
+
result = classify_image(image_array)
|
11 |
+
return result
|
12 |
+
except:
|
13 |
+
raise HTTPException(status_code=423, detail="something went wrong")
|
14 |
|
15 |
+
except Exception as e:
|
16 |
+
raise HTTPException(status_code=413, detail=str(e))
|
features/image_classifier/inferencer.py
CHANGED
@@ -1,22 +1,42 @@
|
|
1 |
import numpy as np
|
2 |
-
from .model_loader import
|
3 |
-
model = load_model()
|
4 |
-
|
5 |
-
def classify_image(image: np.ndarray):
|
6 |
-
predictions = model.predict(image)[0]
|
7 |
-
human_conf = float(predictions[0])
|
8 |
-
ai_conf = float(predictions[1])
|
9 |
-
|
10 |
-
if ai_conf > 0.55:
|
11 |
-
label = "AI Generated"
|
12 |
-
elif ai_conf < 0.45:
|
13 |
-
label = "Human Generated"
|
14 |
-
else:
|
15 |
-
label = "Maybe AI"
|
16 |
-
|
17 |
-
return {
|
18 |
-
"label": label,
|
19 |
-
"ai_confidence": round(ai_conf * 100, 2),
|
20 |
-
"human_confidence": round(human_conf * 100, 2)
|
21 |
-
}
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import numpy as np
|
2 |
+
from .model_loader import get_model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
4 |
+
# Thresholds
|
5 |
+
AI_THRESHOLD = 0.55
|
6 |
+
HUMAN_THRESHOLD = 0.45
|
7 |
+
|
8 |
+
|
9 |
+
def classify_image(image_array: np.ndarray) -> dict:
|
10 |
+
try:
|
11 |
+
model = get_model()
|
12 |
+
predictions = model.predict(image_array)
|
13 |
+
|
14 |
+
if predictions.ndim != 2 or predictions.shape[1] != 1:
|
15 |
+
raise ValueError(
|
16 |
+
"Model output shape is invalid. Expected shape: (batch, 1)"
|
17 |
+
)
|
18 |
+
|
19 |
+
ai_conf = float(np.clip(predictions[0][0], 0.0, 1.0))
|
20 |
+
human_conf = 1.0 - ai_conf
|
21 |
+
|
22 |
+
# Classification logic
|
23 |
+
if ai_conf > AI_THRESHOLD:
|
24 |
+
label = "AI Generated"
|
25 |
+
elif ai_conf < HUMAN_THRESHOLD:
|
26 |
+
label = "Human Generated"
|
27 |
+
else:
|
28 |
+
label = "Uncertain (Maybe AI)"
|
29 |
+
|
30 |
+
return {
|
31 |
+
"label": label,
|
32 |
+
"ai_confidence": round(ai_conf * 100, 2),
|
33 |
+
"human_confidence": round(human_conf * 100, 2),
|
34 |
+
}
|
35 |
+
|
36 |
+
except Exception as e:
|
37 |
+
return {
|
38 |
+
"error": str(e),
|
39 |
+
"label": "Classification Failed",
|
40 |
+
"ai_confidence": None,
|
41 |
+
"human_confidence": None,
|
42 |
+
}
|
features/image_classifier/model_loader.py
CHANGED
@@ -1,43 +1,58 @@
|
|
1 |
-
import tensorflow as tf
|
2 |
-
from tensorflow.keras.models import load_model as keras_load_model
|
3 |
import os
|
4 |
-
from huggingface_hub import snapshot_download
|
5 |
import shutil
|
|
|
|
|
|
|
|
|
6 |
|
7 |
-
#
|
8 |
REPO_ID = "can-org/AI-VS-HUMAN-IMAGE-classifier"
|
9 |
-
MODEL_DIR = "./
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
-
|
|
|
|
|
|
|
13 |
|
14 |
def warmup():
|
15 |
global _model_img
|
16 |
-
|
17 |
-
download_model_Repo()
|
18 |
_model_img = load_model()
|
|
|
19 |
|
20 |
-
def
|
21 |
-
if os.path.exists(MODEL_DIR):
|
|
|
22 |
return
|
23 |
snapshot_path = snapshot_download(repo_id=REPO_ID)
|
24 |
os.makedirs(MODEL_DIR, exist_ok=True)
|
25 |
shutil.copytree(snapshot_path, MODEL_DIR, dirs_exist_ok=True)
|
26 |
|
27 |
def load_model():
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
43 |
|
|
|
|
|
|
|
1 |
import os
|
|
|
2 |
import shutil
|
3 |
+
import logging
|
4 |
+
import tensorflow as tf
|
5 |
+
from tensorflow.keras.layers import Layer
|
6 |
+
from huggingface_hub import snapshot_download
|
7 |
|
8 |
+
# Model config
|
9 |
REPO_ID = "can-org/AI-VS-HUMAN-IMAGE-classifier"
|
10 |
+
MODEL_DIR = "./IMG_Models"
|
11 |
+
WEIGHTS_PATH = os.path.join(MODEL_DIR, "latest-my_cnn_model.h5")
|
12 |
+
|
13 |
+
# Device info (for logging)
|
14 |
+
gpus = tf.config.list_physical_devices("GPU")
|
15 |
+
device = "cuda" if gpus else "cpu"
|
16 |
+
|
17 |
+
# Global model reference
|
18 |
+
_model_img = None
|
19 |
|
20 |
+
# Custom layer used in the model
|
21 |
+
class Cast(Layer):
|
22 |
+
def call(self, inputs):
|
23 |
+
return tf.cast(inputs, tf.float32)
|
24 |
|
25 |
def warmup():
|
26 |
global _model_img
|
27 |
+
download_model_repo()
|
|
|
28 |
_model_img = load_model()
|
29 |
+
logging.info("Image model is ready.")
|
30 |
|
31 |
+
def download_model_repo():
|
32 |
+
if os.path.exists(MODEL_DIR) and os.path.isdir(MODEL_DIR):
|
33 |
+
logging.info("Image model already exists, skipping download.")
|
34 |
return
|
35 |
snapshot_path = snapshot_download(repo_id=REPO_ID)
|
36 |
os.makedirs(MODEL_DIR, exist_ok=True)
|
37 |
shutil.copytree(snapshot_path, MODEL_DIR, dirs_exist_ok=True)
|
38 |
|
39 |
def load_model():
|
40 |
+
global _model_img
|
41 |
+
if _model_img is not None:
|
42 |
+
return _model_img
|
43 |
+
|
44 |
+
print(f"{'GPU detected' if device == 'cuda' else 'No GPU detected'}, loading model on {device.upper()}.")
|
45 |
+
|
46 |
+
_model_img = tf.keras.models.load_model(
|
47 |
+
WEIGHTS_PATH, custom_objects={"Cast": Cast}
|
48 |
+
)
|
49 |
+
print("Model input shape:", _model_img.input_shape)
|
50 |
+
return _model_img
|
51 |
+
|
52 |
+
def get_model():
|
53 |
+
global _model_img
|
54 |
+
if _model_img is None:
|
55 |
+
download_model_repo()
|
56 |
+
_model_img = load_model()
|
57 |
+
return _model_img
|
58 |
|
features/image_classifier/preprocess.py
CHANGED
@@ -1,18 +1,26 @@
|
|
1 |
import numpy as np
|
2 |
import cv2
|
|
|
|
|
3 |
|
4 |
def preprocess_image(file):
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import numpy as np
|
2 |
import cv2
|
3 |
+
from fastapi import HTTPException
|
4 |
+
|
5 |
|
6 |
def preprocess_image(file):
|
7 |
+
try:
|
8 |
+
file.file.seek(0)
|
9 |
+
image_bytes = file.file.read()
|
10 |
+
nparr = np.frombuffer(image_bytes, np.uint8)
|
11 |
+
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
12 |
+
if img is None:
|
13 |
+
raise HTTPException(status_code=500, detail="Could not decode image.")
|
14 |
|
15 |
+
img = cv2.resize(img, (299, 299))
|
16 |
+
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
17 |
+
img = img / 255.0
|
18 |
+
img = np.expand_dims(img, axis=0).astype(np.float32)
|
19 |
+
return img
|
20 |
|
21 |
+
except HTTPException:
|
22 |
+
raise # Re-raise already defined HTTP errors
|
23 |
+
except Exception as e:
|
24 |
+
raise HTTPException(
|
25 |
+
status_code=500, detail=f"Image preprocessing failed: {str(e)}"
|
26 |
+
)
|
features/image_edit_detector/detectors/ela.py
CHANGED
@@ -1,6 +1,7 @@
|
|
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.
|
@@ -16,7 +17,7 @@ def run_ela(image: Image.Image, quality: int = 90, threshold: int = 15) -> bool:
|
|
16 |
|
17 |
# Recompress the image into JPEG format in memory
|
18 |
buffer = io.BytesIO()
|
19 |
-
image.save(buffer, format=
|
20 |
buffer.seek(0)
|
21 |
recompressed = Image.open(buffer)
|
22 |
|
@@ -29,4 +30,3 @@ def run_ela(image: Image.Image, quality: int = 90, threshold: int = 15) -> bool:
|
|
29 |
_ = ImageEnhance.Brightness(diff).enhance(10)
|
30 |
|
31 |
return max_diff > threshold
|
32 |
-
|
|
|
1 |
from PIL import Image, ImageChops, ImageEnhance
|
2 |
import io
|
3 |
|
4 |
+
|
5 |
def run_ela(image: Image.Image, quality: int = 90, threshold: int = 15) -> bool:
|
6 |
"""
|
7 |
Perform Error Level Analysis to detect image manipulation.
|
|
|
17 |
|
18 |
# Recompress the image into JPEG format in memory
|
19 |
buffer = io.BytesIO()
|
20 |
+
image.save(buffer, format="JPEG", quality=quality)
|
21 |
buffer.seek(0)
|
22 |
recompressed = Image.open(buffer)
|
23 |
|
|
|
30 |
_ = ImageEnhance.Brightness(diff).enhance(10)
|
31 |
|
32 |
return max_diff > threshold
|
|
license.md
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# License - All Rights Reserved
|
2 |
+
|
3 |
+
Copyright (c) 2025 CyberAlertNepal
|
4 |
+
|
5 |
+
This software and all associated materials are **not open source** and are protected under a custom license.
|
6 |
+
|
7 |
+
## Strict Usage Terms
|
8 |
+
|
9 |
+
Unless explicit written permission is granted by **CyberAlertNepal**, **no individual or entity** is allowed to:
|
10 |
+
|
11 |
+
- Use this codebase or its models in any capacity — personal, educational, or commercial.
|
12 |
+
- Modify, copy, distribute, or sublicense any part of this project.
|
13 |
+
- Deploy, mirror, or host this project, either publicly or privately.
|
14 |
+
- Incorporate any component of this project into derivative works or other applications.
|
15 |
+
|
16 |
+
This project is intended for **private, internal use by the author(s) only**.
|
17 |
+
|
18 |
+
Any unauthorized usage, reproduction, or distribution is strictly prohibited and may result in legal action.
|
19 |
+
|
20 |
+
**All rights reserved.**
|
requirements.txt
CHANGED
@@ -15,3 +15,4 @@ tensorflow
|
|
15 |
opencv-python
|
16 |
pillow
|
17 |
scipy
|
|
|
|
15 |
opencv-python
|
16 |
pillow
|
17 |
scipy
|
18 |
+
fitz
|