1 /*
2 * Copyright (c) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 /*
17 * 该cpp文件基于OpenCV实现了网球检测功能。为了保证FPS的帧数,
18 * 我们设计的原则是IVE(Intelligent Video Engine)+AI CPU结合使用,即IVE不支持的算子
19 * 通过AI CPU进行计算,否则走IVE硬件加速模块进行处理。并将检测的结果通过VGS标记出来。
20 *
21 * The cpp file implements the tennis ball detection function based on OpenCV.
22 * In order to ensure the number of FPS frames,
23 * The principle of our design is the combination of IVE (Intelligent Video Engine) + AI CPU,
24 * that is, operators not supported by IVE are calculated by AI CPU, otherwise,
25 * the IVE hardware acceleration module is used for processing.
26 * And the detection results are marked by VGS.
27 */
28
29 #include <iostream>
30 #include <opencv2/core.hpp>
31 #include <opencv2/highgui.hpp>
32 #include <opencv2/imgproc.hpp>
33 #include "tennis_detect.h"
34
35 #include "sample_comm_nnie.h"
36 #include "sample_comm_ive.h"
37 #include "sample_media_ai.h"
38 #include "vgs_img.h"
39 #include "misc_util.h"
40
41 using namespace std;
42 using namespace cv;
43
44 static IVE_SRC_IMAGE_S pstSrc;
45 static IVE_DST_IMAGE_S pstDst;
46 static IVE_CSC_CTRL_S stCscCtrl;
47
IveImageParamCfg(IVE_SRC_IMAGE_S * pstSrc,IVE_DST_IMAGE_S * pstDst,VIDEO_FRAME_INFO_S * srcFrame)48 static HI_VOID IveImageParamCfg(IVE_SRC_IMAGE_S *pstSrc, IVE_DST_IMAGE_S *pstDst,
49 VIDEO_FRAME_INFO_S *srcFrame)
50 {
51 pstSrc->enType = IVE_IMAGE_TYPE_YUV420SP;
52 pstSrc->au64VirAddr[0] = srcFrame->stVFrame.u64VirAddr[0];
53 pstSrc->au64VirAddr[1] = srcFrame->stVFrame.u64VirAddr[1];
54 pstSrc->au64VirAddr[2] = srcFrame->stVFrame.u64VirAddr[2]; // 2: Image data virtual address
55
56 pstSrc->au64PhyAddr[0] = srcFrame->stVFrame.u64PhyAddr[0];
57 pstSrc->au64PhyAddr[1] = srcFrame->stVFrame.u64PhyAddr[1];
58 pstSrc->au64PhyAddr[2] = srcFrame->stVFrame.u64PhyAddr[2]; // 2: Image data physical address
59
60 pstSrc->au32Stride[0] = srcFrame->stVFrame.u32Stride[0];
61 pstSrc->au32Stride[1] = srcFrame->stVFrame.u32Stride[1];
62 pstSrc->au32Stride[2] = srcFrame->stVFrame.u32Stride[2]; // 2: Image data span
63
64 pstSrc->u32Width = srcFrame->stVFrame.u32Width;
65 pstSrc->u32Height = srcFrame->stVFrame.u32Height;
66
67 pstDst->enType = IVE_IMAGE_TYPE_U8C3_PACKAGE;
68 pstDst->u32Width = pstSrc->u32Width;
69 pstDst->u32Height = pstSrc->u32Height;
70 pstDst->au32Stride[0] = pstSrc->au32Stride[0];
71 pstDst->au32Stride[1] = 0;
72 pstDst->au32Stride[2] = 0; // 2: Image data span
73 }
74
yuvFrame2rgb(VIDEO_FRAME_INFO_S * srcFrame,IPC_IMAGE * dstImage)75 static HI_S32 yuvFrame2rgb(VIDEO_FRAME_INFO_S *srcFrame, IPC_IMAGE *dstImage)
76 {
77 IVE_HANDLE hIveHandle;
78 HI_S32 s32Ret = 0;
79 stCscCtrl.enMode = IVE_CSC_MODE_PIC_BT709_YUV2RGB; // IVE_CSC_MODE_VIDEO_BT601_YUV2RGB
80 IveImageParamCfg(&pstSrc, &pstDst, srcFrame);
81
82 s32Ret = HI_MPI_SYS_MmzAlloc_Cached(&pstDst.au64PhyAddr[0], (void **)&pstDst.au64VirAddr[0],
83 "User", HI_NULL, pstDst.u32Height*pstDst.au32Stride[0] * 3); // 3: multiple
84 if (HI_SUCCESS != s32Ret) {
85 HI_MPI_SYS_MmzFree(pstDst.au64PhyAddr[0], (void *)pstDst.au64VirAddr[0]);
86 SAMPLE_PRT("HI_MPI_SYS_MmzFree err\n");
87 return s32Ret;
88 }
89
90 s32Ret = HI_MPI_SYS_MmzFlushCache(pstDst.au64PhyAddr[0], (void *)pstDst.au64VirAddr[0],
91 pstDst.u32Height*pstDst.au32Stride[0] * 3); // 3: multiple
92 if (HI_SUCCESS != s32Ret) {
93 HI_MPI_SYS_MmzFree(pstDst.au64PhyAddr[0], (void *)pstDst.au64VirAddr[0]);
94 return s32Ret;
95 }
96 // 3: multiple
97 memset_s((void *)pstDst.au64VirAddr[0], pstDst.u32Height*pstDst.au32Stride[0] * 3,
98 0, pstDst.u32Height*pstDst.au32Stride[0] * 3); // 3: multiple
99 HI_BOOL bInstant = HI_TRUE;
100
101 s32Ret = HI_MPI_IVE_CSC(&hIveHandle, &pstSrc, &pstDst, &stCscCtrl, bInstant);
102 if (HI_SUCCESS != s32Ret) {
103 HI_MPI_SYS_MmzFree(pstDst.au64PhyAddr[0], (void *)pstDst.au64VirAddr[0]);
104 return s32Ret;
105 }
106
107 if (HI_TRUE == bInstant) {
108 HI_BOOL bFinish = HI_TRUE;
109 HI_BOOL bBlock = HI_TRUE;
110 s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
111 while (HI_ERR_IVE_QUERY_TIMEOUT == s32Ret) {
112 usleep(100); // 100: usleep time
113 s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
114 }
115 }
116 dstImage->u64PhyAddr = pstDst.au64PhyAddr[0];
117 dstImage->u64VirAddr = pstDst.au64VirAddr[0];
118 dstImage->u32Width = pstDst.u32Width;
119 dstImage->u32Height = pstDst.u32Height;
120
121 return HI_SUCCESS;
122 }
123
frame2Mat(VIDEO_FRAME_INFO_S * srcFrame,Mat & dstMat)124 static HI_S32 frame2Mat(VIDEO_FRAME_INFO_S *srcFrame, Mat &dstMat)
125 {
126 HI_U32 w = srcFrame->stVFrame.u32Width;
127 HI_U32 h = srcFrame->stVFrame.u32Height;
128 int bufLen = w * h * 3;
129 HI_U8 *srcRGB = NULL;
130 IPC_IMAGE dstImage;
131 if (yuvFrame2rgb(srcFrame, &dstImage) != HI_SUCCESS) {
132 SAMPLE_PRT("yuvFrame2rgb err\n");
133 return HI_FAILURE;
134 }
135 srcRGB = (HI_U8 *)dstImage.u64VirAddr;
136 dstMat.create(h, w, CV_8UC3);
137 memcpy_s(dstMat.data, bufLen * sizeof(HI_U8), srcRGB, bufLen * sizeof(HI_U8));
138 HI_MPI_SYS_MmzFree(dstImage.u64PhyAddr, (void *)&(dstImage.u64VirAddr));
139 return HI_SUCCESS;
140 }
141
TennisDetectLoad(uintptr_t * model)142 HI_S32 tennis_detect::TennisDetectLoad(uintptr_t* model)
143 {
144 HI_S32 ret = 1;
145 *model = 1;
146 SAMPLE_PRT("TennisDetectLoad success\n");
147
148 return ret;
149 }
150
TennisDetectUnload(uintptr_t model)151 HI_S32 tennis_detect::TennisDetectUnload(uintptr_t model)
152 {
153 model = 0;
154
155 return HI_SUCCESS;
156 }
157
158 /*
159 * 网球检测推理
160 * Tennis detect calculation
161 */
TennisDetectCal(uintptr_t model,VIDEO_FRAME_INFO_S * srcFrm,VIDEO_FRAME_INFO_S * dstFrm)162 HI_S32 tennis_detect::TennisDetectCal(uintptr_t model, VIDEO_FRAME_INFO_S *srcFrm, VIDEO_FRAME_INFO_S *dstFrm)
163 {
164 (void)model;
165 int ret = 0;
166 RectBox boxs[32] = {0}; // 32: TENNIS_OBJ_MAX
167 int j = 0;
168
169 Mat image;
170 frame2Mat(srcFrm, image);
171 if (image.size == 0) {
172 SAMPLE_PRT("image is null\n");
173 return HI_FAILURE;
174 }
175
176 Mat src = image;
177 Mat src1 = src.clone();
178 Mat dst, edge, gray, hsv;
179
180 dst.create(src1.size(), src1.type()); // Create a matrix of the same type and size as src (dst)
181
182 /*
183 * cvtColor运算符用于将图像从一个颜色空间转换到另一个颜色空间
184 * The cvtColor operator is used to convert an image from one color space to another color space
185 */
186 cvtColor(src1, hsv, COLOR_BGR2HSV); // Convert original image to HSV image
187
188 /*
189 * 二值化hsv图像,这里是对绿色背景进行二值化,
190 * 这个参数可以根据需要调整
191 *
192 * Binarize the hsv image, here is to binarize the green background,
193 * this parameter can be adjusted according to requirements
194 */
195 inRange(hsv, Scalar(31, 82, 68), Scalar(65, 248, 255), gray); // 31: B, 82: G, 68:R / 65: B, 248:G, 255:R
196
197 /*
198 * 使用canny算子进行边缘检测
199 * Use canny operator for edge detection
200 */
201 Canny(gray, gray, 3, 9, 3); // 3: threshold1, 9: threshold2, 3: apertureSize
202 vector<vector<Point>> contours;
203 findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
204 SAMPLE_PRT("contours.size():%d\n", contours.size());
205
206 for (int i = 0; i < (int)contours.size(); i++) {
207 if (contours.size() > 40) { // 40: contours.size() extremes
208 continue;
209 }
210
211 Rect ret1 = boundingRect(Mat(contours[i]));
212 ret1.x -= 5; // 5: x coordinate translation
213 ret1.y -= 5; // 5: y coordinate translation
214 ret1.width += 10; // 10: Rectangle width plus 10
215 ret1.height += 10; // 10: Rectangle height plus 10
216
217 // 20: Rectangle width and height pixel extremes
218 if ((ret1.width > 20) && (ret1.height > 20)) {
219 boxs[j].xmin = ret1.x * 3; // 3: optimized value
220 boxs[j].ymin = (int)(ret1.y * 2.25); // 2.25: optimized value
221 boxs[j].xmax = boxs[j].xmin + ret1.width * 3; // 3: optimized value
222 boxs[j].ymax = boxs[j].ymin + (int)ret1.height * 2.25; // 2.25: optimized value
223 j++;
224 }
225 }
226 // 25: detect boxesNum
227 if (j > 0 && j <= 25) {
228 SAMPLE_PRT("box num:%d\n", j);
229 MppFrmDrawRects(dstFrm, boxs, j, RGB888_RED, 2); // 2: DRAW_RETC_THICK
230 }
231
232 return ret;
233 }