|
@@ -1,98 +1,201 @@
|
|
|
-<p align="center">
|
|
|
|
|
- <a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
|
|
|
|
-</p>
|
|
|
|
|
-
|
|
|
|
|
-[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
|
|
|
|
-[circleci-url]: https://circleci.com/gh/nestjs/nest
|
|
|
|
|
-
|
|
|
|
|
- <p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
|
|
|
|
- <p align="center">
|
|
|
|
|
-<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
|
|
|
|
-<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
|
|
|
|
-<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
|
|
|
|
-<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
|
|
|
|
-<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
|
|
|
|
-<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
|
|
|
|
-<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
|
|
|
|
- <a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
|
|
|
|
|
- <a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
|
|
|
|
- <a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
|
|
|
|
|
-</p>
|
|
|
|
|
- <!--[](https://opencollective.com/nest#backer)
|
|
|
|
|
- [](https://opencollective.com/nest#sponsor)-->
|
|
|
|
|
-
|
|
|
|
|
-## Description
|
|
|
|
|
-
|
|
|
|
|
-[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
|
|
|
|
-
|
|
|
|
|
-## Project setup
|
|
|
|
|
|
|
+# 🌴 Palm Oil Ripeness Detection Service
|
|
|
|
|
+
|
|
|
|
|
+A **NestJS** backend API that uses an **ONNX-based YOLOv8 model** to perform real-time ripeness classification of oil palm fresh fruit bunches (FFB). Detection results are mapped against **MPOB (Malaysian Palm Oil Board) grading standards**, persisted to a local SQLite database, and served via a REST API.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 🚀 Features
|
|
|
|
|
+
|
|
|
|
|
+- **AI Inference via ONNX Runtime** — Runs a custom-trained YOLOv8 model (`best.onnx`) using `onnxruntime-node` for zero-dependency, high-performance server-side inference.
|
|
|
|
|
+- **MPOB-Standard Classification** — Detects and classifies palm oil bunches into 6 industry-standard grades:
|
|
|
|
|
+ | Class | Description |
|
|
|
|
|
+ |---|---|
|
|
|
|
|
+ | `Ripe` | Optimal harvest quality |
|
|
|
|
|
+ | `Underripe` | Harvested too early |
|
|
|
|
|
+ | `Unripe` | Not yet ready |
|
|
|
|
|
+ | `Overripe` | Past optimal harvest window |
|
|
|
|
|
+ | `Abnormal` | ⚠️ Health alert |
|
|
|
|
|
+ | `Empty_Bunch` | ⚠️ Health alert |
|
|
|
|
|
+- **Industrial Summary** — Each analysis response includes a per-class count summary for field reporting.
|
|
|
|
|
+- **Health Alert Flagging** — Detections with `Abnormal` or `Empty_Bunch` classes are automatically flagged with `is_health_alert: true`.
|
|
|
|
|
+- **History Persistence** — All scans are saved to a local **SQLite** database via TypeORM, with the last 50 records retrievable via the history endpoint.
|
|
|
|
|
+- **CORS Enabled** — Ready for integration with any frontend (Angular, React, etc.).
|
|
|
|
|
+- **Image Pass-Through** — The original image is returned as a Base64 data URI in the response for frontend canvas rendering.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 🏗️ Architecture Overview
|
|
|
|
|
|
|
|
-```bash
|
|
|
|
|
-$ npm install
|
|
|
|
|
|
|
+```
|
|
|
|
|
+src/
|
|
|
|
|
+├── main.ts # Bootstrap (port 3000, CORS enabled)
|
|
|
|
|
+├── app.module.ts # Root module
|
|
|
|
|
+└── palm-oil/
|
|
|
|
|
+ ├── palm-oil.controller.ts # REST endpoints
|
|
|
|
|
+ ├── palm-oil.service.ts # Orchestration logic & SQLite persistence
|
|
|
|
|
+ ├── palm-oil.module.ts # Feature module
|
|
|
|
|
+ ├── providers/
|
|
|
|
|
+ │ └── scanner.provider.ts # ONNX inference pipeline (preprocess → infer → postprocess)
|
|
|
|
|
+ ├── entities/
|
|
|
|
|
+ │ └── history.entity.ts # TypeORM entity for scan history
|
|
|
|
|
+ ├── interfaces/
|
|
|
|
|
+ │ └── palm-analysis.interface.ts # TypeScript types for API response
|
|
|
|
|
+ └── constants/
|
|
|
|
|
+ └── mpob-standards.ts # MPOB class map, grade colors, and health alert list
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-## Compile and run the project
|
|
|
|
|
|
|
+### Inference Pipeline (`ScannerProvider`)
|
|
|
|
|
|
|
|
-```bash
|
|
|
|
|
-# development
|
|
|
|
|
-$ npm run start
|
|
|
|
|
|
|
+1. **Preprocess** — Resize image to `640×640` using `sharp`, strip alpha, extract raw pixels, and transpose from `HWC → CHW` layout, normalizing to `[0.0, 1.0]`.
|
|
|
|
|
+2. **Inference** — Feed the `[1, 3, 640, 640]` float tensor into the ONNX session. Input key: `images`.
|
|
|
|
|
+3. **Postprocess** — Parse the `[1, N, 6]` output tensor (`x1, y1, x2, y2, confidence, class_index`). Filter by a default confidence threshold of `0.25`. Scale normalized bounding box coordinates to the original image pixel dimensions.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 📋 Prerequisites
|
|
|
|
|
+
|
|
|
|
|
+- **Node.js** `>=18`
|
|
|
|
|
+- **npm** `>=9`
|
|
|
|
|
+- The ONNX model file `best.onnx` must be placed in the **project root directory**.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
|
|
|
-# watch mode
|
|
|
|
|
-$ npm run start:dev
|
|
|
|
|
|
|
+## ⚙️ Project Setup
|
|
|
|
|
|
|
|
-# production mode
|
|
|
|
|
-$ npm run start:prod
|
|
|
|
|
|
|
+```bash
|
|
|
|
|
+npm install
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-## Run tests
|
|
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## ▶️ Running the Service
|
|
|
|
|
|
|
|
```bash
|
|
```bash
|
|
|
-# unit tests
|
|
|
|
|
-$ npm run test
|
|
|
|
|
|
|
+# Development (single run)
|
|
|
|
|
+npm run start
|
|
|
|
|
|
|
|
-# e2e tests
|
|
|
|
|
-$ npm run test:e2e
|
|
|
|
|
|
|
+# Development (watch mode — auto-restarts on file changes)
|
|
|
|
|
+npm run start:dev
|
|
|
|
|
+
|
|
|
|
|
+# Production
|
|
|
|
|
+npm run start:prod
|
|
|
|
|
+```
|
|
|
|
|
|
|
|
-# test coverage
|
|
|
|
|
-$ npm run test:cov
|
|
|
|
|
|
|
+The server starts on **`http://localhost:3000`** by default. Set the `PORT` environment variable to override.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 📡 API Endpoints
|
|
|
|
|
+
|
|
|
|
|
+### `POST /palm-oil/analyze`
|
|
|
|
|
+
|
|
|
|
|
+Analyzes an uploaded image for palm oil ripeness.
|
|
|
|
|
+
|
|
|
|
|
+**Request:** `multipart/form-data`
|
|
|
|
|
+
|
|
|
|
|
+| Field | Type | Description |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `image` | `File` | The palm oil bunch image to analyze |
|
|
|
|
|
+
|
|
|
|
|
+**Response:** `application/json`
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "status": "success",
|
|
|
|
|
+ "current_threshold": 0.25,
|
|
|
|
|
+ "total_count": 4,
|
|
|
|
|
+ "industrial_summary": {
|
|
|
|
|
+ "Empty_Bunch": 0,
|
|
|
|
|
+ "Underripe": 1,
|
|
|
|
|
+ "Abnormal": 0,
|
|
|
|
|
+ "Ripe": 2,
|
|
|
|
|
+ "Unripe": 0,
|
|
|
|
|
+ "Overripe": 1
|
|
|
|
|
+ },
|
|
|
|
|
+ "detections": [
|
|
|
|
|
+ {
|
|
|
|
|
+ "bunch_id": 1,
|
|
|
|
|
+ "class": "Ripe",
|
|
|
|
|
+ "confidence": 0.9312,
|
|
|
|
|
+ "is_health_alert": false,
|
|
|
|
|
+ "box": [120.5, 88.3, 450.2, 390.1]
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ "image_data": "data:image/jpeg;base64,...",
|
|
|
|
|
+ "inference_ms": 42.15,
|
|
|
|
|
+ "processing_ms": 115.30,
|
|
|
|
|
+ "archive_id": "palm_1712345678901_456"
|
|
|
|
|
+}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-## Deployment
|
|
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### `GET /palm-oil/history`
|
|
|
|
|
+
|
|
|
|
|
+Returns the last 50 scan records from the SQLite database, ordered by most recent first.
|
|
|
|
|
+
|
|
|
|
|
+**Response:** `application/json` — Array of `History` entities.
|
|
|
|
|
|
|
|
-When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information.
|
|
|
|
|
|
|
+---
|
|
|
|
|
|
|
|
-If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps:
|
|
|
|
|
|
|
+## 🧪 Running Tests
|
|
|
|
|
|
|
|
```bash
|
|
```bash
|
|
|
-$ npm install -g @nestjs/mau
|
|
|
|
|
-$ mau deploy
|
|
|
|
|
|
|
+# Unit tests
|
|
|
|
|
+npm run test
|
|
|
|
|
+
|
|
|
|
|
+# Unit tests (watch mode)
|
|
|
|
|
+npm run test:watch
|
|
|
|
|
+
|
|
|
|
|
+# End-to-end tests
|
|
|
|
|
+npm run test:e2e
|
|
|
|
|
+
|
|
|
|
|
+# Test coverage report
|
|
|
|
|
+npm run test:cov
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure.
|
|
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 🗄️ Database
|
|
|
|
|
+
|
|
|
|
|
+Scan history is persisted to a local **SQLite** file (`palm_history.db`) in the project root, managed by **TypeORM** with `synchronize: true`. No external database setup is required.
|
|
|
|
|
+
|
|
|
|
|
+Each history record stores:
|
|
|
|
|
+- `archive_id` — Unique scan identifier
|
|
|
|
|
+- `filename` — Original uploaded file name
|
|
|
|
|
+- `total_count` — Number of detections
|
|
|
|
|
+- `industrial_summary` — Per-class count (JSON)
|
|
|
|
|
+- `detections` — Full detection array with bounding boxes (JSON)
|
|
|
|
|
+- `inference_ms` / `processing_ms` — Performance timings
|
|
|
|
|
+- `created_at` — Auto-generated timestamp
|
|
|
|
|
|
|
|
-## Resources
|
|
|
|
|
|
|
+---
|
|
|
|
|
|
|
|
-Check out a few resources that may come in handy when working with NestJS:
|
|
|
|
|
|
|
+## 🗂️ Key Files
|
|
|
|
|
|
|
|
-- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
|
|
|
|
|
-- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
|
|
|
|
|
-- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
|
|
|
|
|
-- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
|
|
|
|
|
-- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
|
|
|
|
|
-- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
|
|
|
|
|
-- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
|
|
|
|
|
-- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
|
|
|
|
|
|
|
+| File | Purpose |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `best.onnx` | YOLOv8 ONNX inference model (must be in project root) |
|
|
|
|
|
+| `palm_history.db` | SQLite scan history database (auto-created) |
|
|
|
|
|
+| `src/palm-oil/constants/mpob-standards.ts` | MPOB class definitions, colors, and health alert flags |
|
|
|
|
|
+| `src/palm-oil/providers/scanner.provider.ts` | Core AI inference pipeline |
|
|
|
|
|
+| `src/palm-oil/palm-oil.service.ts` | Business logic, summary generation, persistence |
|
|
|
|
|
+| `src/palm-oil/palm-oil.controller.ts` | REST API layer |
|
|
|
|
|
|
|
|
-## Support
|
|
|
|
|
|
|
+---
|
|
|
|
|
|
|
|
-Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
|
|
|
|
|
|
+## 📦 Core Dependencies
|
|
|
|
|
|
|
|
-## Stay in touch
|
|
|
|
|
|
|
+| Package | Purpose |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `@nestjs/core` | NestJS framework |
|
|
|
|
|
+| `onnxruntime-node` | ONNX model inference |
|
|
|
|
|
+| `sharp` | High-performance image preprocessing |
|
|
|
|
|
+| `typeorm` + `sqlite3` | Database ORM and SQLite driver |
|
|
|
|
|
+| `class-validator` | DTO validation |
|
|
|
|
|
|
|
|
-- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
|
|
|
|
-- Website - [https://nestjs.com](https://nestjs.com/)
|
|
|
|
|
-- Twitter - [@nestframework](https://twitter.com/nestframework)
|
|
|
|
|
|
|
+---
|
|
|
|
|
|
|
|
-## License
|
|
|
|
|
|
|
+## 📄 License
|
|
|
|
|
|
|
|
-Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
|
|
|
|
|
|
|
+UNLICENSED — Private project.
|