import { cloneDeep, get, isNull, isNumber, isString } from 'lodash-es'; import { dataURLToBlob, generateImg, urlToImg } from '../file'; import { IOdRectOrigin, Rect } from '@zhst/types'; const proto = { Common: { AlgorithmVersion: { VERSION_REID_HEAD_ATTR: '形体', VERSION_FACE: '人脸', VERSION_NON_MOTOR_VEHICLE: '非机动车', }, }, }; export const ALGORITHM_VERSION = { ['7']: '形体', ['4']: '人脸', ['6']: '非机动车', }; export const algorithmVersions = [...Object.keys(ALGORITHM_VERSION)] export const getBikeExtendRect = (rect: Rect, maxW: number) => { const newRect = { ...rect }; //向上扩大一倍 const oldY = cloneDeep(rect.y); newRect.y = newRect.y - newRect.h < 0 ? 0 : newRect.y - newRect.h; newRect.h += oldY - newRect.y; let newX = Math.round(newRect.x - newRect.w * 0.15); if (newX < 0) { newX = 0; } let newW = newRect.x - newX + newRect.w + Math.round(newRect.w * 0.15); if (newX + newW > maxW) { newW = maxW - newX; } newRect.x = newX; newRect.w = newW; return newRect; }; export const getOtherExtendRect = (srcRect, maxW, maxH, type) => { const wExtendRadio = 0.25; const upExtendRadio = 0.25; const downExtendRadio = 0.25; const fixPersonExtend = true; let nx = 0; let nw = 0; nx = srcRect.x - Math.round(srcRect.w * wExtendRadio); if (nx < 0) { nx = 0; } nw = srcRect.x - nx + srcRect.w + Math.round(srcRect.w * wExtendRadio); if (nx + nw > maxW) { nw = maxW - nx; } let ny = 0; let nh = 0; ny = srcRect.y - Math.round(upExtendRadio * srcRect.h); if (ny < 0) { ny = 0; } nh = srcRect.y - ny + srcRect.h + Math.round(srcRect.h * downExtendRadio); if (ny + nh > maxH) { nh = maxH - ny; } let newRect = { x: nx, y: ny, w: nw, h: nh, }; if ( (type === proto.Common.AlgorithmVersion.VERSION_REID_HEAD_ATTR || type === proto.Common.AlgorithmVersion.VERSION_FACE) && fixPersonExtend ) { const fixW = Math.round(nh * 0.75); if (nw < fixW) { // 应该扩展宽度 let newX = nx + Math.round(nw / 2.0 - 0.5 * fixW); if (newX < 0) { newX = 0; } let newW = fixW; if (newW + newX > maxW) { newW = maxW - newX; } newRect = { x: newX, y: ny, w: newW, h: nh, }; } else if (nw > fixW) { // 应该扩展高度 const fixH = Math.round(nw * 1.333); let newY = ny + Math.round(nh / 2.0 - 0.5 * fixH); if (newY < 0) { newY = 0; } let newH = fixH; if (newY + newH > maxH) { newH = maxH - newY; } newRect = { x: nx, y: newY, w: nw, h: newH, }; } } return newRect; }; export const getNormalization = (srcRect: Rect, maxW: number, maxH: number) => { const newRect = { ...srcRect, }; newRect.x = srcRect.x / maxW; newRect.y = srcRect.y / maxH; newRect.w = srcRect.w / maxW; newRect.h = srcRect.h / maxH; if (newRect.x + newRect.w > 1) { newRect.w = 1 - newRect.x; } if (newRect.y + newRect.h > 1) { newRect.h = 1 - newRect.y; } return newRect; }; //传入od框 穿出 od扩展框 export const getExtendRect = (normalizationRect: Rect, imgW: number, imgH: number, type: string) => { const rect = { x: normalizationRect.x * imgW, y: normalizationRect.y * imgH, w: normalizationRect.w * imgW, h: normalizationRect.h * imgH, }; let newRect; if (type === proto.Common.AlgorithmVersion.VERSION_NON_MOTOR_VEHICLE) { newRect = getBikeExtendRect(rect, imgW); } else { newRect = getOtherExtendRect(rect, imgW, imgH, type); } newRect = getNormalization(newRect, imgW, imgH); return newRect; }; export const getTransformRect = (image: { height: number; width: number; }, transform: { translateX: any; translateY: any; scale: any; rotate: any; }, rect: Rect) => { const canvasRect = { x: rect.x, y: rect.y, x2: rect.x + rect.w, y2: rect.h + rect.y, }; //1.转成缩放前的坐标 const { translateX, translateY, scale, rotate } = transform; const originAxisRect = { x: (canvasRect.x - translateX) / scale, y: (canvasRect.y - translateY) / scale, x2: (canvasRect.x2 - translateX) / scale, y2: (canvasRect.y2 - translateY) / scale, }; //2.转成图片坐标 //不考虑旋转 图片原点就是坐标原点 let imgAxisRect = originAxisRect; //但是旋转90度后图片不在坐标原点 加上这部分diff if (rotate % 180 !== 0) { //90度调整偏移量 const offsetX = -(image.height - image.width) / 2; const offsetY = -(image.width - image.height) / 2; imgAxisRect = { x: originAxisRect.x - offsetX, y: originAxisRect.y - offsetY, x2: originAxisRect.x2 - offsetX, y2: originAxisRect.y2 - offsetY, }; } //3.限制框不要超出图片 let imgW = image.width; let imgH = image.height; if (rotate % 180 !== 0) { [imgW, imgH] = [imgH, imgW]; } imgAxisRect.x = Math.min(imgW, Math.max(imgAxisRect.x, 0)); imgAxisRect.y = Math.min(imgH, Math.max(imgAxisRect.y, 0)); imgAxisRect.x2 = Math.min(imgW, Math.max(imgAxisRect.x2, 0)); imgAxisRect.y2 = Math.min(imgH, Math.max(imgAxisRect.y2, 0)); //获取归一化坐标 const endRect = { x: imgAxisRect.x2 > imgAxisRect.x ? imgAxisRect.x : imgAxisRect.x2, y: imgAxisRect.y2 > imgAxisRect.y ? imgAxisRect.y : imgAxisRect.y2, w: Math.abs(imgAxisRect.x2 - imgAxisRect.x), h: Math.abs(imgAxisRect.y2 - imgAxisRect.y), }; return getNormalization(endRect, imgW, imgH); }; //旋转图片后转成file 对象 export const getRotateImg = (image: HTMLImageElement, rotate: number) => { let imgW = image.width; let imgH = image.height; if (rotate % 180 !== 0) { [imgW, imgH] = [imgH, imgW]; } const commonCanvas = document.createElement('canvas'); commonCanvas.width = imgW; commonCanvas.height = imgH; commonCanvas.style.display = 'none'; document.body.appendChild(commonCanvas); const commonCtx = commonCanvas.getContext('2d'); //移动到图片中心 旋转 commonCtx?.save(); if (rotate % 180 !== 0) { //90度调整偏移量 commonCtx?.translate((image.height - image.width) / 2, (image.width - image.height) / 2); } commonCtx?.translate(image.width / 2, image.height / 2); commonCtx?.rotate((rotate / 180) * Math.PI); commonCtx?.translate(-image.width / 2, -image.height / 2); commonCtx?.drawImage(image, 0, 0); commonCtx?.restore(); const dataUrl = commonCanvas.toDataURL('image/jpeg'); const blobData = dataURLToBlob(dataUrl); const file = new window.File([blobData], `${new Date().getTime()}`, { type: 'image/jpeg', }); commonCanvas.parentNode?.removeChild(commonCanvas); return file; }; /** * 格式化工具 * @param originData * @returns */ export const getOdRect = (originData: IOdRectOrigin) => { let data = get(originData, 'objects', []) .filter((v: any) => !isNull(get(v, 'infoOnSource.bboxInFrame.bboxRatio'))) .map((v: any, index: any) => { // objectId==0 特征没有提取到过滤1掉 const rect = get(v, 'infoOnSource.bboxInFrame.bboxRatio'); const extendBox = get(v, 'infoOnSource.bboxInFrame.extendBoxRatio'); const frameTimestamp = get(v, 'timestamp'); //时间戳创建档案的时候需要 const qualityScore = get(v, 'qualityScore'); const algorithmVersion = get(v, 'objectType') === 'OBJECT_TYPE_PEDESTRAIN' ? 'VERSION_REID_HEAD_ATTR' : get(v, 'objectType') === 'OBJECT_TYPE_FACE' ? 'VERSION_FACE' : 'VERSION_REID_HEAD_ATTR'; const featureData = get(v, 'feature', []).filter( (v: any) => v.type === 'FEATURE_TYPE_BYTE' ); const objectRectIndex = algorithmVersion === 'VERSION_FACE' ? 0 : 1; const objectType = get(v, 'objectType'); const objectId = get(v, 'objectIndex.objectId'); const sourceObjectId = get(v, 'sourceObjectId'); return { x: rect.x, y: rect.y, w: rect.w, h: rect.h, // faceCorrectImage: faceCorrectImage, id: index, qualityScore: qualityScore, algorithmVersion: algorithmVersion, featureData: get(featureData, '0.featureByte'), objectRectIndex: objectRectIndex, objectType: objectType, objectId: objectId, frameTimestamp: frameTimestamp, sourceObjectId: sourceObjectId, extendBox: extendBox, }; }); if (data.length > 0) { data = data.filter((v: { objectId: string; }) => v.objectId !== '0'); } else { throw new Error('empty'); } return data; }; //档案库od export const getOdRectV2 = (originData: { odv2Result: any[]; }) => { // const fileKey = 'v1_' + window.btoa('public_' + imgKey.split('public/')[1]); const resp = originData.odv2Result[0]; const subObjects: { x: any; y: any; w: any; h: any; id: any; qualityScore: any; algorithmVersion: any; featrueData: any; objectRectIndex: number; objectType: any; objectId: any; }[] = []; //形体 const data = get(resp, 'objects', []) .filter((v) => !isNull(get(v, 'subObjects[0].infoOnSource.bboxInFrame.bboxRatio'))) .map((v, index) => { const rect = get(v, 'infoOnSource.bboxInFrame.bboxRatio'); const qualityScore = get(v, 'qualityScore'); const algorithmVersion = get(v, 'objectType'); const featrueData = get(v, 'feature', []).filter( (v) => v.name === 'feature-body' || v.name === 'feature-face' ); const objectRectIndex = algorithmVersion === 'OBJECT_TYPE_FACE' ? 0 : 1; const objectType = get(v, 'objectType'); const objectId = get(v, 'objectIndex.objectId'); //如果存在subObjects的数组不为null表示形体里面带人脸,人脸的od框也要显示出来 if (get(v, 'subObjects', []).length) { get(v, 'subObjects', []).forEach((e) => { const rect = get(e, 'infoOnSource.bboxInFrame.bboxRatio'); const qualityScore = get(e, 'qualityScore'); const algorithmVersion = get(e, 'objectType'); const featrueData = get(e, 'feature', []).filter( (v: { name: string; }) => v.name === 'feature-body' || v.name === 'feature-face' ); const objectRectIndex = algorithmVersion === 'OBJECT_TYPE_FACE' ? 0 : 1; const objectType = get(e, 'objectType'); const objectId = get(e, 'objectIndex.objectId'); subObjects.push({ x: rect.x, y: rect.y, w: rect.w, h: rect.h, id: index, qualityScore: qualityScore, algorithmVersion: algorithmVersion, featrueData: featrueData.length ? featrueData[0].featureByte : '', objectRectIndex: objectRectIndex, objectType: objectType, objectId, }); }); } return { x: rect.x, y: rect.y, w: rect.w, h: rect.h, id: index, qualityScore: qualityScore, algorithmVersion: algorithmVersion, featrueData: featrueData[0].featureByte, objectRectIndex: objectRectIndex, objectType: objectType, objectId: objectId, }; }); const brr = data.concat(subObjects).map((v: { id: any; }, vs: any) => { if (String(v.id)) { v.id = vs; } return v; }); if (brr.length > 0) { console.log(brr, 'data111'); } else { throw new Error('empty'); } console.log(brr); return brr; }; //获取图片 export const getFileByRect = async (img: any, odRect: Rect) => { let image; if (isString(img)) { const url = generateImg(img); console.log(url, '获取图片'); image = await urlToImg(url); console.log(image, '获取的图片'); } else { image = img; } const commonCanvas = document.createElement('canvas'); commonCanvas.width = odRect.w * image.width; commonCanvas.height = odRect.h * image.height; commonCanvas.style.display = 'none'; document.body.appendChild(commonCanvas); const commonCtx = commonCanvas.getContext('2d'); commonCtx?.translate(-odRect.x * image.width, -odRect.y * image.height); commonCtx?.drawImage(image, 0, 0); const base64 = commonCanvas.toDataURL('image/jpeg'); const blobData = dataURLToBlob(base64); commonCanvas?.parentNode?.removeChild(commonCanvas); const file = new window.File([blobData], `${new Date().getTime()}`, { type: 'image/jpeg', }); return file; }; /** * Get transforms base on the given object. * @param {Object} obj - The target object. * @returns {string} A string contains transform values. */ export function getTransforms({ rotate, scaleX, scaleY, translateX, translateY, }: { rotate?: number; scaleX?: number; scaleY?: number; translateX?: number; translateY?: number; }) { const values = []; if (isNumber(translateX) && translateX !== 0) { values.push(`translateX(${translateX}px)`); } if (isNumber(translateY) && translateY !== 0) { values.push(`translateY(${translateY}px)`); } // Rotate should come first before scale to match orientation transform if (isNumber(rotate) && rotate !== 0) { values.push(`rotate(${rotate}deg)`); } if (isNumber(scaleX) && scaleX !== 1) { values.push(`scaleX(${scaleX})`); } if (isNumber(scaleY) && scaleY !== 1) { values.push(`scaleY(${scaleY})`); } const transform = values.length ? values.join(' ') : 'none'; return { WebkitTransform: transform, msTransform: transform, transform, }; }