
鸿蒙跨端智能皮肤检测系统开发指南 原创
鸿蒙跨端智能皮肤检测系统开发指南
一、系统架构设计
基于HarmonyOS的AI能力和分布式技术,构建皮肤状态检测与护理建议系统:
图像采集层:通过摄像头获取皮肤图像
皮肤分析层:使用AI模型分析皮肤状态
建议生成层:根据分析结果生成护理建议
跨端同步层:多设备间同步检测结果和护理方案
!https://example.com/harmony-skin-analysis-arch.png
二、核心代码实现
皮肤检测服务
// SkinAnalysisService.ets
import ai from ‘@ohos.ai’;
import camera from ‘@ohos.multimedia.camera’;
import distributedData from ‘@ohos.distributedData’;
import { SkinAnalysisResult, SkinCareAdvice } from ‘./SkinTypes’;
class SkinAnalysisService {
private static instance: SkinAnalysisService = null;
private modelManager: ai.ModelManager;
private dataManager: distributedData.DataManager;
private resultListeners: SkinAnalysisListener[] = [];
private constructor() {
this.initModelManager();
this.initDataManager();
public static getInstance(): SkinAnalysisService {
if (!SkinAnalysisService.instance) {
SkinAnalysisService.instance = new SkinAnalysisService();
return SkinAnalysisService.instance;
private initModelManager(): void {
try {
this.modelManager = ai.createModelManager(getContext());
// 加载皮肤分析模型
this.modelManager.loadModel({
modelName: 'skin_analysis',
modelPath: 'resources/rawfile/skin_analysis.model',
callback: (err, data) => {
if (err) {
console.error('加载皮肤模型失败:', JSON.stringify(err));
}
});
// 加载护理建议模型
this.modelManager.loadModel({
modelName: 'skin_care_advice',
modelPath: 'resources/rawfile/skin_care.model',
callback: (err, data) => {
if (err) {
console.error('加载护理模型失败:', JSON.stringify(err));
}
});
catch (err) {
console.error('初始化模型管理器失败:', JSON.stringify(err));
}
private initDataManager(): void {
this.dataManager = distributedData.createDataManager({
bundleName: ‘com.example.skinanalysis’,
area: distributedData.Area.GLOBAL,
isEncrypted: true
});
this.dataManager.registerDataListener('skin_result_sync', (data) => {
this.handleSyncData(data);
});
public async requestPermissions(): Promise<boolean> {
try {
const permissions = [
'ohos.permission.USE_AI',
'ohos.permission.CAMERA',
'ohos.permission.DISTRIBUTED_DATASYNC'
];
const result = await abilityAccessCtrl.requestPermissionsFromUser(
getContext(),
permissions
);
return result.grantedPermissions.length === permissions.length;
catch (err) {
console.error('请求权限失败:', JSON.stringify(err));
return false;
}
public async analyzeSkin(imageData: ArrayBuffer): Promise<SkinAnalysisResult> {
try {
const input = {
data: imageData,
width: 224,
height: 224,
format: ‘RGB’
};
const analysisResult = await this.modelManager.runModel({
modelName: 'skin_analysis',
input: input
});
const advice = await this.generateAdvice(analysisResult.result);
const result: SkinAnalysisResult = {
id: Date.now().toString(),
moistureLevel: analysisResult.result.moisture,
oilinessLevel: analysisResult.result.oiliness,
sensitivityLevel: analysisResult.result.sensitivity,
acneLevel: analysisResult.result.acne,
pigmentationLevel: analysisResult.result.pigmentation,
advice: advice,
timestamp: Date.now(),
imageData: imageData
};
// 同步分析结果
this.syncResult(result);
return result;
catch (err) {
console.error('皮肤分析失败:', JSON.stringify(err));
throw err;
}
private async generateAdvice(analysis: any): Promise<SkinCareAdvice> {
const input = {
moisture: analysis.moisture,
oiliness: analysis.oiliness,
sensitivity: analysis.sensitivity,
acne: analysis.acne,
pigmentation: analysis.pigmentation
};
const output = await this.modelManager.runModel({
modelName: 'skin_care_advice',
input: input
});
return output.result;
private syncResult(result: SkinAnalysisResult): void {
this.dataManager.syncData('skin_result_sync', {
type: 'skin_result',
data: result,
timestamp: Date.now()
});
private handleSyncData(data: any): void {
if (!data || data.type !== 'skin_result') return;
this.notifyResultListeners(data.data);
private notifyResultListeners(result: SkinAnalysisResult): void {
this.resultListeners.forEach(listener => {
listener.onSkinAnalysisResult(result);
});
public addResultListener(listener: SkinAnalysisListener): void {
if (!this.resultListeners.includes(listener)) {
this.resultListeners.push(listener);
}
public removeResultListener(listener: SkinAnalysisListener): void {
this.resultListeners = this.resultListeners.filter(l => l !== listener);
}
interface SkinAnalysisListener {
onSkinAnalysisResult(result: SkinAnalysisResult): void;
export const skinAnalysisService = SkinAnalysisService.getInstance();
相机服务封装
// CameraService.ets
import camera from ‘@ohos.multimedia.camera’;
import image from ‘@ohos.multimedia.image’;
class CameraService {
private static instance: CameraService = null;
private cameraManager: camera.CameraManager;
private cameraInput: camera.CameraInput;
private previewOutput: camera.PreviewOutput;
private captureSession: camera.CaptureSession;
private imageReceiver: image.ImageReceiver;
private constructor() {
this.initCamera();
public static getInstance(): CameraService {
if (!CameraService.instance) {
CameraService.instance = new CameraService();
return CameraService.instance;
private initCamera(): void {
try {
const context = getContext() as common.Context;
this.cameraManager = camera.getCameraManager(context);
// 获取前置摄像头
const cameras = this.cameraManager.getSupportedCameras();
const frontCamera = cameras.find(cam => cam.position === camera.CameraPosition.FRONT);
if (!frontCamera) {
throw new Error('未找到前置摄像头');
// 创建相机输入
this.cameraInput = this.cameraManager.createCameraInput(frontCamera);
// 创建预览输出
const previewProfile = this.cameraManager.getSupportedOutputCapability(frontCamera)
.previewProfiles[0];
this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile);
// 创建图像接收器
this.imageReceiver = image.createImageReceiver(
224, 224,
image.ImageFormat.RGB_888,
);
// 创建捕获会话
this.captureSession = this.cameraManager.createCaptureSession();
this.captureSession.beginConfig();
this.captureSession.addInput(this.cameraInput);
this.captureSession.addOutput(this.previewOutput);
this.captureSession.commitConfig();
this.captureSession.start();
catch (err) {
console.error('初始化相机失败:', JSON.stringify(err));
}
public async captureFrame(): Promise<ArrayBuffer> {
try {
const image = await this.imageReceiver.readNextImage();
const buffer = await image.getComponent(image.ComponentType.RGB);
image.release();
return buffer.byteArray;
catch (err) {
console.error('捕获帧失败:', JSON.stringify(err));
throw err;
}
public startPreview(surfaceId: string): void {
this.previewOutput.start(surfaceId);
public stopPreview(): void {
this.previewOutput.stop();
public release(): void {
this.captureSession.stop();
this.cameraInput.release();
this.previewOutput.release();
this.captureSession.release();
}
export const cameraService = CameraService.getInstance();
主界面实现
// MainScreen.ets
import { skinAnalysisService } from ‘./SkinAnalysisService’;
import { cameraService } from ‘./CameraService’;
import { SkinAnalysisResult, SkinCareAdvice } from ‘./SkinTypes’;
@Component
export struct MainScreen {
@State hasPermission: boolean = false;
@State isAnalyzing: boolean = false;
@State currentResult: SkinAnalysisResult | null = null;
@State previewSurfaceId: string = ‘’;
@State showHistory: boolean = false;
@State analysisHistory: SkinAnalysisResult[] = [];
build() {
Column() {
// 标题栏
Row() {
Text(‘皮肤状态检测’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Button(this.hasPermission ? '历史记录' : '授权')
.width(100)
.onClick(() => {
if (this.hasPermission) {
this.showHistory = !this.showHistory;
else {
this.requestPermissions();
})
.padding(10)
.width('100%')
// 历史记录面板
if (this.showHistory) {
Column() {
Text('历史检测记录')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
if (this.analysisHistory.length === 0) {
Text('暂无历史记录')
.fontSize(16)
.fontColor('#666666')
.margin({ top: 20 })
else {
List({ space: 10 }) {
ForEach(this.analysisHistory, (item) => {
ListItem() {
Column() {
Row() {
Text(new Date(item.timestamp).toLocaleDateString())
.fontSize(16)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Text(水分: ${item.moistureLevel.toFixed(1)})
.fontSize(14)
.fontColor('#409EFF')
.margin({ right: 10 })
.margin({ bottom: 5 })
Row() {
Progress({
value: item.moistureLevel,
total: 100,
style: ProgressStyle.Linear
})
.width('70%')
.height(10)
}
.padding(10)
.width('100%')
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E0E0E0' })
.borderRadius(8)
.onClick(() => {
this.currentResult = item;
this.showHistory = false;
})
})
.height(300)
}
.padding(15)
.width('90%')
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E0E0E0' })
.borderRadius(8)
.margin({ bottom: 20 })
// 相机预览区域
if (!this.showHistory) {
Stack() {
if (this.hasPermission) {
// 相机预览Surface
Surface({
id: this.previewSurfaceId,
type: SurfaceType.SURFACE_TEXTURE,
width: '100%',
height: 300
})
.onAppear(() => {
this.previewSurfaceId = preview_${Date.now()};
cameraService.startPreview(this.previewSurfaceId);
})
.onDisappear(() => {
cameraService.stopPreview();
})
// 分析按钮
if (!this.isAnalyzing) {
Button('开始检测')
.width(150)
.height(50)
.fontSize(20)
.backgroundColor('#409EFF')
.position({ x: '50%', y: '80%' })
.margin({ left: -75 })
.onClick(() => {
this.startAnalysis();
})
else {
Column() {
Progress({})
.width(50)
.height(50)
Text('分析中...')
.fontSize(16)
.margin({ top: 10 })
.width(‘100%’)
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
} else {
Column() {
Text('需要权限')
.fontSize(18)
.margin({ bottom: 10 })
Text('请点击右上角"授权"按钮,允许应用访问相机和AI功能')
.fontSize(16)
.fontColor('#666666')
.padding(20)
.width('90%')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.margin({ top: 50 })
}
.width('100%')
.height(300)
.margin({ bottom: 20 })
// 分析结果
if (this.currentResult && !this.showHistory) {
Column() {
Text('皮肤分析结果')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 15 })
// 皮肤指标展示
Grid() {
GridItem() {
this.buildIndicator('水分', this.currentResult.moistureLevel, '#409EFF')
GridItem() {
this.buildIndicator('油分', this.currentResult.oilinessLevel, '#FF9800')
GridItem() {
this.buildIndicator('敏感度', this.currentResult.sensitivityLevel, '#F44336')
GridItem() {
this.buildIndicator('痘痘', this.currentResult.acneLevel, '#4CAF50')
GridItem() {
this.buildIndicator('色素沉着', this.currentResult.pigmentationLevel, '#9C27B0')
}
.columnsTemplate('1fr 1fr')
.rowsTemplate('1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(10)
.height(200)
.margin({ bottom: 20 })
// 护理建议
Column() {
Text('护理建议')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
ForEach(this.currentResult.advice.items, (item) => {
Row() {
Image(item.type === 'cleanser' ? $r('app.media.ic_cleanser') :
item.type === 'moisturizer' ? $r('app.media.ic_moisturizer') :
item.type === 'sunscreen' ? $r('app.media.ic_sunscreen') :
$r('app.media.ic_treatment'))
.width(24)
.height(24)
.margin({ right: 10 })
Text(item.description)
.fontSize(14)
.layoutWeight(1)
.textAlign(TextAlign.Start)
.margin({ bottom: 8 })
})
.padding(15)
.width('100%')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.width(‘100%’)
}
.width('100%')
.height('100%')
.padding(20)
.onAppear(() => {
this.checkPermissions();
skinAnalysisService.addResultListener({
onSkinAnalysisResult: (result) => {
this.handleAnalysisResult(result);
});
})
.onDisappear(() => {
skinAnalysisService.removeResultListener({
onSkinAnalysisResult: (result) => {
this.handleAnalysisResult(result);
});
cameraService.release();
})
private buildIndicator(title: string, value: number, color: string): void {
Column() {
Text(title)
.fontSize(14)
.fontColor('#666666')
Text(${value.toFixed(1)})
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(color)
.margin({ top: 5, bottom: 5 })
Progress({
value: value,
total: 100,
style: ProgressStyle.Linear
})
.width('100%')
.height(6)
.padding(10)
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E0E0E0' })
.borderRadius(8)
private async checkPermissions(): Promise<void> {
try {
const permissions = [
'ohos.permission.USE_AI',
'ohos.permission.CAMERA',
'ohos.permission.DISTRIBUTED_DATASYNC'
];
const result = await abilityAccessCtrl.verifyPermissions(
getContext(),
permissions
);
this.hasPermission = result.every(perm => perm.granted);
catch (err) {
console.error('检查权限失败:', JSON.stringify(err));
this.hasPermission = false;
}
private async requestPermissions(): Promise<void> {
this.hasPermission = await skinAnalysisService.requestPermissions();
if (!this.hasPermission) {
prompt.showToast({ message: '授权失败,无法使用皮肤检测功能' });
}
private async startAnalysis(): Promise<void> {
try {
this.isAnalyzing = true;
// 捕获当前帧
const frame = await cameraService.captureFrame();
// 分析皮肤状态
const result = await skinAnalysisService.analyzeSkin(frame);
this.currentResult = result;
this.analysisHistory = [result, ...this.analysisHistory].slice(0, 10); // 保留最近10条记录
catch (err) {
console.error('皮肤分析失败:', JSON.stringify(err));
prompt.showToast({ message: '分析失败,请重试' });
finally {
this.isAnalyzing = false;
}
private handleAnalysisResult(result: SkinAnalysisResult): void {
this.currentResult = result;
this.isAnalyzing = false;
// 添加到历史记录
this.analysisHistory = [result, ...this.analysisHistory].slice(0, 10);
}
类型定义
// SkinTypes.ets
export interface SkinAnalysisResult {
id: string;
moistureLevel: number; // 0-100
oilinessLevel: number; // 0-100
sensitivityLevel: number; // 0-100
acneLevel: number; // 0-100
pigmentationLevel: number; // 0-100
advice: SkinCareAdvice;
timestamp: number;
imageData: ArrayBuffer;
export interface SkinCareAdvice {
summary: string;
items: {
type: ‘cleanser’ ‘moisturizer’ ‘sunscreen’
‘treatment’;
description: string;
priority: number;
}[];
三、项目配置与权限
权限配置
// module.json5
“module”: {
"requestPermissions": [
“name”: “ohos.permission.USE_AI”,
"reason": "使用AI模型分析皮肤状态"
},
“name”: “ohos.permission.CAMERA”,
"reason": "拍摄皮肤照片"
},
“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,
"reason": "同步分析结果"
],
"abilities": [
“name”: “MainAbility”,
"type": "page",
"visible": true
},
“name”: “CameraAbility”,
"type": "service",
"backgroundModes": ["camera"]
]
}
四、总结与扩展
本皮肤检测系统实现了以下核心功能:
皮肤状态分析:准确检测水分、油分、敏感度等关键指标
个性化建议:根据皮肤状态生成定制护理方案
历史记录:保存多次检测结果便于追踪变化
跨设备同步:多设备间共享检测结果和护理方案
扩展方向:
皮肤趋势分析:绘制皮肤指标变化曲线
产品推荐:对接电商平台推荐适合的护肤品
环境因素分析:结合天气、湿度等环境数据优化建议
皮肤类型测试:帮助用户确定皮肤类型
AR试妆:虚拟试用推荐产品
专业咨询:连接皮肤科医生提供专业建议
通过HarmonyOS的AI能力和分布式技术,我们构建了一个智能化的皮肤健康管理系统,帮助用户科学了解皮肤状态,并获得个性化的护理方案,同时支持多设备间的数据同步和共享。
