🐱前言
今日突发奇想给自家小猪咪做一个品种血统鉴定小工具看看猪咪继承了哪些血统,毕竟小猪咪到家这么久了都不知道他继承了那只猪咪的基因~~
🤔️技术选择
目前调研了多个图片识别,感觉只有百度的做的最好,所以我们将使用百度的图像识别对小猪咪进行血统鉴定
具体费用如下:
💻百度控制台创建应用
首先创建一个应用,由于我们只使用了动物识别功能所以只需要选择动物识别即可
创建完毕后我们就拥有调用api的接口的key了
🙏调试
之后有需要的话,我们可以在控制台中对接口进行在线调试
✍️代码编写
接下来我们开始编写api接口的代码,用于前端的调用
接口说明
百度接口api限制图片最大只能是4m,所以我们在调用接口的时候需要先对图片进行压缩处理到4M以内,所以我们直接使用下面的SDK即可。
对于SDK使用的话就是直接把图片转为base64然后再调用SDK即可
使用Sharp压缩图片
由于我们使用的是 Nestjs ,所以相对来说简单一点,安装sharp ,推荐使用pnpm
安装避免版本出错
pnpm install sharp
具体接口逻辑代码如下:
js
import { Injectable } from '@nestjs/common';
import { InjectEntityManager } from '@nestjs/typeorm';
import { EntityManager } from 'typeorm';
import { IdentificationDto } from './dto/identification.dto';
import { imageClassify } from 'baidu-aip-sdk';
import fetch from 'node-fetch';
import { Identification } from './entities/identification.entity';
import * as dayjs from 'dayjs';
import { GetIdentificationDto } from './dto/getIdentification.dto';
import * as sharp from 'sharp';
@Injectable()
export class AnimalService {
constructor() {
// 设置APPID/AK/SK
const APP_ID = 'xxxxxxxx';
const API_KEY = 'xxxxxxxx';
const SECRET_KEY = 'xxxxxxxx';
// 新建一个对象,建议只保存一个对象调用服务接口
this.client = new imageClassify(APP_ID, API_KEY, SECRET_KEY);
}
client: any;
@InjectEntityManager()
private entityManager: EntityManager;
// 图片转buffer
private async getImageBuffer(url: string): Promise<Buffer> {
const response = await fetch(url);
const buffer = Buffer.from(await response.arrayBuffer());
return buffer;
}
// 压缩图片
private async resizeImageIfNeeded(
buffer: Buffer,
maxSize: number,
): Promise<Buffer> {
const originalSize = buffer.length;
// 小于4m
if (originalSize <= maxSize) {
return buffer;
}
// 压缩图片
const compressedImageBuffer = await sharp(buffer)
.resize({
// 图片宽
width: 500,
// 图片
height: 500,
// 保留纵横比,将图像大小调整为尽可能大,同时确保其尺寸小于或等于指定的尺寸
fit: 'inside',
// 不放大
withoutEnlargement: true,
})
.toBuffer();
return compressedImageBuffer;
}
// url转base64
async urlToBase64(url: string): Promise<string> {
const imageBuffer = await this.getImageBuffer(url);
// 是否需要压缩
const resizedBuffer = await this.resizeImageIfNeeded(imageBuffer, 2 * 1024);
const base64String = resizedBuffer.toString('base64');
return base64String;
}
// 动物识别
async identification(data: IdentificationDto, userId: number) {
// url转base64
const image = await this.urlToBase64(data.url);
// 如果有可选参数
const options = {};
// 返回预测得分top结果数,默认为6,相当于返回多少条结果
options['top_num'] = '4';
// 用于控制返回结果是否带有百科信息,若不输入此参数,则默认不返回百科结果;若输入此参数,会根据输入的整数返回相应个数的百科信息
options['baike_num'] = '1';
// 带参数调用动物识别
try {
const res = await this.client.animalDetect(image, options);
if (!res.result) {
return {
code: -1,
msg: '上传照片非动物',
};
}
const result = res.result[0];
if (result.name === '非动物') {
return {
code: -1,
msg: '上传照片非动物',
};
}
return {
data: {
list: res.result,
},
};
} catch (error) {
throw error;
}
}
接口入参代码如下:
js
import { WINSTON_LOGGER_TOKEN } from 'src/winston/winston.module';
import {
Controller,
HttpStatus,
HttpCode,
HttpException,
Inject,
UseInterceptors,
Post,
Body,
UseGuards,
Headers,
} from '@nestjs/common';
import { InvokeRecordInterceptor } from 'src/invoke-record.interceptor';
import { AnimalService } from './animal.service';
import { IdentificationDto } from './dto/identification.dto';
import { LoginGuard } from 'src/login.guard';
import { RedisService } from '../redis/redis.service';
@Controller('animal')
@UseInterceptors(InvokeRecordInterceptor)
export class AnimalController {
constructor(private readonly animalService: AnimalService) {}
@Inject(WINSTON_LOGGER_TOKEN)
private logger;
@Inject(RedisService)
private redisService: RedisService;
// 动物识别
@Post('identification')
@UseGuards(LoginGuard)
@HttpCode(HttpStatus.OK)
async identification(
@Body() data: IdentificationDto,
@Headers() headers: Record<string, string>,
) {
try {
const token = headers.authorization.split(' ')[1];
const redisToken = await this.redisService.get(token);
const _redisToken = JSON.parse(redisToken);
const res = await this.animalService.identification(data, _redisToken.id);
return {
data: {},
...res,
};
} catch (e) {
this.logger.error(e, '动物识别错误');
throw new HttpException(`服务器错误`, HttpStatus.BAD_REQUEST);
}
}
}
以上代码写完后就可以到前端进行接入了
✍️前端接入
前端直传到腾讯云cos,然后把url传入上面写的Nestjs接口
js
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
camera: 'back',
}).then(async ({ tempFiles }) => {
const file = tempFiles[0];
loading.value = true;
// uploadFile
await uploadFile(
{
url: file.tempFilePath,
type: 'animal',
noMediaCheck: true,
},
async (url) => {
const res = await identification({
url: url,
});
if (res.code === 0) {
identificationStore.setIdentification({
cover: url,
...res.data,
});
}
loading.value = false;
},
() => {
loading.value = false;
},
);
});
👀效果展示
以上代码写完后,最终的代码效果显示如下:
🧐预览地址
掘友们可扫描一下二维码可进行体验,进入小程序后点击小工具即可使用~~