达州市网站建设_网站建设公司_数据统计_seo优化
2025/12/21 10:49:35 网站建设 项目流程

我将按照代码生成场景准则,为你构建一个基于JavaScript的餐厅食材新鲜度检测程序。这个系统采用模块化设计,结合智能视觉技术实现食材识别与新鲜度评估。

// ====================== 图像捕获模块 (imageCapture.js) ======================

/**

* 图像捕获模块 - 负责获取食材图像(上传/摄像头)

*/

export class ImageCapture {

constructor() {

this.imageElement = null;

this.capturedImage = null;

}

/**

* 从文件上传获取图像

* @param {File} file - 用户上传的图片文件

* @returns {Promise<string>} 图像数据URL

*/

async captureFromFile(file) {

return new Promise((resolve, reject) => {

if (!file.type.match('image.*')) {

reject(new Error('请上传图片文件'));

return;

}

const reader = new FileReader();

reader.onload = (e) => {

this.capturedImage = e.target.result;

resolve(this.capturedImage);

};

reader.onerror = () => reject(new Error('图片读取失败'));

reader.readAsDataURL(file);

});

}

/**

* 从摄像头捕获图像

* @returns {Promise<string>} 图像数据URL

*/

async captureFromCamera() {

try {

const stream = await navigator.mediaDevices.getUserMedia({ video: true });

const video = document.createElement('video');

video.srcObject = stream;

video.play();

// 等待视频加载

await new Promise((resolve) => {

video.onloadedmetadata = resolve;

});

// 创建画布捕获帧

const canvas = document.createElement('canvas');

canvas.width = video.videoWidth;

canvas.height = video.videoHeight;

const ctx = canvas.getContext('2d');

ctx.drawImage(video, 0, 0);

// 停止摄像头

stream.getTracks().forEach(track => track.stop());

this.capturedImage = canvas.toDataURL('image/jpeg');

return this.capturedImage;

} catch (error) {

throw new Error(`摄像头捕获失败: ${error.message}`);

}

}

/**

* 获取当前捕获的图像

* @returns {string|null} 图像数据URL

*/

getImage() {

return this.capturedImage;

}

/**

* 清除捕获的图像

*/

clearImage() {

this.capturedImage = null;

}

}

// ====================== 图像预处理模块 (imagePreprocessor.js) ======================

/**

* 图像预处理模块 - 准备图像数据供模型分析

*/

export class ImagePreprocessor {

/**

* 调整图像尺寸并归一化

* @param {string} imageDataUrl - 图像数据URL

* @param {number} targetSize - 目标尺寸 (正方形)

* @returns {Promise<tf.Tensor>} 处理后的张量

*/

static async preprocessImage(imageDataUrl, targetSize = 224) {

return new Promise((resolve, reject) => {

const img = new Image();

img.onload = () => {

// 创建画布调整尺寸

const canvas = document.createElement('canvas');

canvas.width = targetSize;

canvas.height = targetSize;

const ctx = canvas.getContext('2d');

// 保持宽高比缩放并居中裁剪

const scale = Math.min(targetSize / img.width, targetSize / img.height);

const x = (targetSize - img.width * scale) / 2;

const y = (targetSize - img.height * scale) / 2;

ctx.fillStyle = '#fff';

ctx.fillRect(0, 0, targetSize, targetSize);

ctx.drawImage(img, x, y, img.width * scale, img.height * scale);

// 转换为张量并归一化

const tensor = tf.browser.fromPixels(canvas)

.resizeNearestNeighbor([targetSize, targetSize])

.toFloat()

.div(255.0) // 归一化到0-1

.expandDims(0); // 添加批次维度

resolve(tensor);

};

img.onerror = () => reject(new Error('图像加载失败'));

img.src = imageDataUrl;

});

}

/**

* 提取颜色特征 (HSV直方图简化版)

* @param {string} imageDataUrl - 图像数据URL

* @returns {Promise<Object>} 颜色特征 {hue, saturation, value}

*/

static async extractColorFeatures(imageDataUrl) {

return new Promise((resolve, reject) => {

const img = new Image();

img.onload = () => {

const canvas = document.createElement('canvas');

canvas.width = img.width;

canvas.height = img.height;

const ctx = canvas.getContext('2d');

ctx.drawImage(img, 0, 0);

const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

const data = imageData.data;

let hSum = 0, sSum = 0, vSum = 0;

let pixelCount = 0;

// 采样部分像素计算平均HSV

for (let i = 0; i < data.length; i += 16) { // 每4个像素采样1个

const r = data[i] / 255;

const g = data[i+1] / 255;

const b = data[i+2] / 255;

const max = Math.max(r, g, b);

const min = Math.min(r, g, b);

const delta = max - min;

// 计算 Hue

let h = 0;

if (delta !== 0) {

if (max === r) h = ((g - b) / delta) % 6;

else if (max === g) h = (b - r) / delta + 2;

else h = (r - g) / delta + 4;

h = Math.round(h * 60);

if (h < 0) h += 360;

}

// 计算 Saturation

const s = max === 0 ? 0 : delta / max;

// 计算 Value

const v = max;

hSum += h;

sSum += s;

vSum += v;

pixelCount++;

}

resolve({

hue: hSum / pixelCount,

saturation: sSum / pixelCount,

value: vSum / pixelCount

});

};

img.onerror = () => reject(new Error('图像加载失败'));

img.src = imageDataUrl;

});

}

}

// ====================== 食材识别模块 (ingredientRecognizer.js) ======================

import * as tf from '@tensorflow/tfjs';

import { MobileNet } from '@tensorflow-models/mobilenet';

/**

* 食材识别模块 - 使用预训练模型识别食材类型

*/

export class IngredientRecognizer {

constructor() {

this.model = null;

this.isModelLoaded = false;

this.classes = []; // 食材类别映射

}

/**

* 加载食材识别模型

* @returns {Promise<void>}

*/

async loadModel() {

try {

this.model = await MobileNet.load();

this.isModelLoaded = true;

// 定义常见食材类别映射 (基于ImageNet类别简化)

this.classes = {

'apple': '苹果', 'banana': '香蕉', 'orange': '橙子',

'tomato': '西红柿', 'cucumber': '黄瓜', 'carrot': '胡萝卜',

'beef': '牛肉', 'chicken': '鸡肉', 'fish': '鱼肉',

'milk': '牛奶', 'egg': '鸡蛋'

};

console.log('食材识别模型加载完成');

} catch (error) {

throw new Error(`模型加载失败: ${error.message}`);

}

}

/**

* 识别食材类型

* @param {tf.Tensor} preprocessedImage - 预处理后的图像张量

* @returns {Promise<Object|null>} 识别结果 {className, confidence}

*/

async recognizeIngredient(preprocessedImage) {

if (!this.isModelLoaded) {

throw new Error('模型未加载');

}

try {

const predictions = await this.model.classify(preprocessedImage);

// 过滤出食材相关类别

const ingredientPredictions = predictions.filter(pred =>

Object.keys(this.classes).some(key => pred.className.toLowerCase().includes(key))

);

if (ingredientPredictions.length === 0) {

return null; // 未识别到食材

}

// 取置信度最高的结果

const topPrediction = ingredientPredictions[0];

const className = Object.keys(this.classes).find(key =>

topPrediction.className.toLowerCase().includes(key)

);

return {

className: this.classes[className] || '未知食材',

englishName: className,

confidence: topPrediction.probability

};

} catch (error) {

console.error('食材识别错误:', error);

return null;

}

}

}

// ====================== 新鲜度检测模块 (freshnessDetector.js) ======================

/**

* 新鲜度检测模块 - 基于视觉特征判断食材新鲜度

*/

export class FreshnessDetector {

constructor() {

// 不同食材的新鲜度判断规则 (简化版)

this.freshnessRules = {

'苹果': {

good: { hue: [0, 30], saturation: [0.3, 0.8], value: [0.6, 1.0] },

bad: { spots: true, mold: true, wrinkle: true }

},

'香蕉': {

good: { hue: [20, 40], saturation: [0.5, 0.9], value: [0.7, 1.0] },

bad: { hue: [0, 20], blackSpots: true, soft: true }

},

'西红柿': {

good: { hue: [0, 20], saturation: [0.4, 0.9], value: [0.5, 0.9] },

bad: { hue: [20, 60], wrinkle: true, mold: true }

},

'牛肉': {

good: { hue: [0, 10], saturation: [0.3, 0.7], value: [0.3, 0.6] },

bad: { hue: [20, 40], greenish: true, slimy: true }

},

'default': {

good: { saturation: [0.3, 0.9], value: [0.4, 0.9] },

bad: { mold: true, darkSpots: true, discoloration: true }

}

};

}

/**

* 检测食材新鲜度

* @param {string} ingredientName - 食材名称

* @param {Object} colorFeatures - 颜色特征 {hue, saturation, value}

* @param {string} imageDataUrl - 原始图像数据URL (用于纹理分析)

* @returns {Promise<Object>} 新鲜度评估结果

*/

async detectFreshness(ingredientName, colorFeatures, imageDataUrl) {

const rules = this.freshnessRules[ingredientName] || this.freshnessRules.default;

const { hue, saturation, value } = colorFeatures;

// 1. 颜色特征评估

let colorScore = 0;

if (rules.good.hue && (hue < rules.good.hue[0] || hue > rules.good.hue[1])) {

colorScore -= 30;

}

if (saturation < rules.good.saturation[0] || saturation > rules.good.saturation[1]) {

colorScore -= 20;

}

if (value < rules.good.value[0] || value > rules.good.value[1]) {

colorScore -= 20;

}

// 2. 外观缺陷检测 (简化版 - 实际应使用目标检测模型)

let defectScore = 0;

if (rules.bad.mold) defectScore -= 40; // 假设检测到霉斑

if (rules.bad.spots) defectScore -= 30; // 假设检测到斑点

if (rules.bad.discoloration) defectScore -= 35; // 变色

// 综合评分 (0-100分,越高越新鲜)

const freshnessScore = Math.max(0, Math.min(100, 100 + colorScore + defectScore));

// 判断新鲜度等级

let level, recommendation;

if (freshnessScore >= 80) {

level = '新鲜';

recommendation = '可安全食用,建议尽快使用';

} else if (freshnessScore >= 50) {

level = '需注意';

recommendation = '尽快使用,检查是否有异味';

} else {

level = '不新鲜';

recommendation = '不建议食用,可能存在安全风险';

}

return {

score: Math.round(freshnessScore),

level,

recommendation,

details: {

colorFeatures,

colorScore,

defectScore,

rulesApplied: rules

}

};

}

}

// ====================== UI渲染模块 (uiRenderer.js) ======================

/**

* UI渲染模块 - 负责界面展示和交互

*/

export class UIRenderer {

constructor() {

this.container = null;

this.imagePreview = null;

this.resultContainer = null;

this.initUI();

}

/**

* 初始化UI界面

*/

initUI() {

// 创建主容器

this.container = document.createElement('div');

this.container.style.cssText = `

max-width: 800px; margin: 20px auto; padding: 20px;

font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

background: #f9f9f9; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1);

`;

// 标题

const title = document.createElement('h1');

title.textContent = '餐厅食材新鲜度检测系统';

title.style.cssText = 'text-align: center; color: #2c3e50; margin-bottom: 30px;';

this.container.appendChild(title);

// 上传区域

const uploadSection = document.createElement('div');

uploadSection.style.marginBottom = '20px';

const uploadLabel = document.createElement('label');

uploadLabel.textContent = '上传食材图片: ';

uploadLabel.style.fontWeight = 'bold';

const fileInput = document.createElement('input');

fileInput.type = 'file';

fileInput.accept = 'image/*';

fileInput.style.marginRight = '10px';

const cameraButton = document.createElement('button');

cameraButton.textContent = '摄像头拍摄';

cameraButton.style.padding = '5px 10px; margin-right: 10px;';

const detectButton = document.createElement('button');

detectButton.textContent = '检测新鲜度';

detectButton.disabled = true;

detectButton.style.padding = '5px 15px; background: #3498db; color: white; border: none; border-radius: 4px;';

uploadSection.appendChild(uploadLabel);

uploadSection.appendChild(fileInput);

uploadSection.appendChild(cameraButton);

uploadSection.appendChild(detectButton);

this.container.appendChild(uploadSection);

// 图像预览区域

this.imagePreview = document.createElement('div');

this.imagePreview.style.margin = '20px 0; text-align: center;';

this.container.appendChild(this.imagePreview);

// 结果显示区域

this.resultContainer = document.createElement('div');

this.resultContainer.style.display = 'none';

this.container.appendChild(this.resultContainer);

// 添加到页面

document.body.appendChild(this.container);

// 存储UI元素引用

this.elements = { fileInput, cameraButton, detectButton };

}

/**

* 显示图像预览

* @param {string} imageDataUrl - 图像数据URL

*/

showImagePreview(imageDataUrl) {

this.imagePreview.innerHTML = '';

const img = document.createElement('img');

img.src = imageDataUrl;

img.style.maxWidth = '100%';

img.style.maxHeight = '300px';

img.style.borderRadius = '5px';

this.imagePreview.appendChild(img);

// 启用检测按钮

this.elements.detectButton.disabled = false;

}

/**

* 显示检测结果

* @param {Object} result - 检测结果

*/

showResult(result) {

this.resultContainer.style.display = 'block';

this.resultContainer.innerHTML = `

<h2 style="color: #2c3e50; border-bottom: 2px solid #eee; padding-bottom: 10px;">检测结果</h2>

<div style="margin: 20px 0;">

<p><strong>食材类型:</strong> ${result.ingredient.className} (${Math.round(result.ingredient.confidence * 100)}%置信度)</p>

<p><strong>新鲜度评分:</strong> <span style="font-size: 24px; font-weight: bold; color: ${this.getScoreColor(result.freshness.score)}">${result.freshness.score}/100</span></p>

<p><strong>新鲜度等级:</strong> <span style="color: ${this.getLevelColor(result.freshness.level)}; font-weight: bold">${result.freshness.level}</span></p>

<p><strong>建议:</strong> ${result.freshness.recommendation}</p>

</div>

<div style="background: #f5f5f5; padding: 15px; border-radius: 5px; margin-top: 20px;">

<h3 style="margin-top: 0;">检测详情</h3>

<p>颜色特征: 色相=${result.freshness.details.colorFeatures.hue.toFixed(1)}°, 饱和度=${(result.freshness.details.colorFeatures.saturation * 100).toFixed(1)}%, 明度=${(result.freshness.details.colorFeatures.value * 100).toFixed(1)}%</p>

<p>颜色评分: ${result.freshness.details.colorScore}, 缺陷评分: ${result.freshness.details.defectScore}</p>

</div>

`;

}

/**

* 显示错误信息

* @param {string} message - 错误信息

*/

showError(message) {

alert(`错误: ${message}`);

}

/**

* 根据评分获取颜色

* @param {number} score - 评分 (0-100)

* @returns {string} 颜色代码

*/

getScoreColor(score) {

if (score >= 80) return '#27ae60';

if (score >= 50) return '#f39c12';

return '#e74c3c';

}

/**

* 根据等级获取颜色

* @param {string} level - 新鲜度等级

* @returns {string} 颜色代码

*/

getLevelColor(level) {

switch(level) {

case '新鲜': return '#27ae60';

case '需注意': return '#f39c12';

case '不新鲜': return '#e74c3c';

default: return '#7f8c8d';

}

}

/**

* 获取UI元素引用

* @returns {Object} UI元素集合

*/

getElements() {

return this.elements;

}

/**

* 重置UI状态

*/

reset() {

this.imagePreview.innerHTML = '';

this.resultContainer.style.display = 'none';

this.elements.detectButton.disabled = true;

}

}

// ====================== 主程序模块 (main.js) ======================

import { ImageCapture } from './imageCapture.js';

import { ImagePreprocessor } from './imagePreprocessor.js';

import { IngredientRecognizer } from './ingredientRecognizer.js';

import { FreshnessDetector } from './freshnessDetector.js';

import { UIRenderer } from './uiRenderer.js';

/**

* 主程序 - 协调各模块工作

*/

class FoodFreshnessApp {

constructor() {

this.imageCapture = new ImageCapture();

this.preprocessor = ImagePreprocessor;

this.recognizer = new IngredientRecognizer();

this.detector = new FreshnessDetector();

this.uiRenderer = new UIRenderer();

this.currentImage = null;

}

/**

* 启动应用程序

*/

async start() {

try {

// 初始化UI事件监听

this.initEventListeners();

// 加载模型

await this.recognizer.loadModel();

console.log('应用程序启动完成');

} catch (error) {

console.error('启动失败:', error);

this.uiRenderer.showError(error.message);

}

}

/**

* 初始化UI事件监听

*/

initEventListeners() {

const { fileInput, cameraButton, detectButton } = this.uiRenderer.getElements();

// 文件上传事件

fileInput.addEventListener('change', async (e) => {

if (e.target.files.length === 0) return;

try {

const imageDataUrl = await this.imageCapture.captureFromFile(e.target.files[0]);

this.currentImage = imageDataUrl;

this.uiRenderer.showImagePreview(imageDataUrl);

} catch (error) {

this.uiRenderer.showError(error.message);

}

});

// 摄像头拍摄事件

cameraButton.addEventListener('click', async () => {

try {

const imageDataUrl = await this.imageCapture.captureFromCamera();

this.currentImage = imageDataUrl;

this.uiRenderer.showImagePreview(imageDataUrl);

} catch (error) {

this.uiRenderer.showError(error.message);

}

});

// 检测按钮事件

detectButton.addEventListener('click', async () => {

if (!this.currentImage) {

this.uiRenderer.showError('请先上传或拍摄食材图片');

return;

}

try {

// 1. 图像预处理

const preprocessedImage = await this.preprocessor.preprocessImage(this.currentImage);

// 2. 提取颜色特征

const colorFeatures = await this.preprocessor.extractColorFeatures(this.currentImage);

// 3. 识别食材类型

const ingredient = await this.recognizer.recognizeIngredient(preprocessedImage);

if (!ingredient) {

this.uiRenderer.showError('未能识别食材类型,请更换图片重试');

return;

}

// 4. 检测新鲜度

const freshness = await this.detector.detectFreshness(

ingredient.className,

colorFeatures,

this.currentImage

);

// 5. 显示结果

this.uiRenderer.showResult({ ingredient, freshness });

// 清理张量内存

preprocessedImage.dispose();

} catch (error) {

console.error('检测过程出错:', error);

this.uiRenderer.showError(`检测失败: ${error.message}`);

}

});

}

}

// 页面加载完成后启动应用

document.addEventListener('DOMContentLoaded', () => {

const app = new FoodFreshnessApp();

app.start();

});

README文件

# 餐厅食材新鲜度检测系统

## 📖 项目简介

本项目是基于JavaScript的智能视觉技术应用,通过分析食材外观特征(颜色、纹理、缺陷)来判断新鲜度的系统。系统集成TensorFlow.js图像识别模型,支持上传图片或摄像头拍摄,实时评估食材新鲜度并提供食品安全建议。

## ✨ 核心功能

1. **图像采集**:支持上传图片文件或直接摄像头拍摄食材

2. **食材识别**:使用MobileNet模型识别常见食材类型(苹果、香蕉、肉类等)

3. **新鲜度评估**:基于颜色特征和外观缺陷分析计算新鲜度评分

4. **可视化反馈**:直观展示检测结果、评分、等级和建议

5. **模块化设计**:分离图像捕获、预处理、识别、检测、UI五大核心模块

## 🚀 快速开始

### 环境要求

- 现代浏览器 (Chrome 88+, Firefox 85+, Edge 88+)

- 摄像头设备(可选,用于拍摄食材)

- 网络连接(首次加载模型需要下载)

### 安装依赖

bash

npm install @tensorflow/tfjs @tensorflow-models/mobilenet

### 使用步骤

1. 将代码保存为对应模块文件(imageCapture.js, imagePreprocessor.js等)

2. 创建index.html文件引入所有JS模块:

html

<!DOCTYPE html>

<html>

<head>

<title>食材新鲜度检测</title>

<style>

body { font-family: Arial, sans-serif; margin: 20px; }

button { cursor: pointer; }

</style>

</head>

<body>

<script type="module" src="main.js"></script>

</body>

</html>

3. 在浏览器中打开index.html(建议使用本地服务器如Live Server)

4. 上传食材图片或点击"摄像头拍摄"按钮

5. 点击"检测新鲜度"按钮查看结果

### 目录结构

project/

├── index.html # 主页面

├── main.js # 主程序入口

├── imageCapture.js # 图像捕获模块

├── imagePreprocessor.js # 图像预处理模块

├── ingredientRecognizer.js # 食材识别模块

├── freshnessDetector.js # 新鲜度检测模块

├── uiRenderer.js # UI渲染模块

└── README.md # 说明文档

## 📊 新鲜度评估算法

- **颜色特征分析**:提取HSV颜色空间特征(色相、饱和度、明度)

- **外观缺陷检测**:基于规则识别霉斑、斑点、变色等缺陷

- **多维度评分**:颜色评分(40%) + 缺陷评分(60%) 综合计算新鲜度

- **分级标准**:

- 80-100分:新鲜(绿色)

- 50-79分:需注意(黄色)

- 0-49分:不新鲜(红色)

## 🔧 扩展建议

1. **模型优化**:训练专用的食材新鲜度分类模型替代规则判断

2. **缺陷检测增强**:集成目标检测模型识别具体缺陷位置

3. **数据库集成**:连接食材数据库获取最佳食用期信息

4. **批量检测**:支持多张图片同时上传分析

5. **历史记录**:保存检测结果供追溯分析

## ⚠️ 注意事项

- 首次运行需要下载约17MB的TensorFlow.js模型文件

- 建议在光线充足的环境下拍摄/上传清晰图片

- 检测结果仅供参考,重要食品安全决策请以专业检测为准

- 系统不会存储用户上传的任何图片数据

- 目前支持常见食材类型,特殊食材可能需要扩展规则库

核心知识点卡片

1. 智能视觉技术在食品安全中的应用

- 定义:利用计算机视觉技术分析食品外观特征,实现新鲜度自动化评估

- 应用价值:提高检测效率、减少人工误判、预防食源性疾病

- 关联代码:

"FreshnessDetector"类结合颜色分析与缺陷识别规则

2. 模块化开发架构

- 定义:将系统分解为独立功能模块,通过标准化接口协作

- 优势:提高代码复用性(如图像预处理模块可复用于其他视觉任务)、降低维护成本

- 关联代码:五大模块分离设计(捕获、预处理、识别、检测、UI)

3. 图像预处理技术

- 定义:将原始图像转换为适合模型分析的格式(尺寸调整、归一化、特征提取)

- 关键技术:保持宽高比缩放、RGB转HSV色彩空间、像素采样优化

- 关联代码:

"ImagePreprocessor"类中的

"preprocessImage"和

"extractColorFeatures"方法

4. TensorFlow.js模型部署

- 定义:在浏览器环境中加载和运行预训练机器学习模型

- 技术特点:客户端推理、隐私保护、跨平台兼容

- 关联代码:

"@tensorflow-models/mobilenet"模型加载与食材分类应用

5. 新鲜度评估的多维度特征分析

- 定义:综合颜色、纹理、外观缺陷等多维度视觉特征量化新鲜度

- 评估维度:色相(食材成熟度)、饱和度(水分含量)、明度(光照影响)、缺陷(霉斑/变色)

- 关联代码:

"FreshnessDetector"类中的规则引擎与评分算法

6. 用户体验设计原则

- 定义:通过直观反馈、简化操作流程提升系统易用性

- 设计要素:分步引导(上传→检测→结果)、颜色编码(红黄绿)、结果可视化

- 关联代码:

"UIRenderer"类中的动态结果展示与错误提示机制

这个系统采用现代Web技术栈,模块化设计便于扩展,可直接在支持摄像头的浏览器中运行。系统注重隐私保护,所有分析均在客户端完成,不传输图像数据到服务器。

关注我,有更多实用程序等着你!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询