7. 宠物特征分析
- 首先使用 App 端侧 AI 能力 对宠物图像进行宠物识别和异物过滤,完成上传图像质量判断
- 1. 宠物图像资源导入
- 调用
chooseImage
方法,支持用户从本地相册选择图片或使用相机拍照。 - 调用
resizeImage
方法,保持原图长宽比裁剪至目标尺寸,再根据文件大小限制进行质量压缩。
- 调用
- 2. 宠物图像质量检测
- 调用
petsDetectCreate
方法,初始化 AI 宠物图像质量检测实例。 - 调用
petsPictureQualityDetectForImage
方法,输入参数进行质量检测并返回结果。 - 页面销毁时调用
petsDetectDestory
方法销毁实例,避免内存泄漏。
- 调用
- 1. 宠物图像资源导入
- 若图像通过 端侧 AI 质量判定,则上传至云端分析宠物体型、毛色、表情等特征,实现进食时的精准识别
- 1. 请求云端获取文件上传签名及对应
objectKey
- 2. 图片上传与分析流程
- 将图片上传至指定服务器,完成后传递
objectKey
至特征分析接口。 - 接口返回
taskId
,通过轮询获取分析进度:- 若
analysisResult
为2
,表示分析成功。 - 若
analysisResult
为1
,表示分析失败。
- 若
- 将图片上传至指定服务器,完成后传递
- 1. 请求云端获取文件上传签名及对应
功能展示
相关代码段
export async function pictureQualityDetect(pathUrl: string) {
const result = await new Promise<{
imagePath: string;
lowQuality: boolean;
lowQualityReason: number;
}>((resolve, reject) => {
petsPictureQualityDetectForImage({
inputImagePath: pathUrl,
labelAllow: 1,
objectAreaPercent: 30,
objectFaceRotationAngle: 40,
objectFaceSideAngle: 45,
maximumPictureBrightness: 80,
minimumPictureBrightness: 0,
success: res => {
resolve(res);
},
fail: error => {
reject(error);
},
});
});
return result;
}
const handleChooseImg = async () => {
let paths = [];
try {
// paths = await chooseImage(3);
paths = await chooseImage(3, () => enter('analyzing'));
} catch (err) {
return;
}
// 先进入下一阶段
// enter('analyzing');
setState({
analyzingText: Strings.getLang('add_pet_analytics_upload_img'),
});
const controller = new AbortController();
controllerRef.current = controller;
let tempPaths: Array<{ imagePath: string; lowQuality: boolean; lowQualityReason: number }> = [];
let pathList: Array<{ imagePath: string; lowQuality: boolean; lowQualityReason: number }> = [];
if (petType === 'cat') {
try {
tempPaths = (await Promise.all(paths.map(url => pictureQualityDetect(url)))).map(d => d);
pathList = tempPaths.filter(item => !item.lowQuality) as Array<{
imagePath: string;
lowQuality: boolean;
lowQualityReason: number;
}>;
const lowQualityList = tempPaths
.filter(item => item.lowQuality)
.map(item => item.lowQualityReason) as Array<number>;
if (pathList.length === 0) {
const errorText = Array.from(new Set(lowQualityList)).map((lowQualityItem, index) => {
return `${Strings.getLang(`dsc_${lowQualityItem}`)}`;
});
setPicErrorTips(errorText.join('、'));
setTimeout(() => {
enter('failed');
}, 1000);
return;
}
} catch (error) {
enter('failed');
return;
}
}
let images: Array<{ imageDisplayUrl: string; objectKey: string }> = [];
const pathSourceList = petType === 'cat' ? pathList.map(element => element.imagePath) : paths;
try {
images = (
await Promise.all(pathSourceList.map(p => uploadImageCat(p, ANALYTICS_BIZ_TYPE)))
).map(d => ({
imageDisplayUrl: d.publicUrl,
objectKey: d.cloudKey,
}));
if (controller.signal.aborted) {
return;
}
} catch (error) {
setTimeout(() => {
enter('failed');
}, 1000);
return;
}
let idx = 0;
const tips = [
Strings.getLang('add_pet_analytics_upload_tip1'),
Strings.getLang('add_pet_analytics_upload_tip2'),
Strings.getLang('add_pet_analytics_upload_tip3'),
Strings.getLang('add_pet_analytics_upload_tip4'),
Strings.getLang('add_pet_analytics_upload_tip5'),
];
const id = setInterval(() => {
setState({
analyzingText: tips[idx++ % tips.length],
});
}, 3000);
try {
const taskId = await analyzePetFeature({
ownerId: homeId,
images,
miniAppId,
agentId: AGENT_ID,
});
const [infoRes] = await Promise.all([
loopGetAnalysisResult({
taskId,
controller,
type: AnalysType.Profile,
}),
]);
if (infoRes) {
setState({
similarShow: true,
petResInfo: infoRes,
});
} else if (infoRes) {
emitter.emit('selectProfile', infoRes);
enter('success');
} else {
// 超时
enter('failed');
}
} catch (error) {
enter('failed');
return;
} finally {
clearInterval(id);
}
};
// 上传图片
export async function uploadImage(filePath: string, bizType: UploadFileBizType) {
const fileName = parseFileName(filePath);
const signInfo = await getPetUploadSign({ bizType, fileName });
const { url, objectKey } = signInfo;
await uploadFile(url, filePath, fileName);
return { cloudKey: objectKey };
}
8. 多宠识别记录
主要展示家庭下宠物进食情况,当宠物来进食时,会识别出具体哪只宠物,并生成一条进食记录。
相关代码段
const [eatingRecords, setEatingRecords] = useState<IEatingRecord[]>([]);
const getData = async (pageNo: number, isFresh?: boolean) => {
try {
const day = dayjs();
const startOfDay = day.clone().startOf('day').valueOf();
const endOfDay = day.clone().endOf('day').valueOf();
setRefreshing(!!isFresh);
const eatParams = {
ownerId: homeId,
uuid: getDevInfo().uuid,
startTime: startOfDay,
endTime: endOfDay,
pageNo,
pageSize: 100,
};
const { pageNo: pageNumber, hasNext, data = [] } = await fetchPetEatingRecordApi(eatParams);
setRefreshing(false);
setHasNext(hasNext);
setCurrentPageNo(pageNumber);
const formattedEatingRecord = data.map(item => {
const petName = Array.isArray(item.pets)
? item.pets.map(p => {
return find(pets, { id: p.petId })?.name;
})
: Strings.getLang('pet');
return {
...item,
timeStamp: item.recordTime,
type: RECORD_DATA_TYPE.feed,
desc: Strings.formatValue('dsc_feed_eating', petName),
};
});
if (pageNo > 1) {
setEatingRecords([...eatingRecords, ...formattedEatingRecord]);
} else {
setEatingRecords(formattedEatingRecord);
}
} catch (error) {
setRefreshing(false);
console.log('fetch error: ', error);
}
};