当前位置: 首页 > news >正文

门户网站建设系统戚墅堰网站建设

门户网站建设系统,戚墅堰网站建设,莱芜信息平台,天津网络推广培训大家好#xff0c;本文转自我一个读者朋友Homio的文章#xff0c;推荐给大家#xff0c;希望对做这方便的同学有所帮助。程序员#xff0c;哦#xff01;不#xff01;软件工程师们都对opencv很熟悉#xff0c;它在工作学习研究中起到了不可或缺的作用。但是它臃肿的身躯… 大家好本文转自我一个读者朋友Homio的文章推荐给大家希望对做这方便的同学有所帮助。程序员哦不软件工程师们都对opencv很熟悉它在工作学习研究中起到了不可或缺的作用。但是它臃肿的身躯和迟缓的动作让人苦不堪言。opencv初体验今天的主角叫图像去畸变算法。先隆重请它出场图像去畸变效果为了更好的成像质量引入的透镜导致了图像的畸变无论是在摄影还是在AI图像识别方面都有还原事物真实面目的需求。那么我们首先体验一下在opencv中去畸变的快感吧。偷偷说一下上面那张图是在ps中去畸变的opencv中去畸变有两种形式一种直接调用cv::undistort()函数函数参数如下param src Input (distorted) image. param dst Output (corrected) image that has the same size and type as src . param cameraMatrix Input camera matrix \f$A \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . param distCoeffs Input vector of distortion coefficients \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are assumed. param newCameraMatrix Camera matrix of the distorted image. By default, it is the same as cameraMatrix but you may additionally scale and shift the result by using a different matrix.*/ CV_EXPORTS_W void undistort( InputArray src, OutputArray dst,InputArray cameraMatrix,InputArray distCoeffs,InputArray newCameraMatrix noArray() );此法对每帧图像去畸变都要计算复杂的公式耗时非常多因此在实时视频处理方面没人用它。另一种方式就是调用cv::initUndistortRectifyMap()和cv::remap()。该方法是先根据相机内参获取到两个映射表相机内参是用张正友标定法获取的这里就不解析了再用remap()函数找到非畸变图像对映畸变图像的像素直接把这个像素拷贝过来就了。因此去畸变的过程就变成了查表的过程复杂的运算只需要做一次就够了。那么我们测试的代码如下int getInternalParm(const char *datFileName, Mat mapx, Mat mapy) {Size imageSize;Matx33d intrinsic_matrix;Vec4d distortion_coeffs;mapx Mat(imageSize, CV_32FC1);mapy Mat(imageSize, CV_32FC1);FILE *camParam fopen(datFileName, rb);if (NULL camParam){std::cout fopen fail: datFileName std::endl;return -1;}fread(intrinsic_matrix, sizeof(cv::Matx33d), 1, camParam);fread(distortion_coeffs, sizeof(cv::Vec4d), 1, camParam);fread(imageSize, sizeof(Size), 1, camParam);fclose(camParam);Mat R Mat::eye(3, 3, CV_32F);fisheye::initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, R, intrinsic_matrix, imageSize, CV_32FC1, mapx, mapy);return 0; }int main(int argc, char *argv[]) {Mat mapx;Mat mapy;if (-1 getInternalParm(camParam.dat, mapx, mapy)){printf(getInternalParm error!\n);return 0;}Mat frame_bgr  imread(fisheys_modle.jpg);Mat image_undistort;remap(frame_bgr, image_undistort, mapx, mapy, INTER_NEAREST); //去畸变return 0; }庖丁解牛要想把opencv扔进垃圾桶光完成上面的代码是没有任何用的你完全不知道去畸变是怎么回事只能看着opencv跑出来的图像大呼好牛逼啊。然并卵。径向畸变                                    切向畸变引起图像畸变的原因有两个一种是由于透镜形状引起的畸变称之为径向畸变可以看到镜像畸变基本上都是沿着半径方向分布。另一种是由于安装导致透镜和成像平面不平行导致的畸变称之为切向畸变。先来了解下相机的内参矩阵其中fx f/dx f为相机焦距单位mmdx为像素x方向宽度单位mm1/dx表示x方向上1mm内有多少像素。fy同理。cxu0表示图像中心在像素坐标系下的位置。cy同理。四个重要的坐标系    在去畸变算法中有四个重要的坐标系世界坐标系、相机坐标系、成像坐标系、像素坐标系。世界坐标系-成像坐标系完整的坐标变换过程真实世界上的一点M 经过旋转平移后达到成像平面上其坐标我们用小写的x, y, z表示。然后除以z坐标映射到归一化坐标系最后再经过内参矩阵A转换到像素坐标系坐标用uv表示。其中s表示可以对图像进行缩放我们直接不缩放令其为1即可。由于我们不关心世界坐标系的点直接将上述公式1-1归一化后代入公式1-2可得其中x/z我们称之为归一化坐标x y/z为归一化坐标y 。那么将上式展开可得归一化坐标系和像素坐标系的转换关系至此我们完成了图像坐标系的转换此后去畸变的过程就是在归一化坐标系下完成的。比如我们拿到一张畸变图像可以依次取出像素点转换到归一化坐标系下。归一化坐标系下有公式对应畸变图像和非畸变图像的坐标那么就能获取到非畸变图像在归一化坐标系下的坐标再根据式1-4求得非畸变图像在像素坐标系下的坐标。那么归一化坐标系下畸变图像和非畸变图像是如何对应的呢学者们给出了两种畸变模型针孔模型鱼眼模型其中针孔模型适用于畸变比较小的镜头鱼眼模型适用于畸变比较大的镜头。上式中k和p参数为径向畸变系数和切向畸变系数在标定时已经获取。只有畸变坐标和非畸变坐标未知。因此可以根据一方求得另一方。求解过程列解如下针孔模型鱼眼模型九层垒土那么我们将上述去畸变的过程自己用C语言写一下吧#include sys/time.h #include string #include math.h#define DST_WIDTH 640 #define DST_HEIGHT 480 #define SRC_VER_DEV 0 /** the source image deviation in vertical*/ #define SRC_UPR_DEV 0 /** the source image deviation in upright*/ #define SRC_WIDTH 640 #define SRC_HEIGHT 480typedef struct distort_coor_t {uint32_t u_distorted;uint32_t v_distorted; }distort_coor;void NV12_to_YUV444packed(uint8_t *src_date, uint8_t *dst_data, int width, int height) {uint8_t *src_y  src_date;uint8_t *src_uv src_date width * height;uint32_t tmp 0;int k 0;for (int i 0; i height; i){for (int j 0; j width; j){dst_data[k] src_y[i * width j];tmp 2 * ((i / 2) * (width / 2) (j / 2));dst_data[k] src_uv[tmp];dst_data[k] src_uv[tmp 1];}}return; }void YUV444packed_to_NV12(uint8_t *src_data, uint8_t *dst_data, int width, int height) {uint8_t *dst_y  dst_data;uint8_t *dst_u dst_data width * height;uint32_t tmp 0;int k 0;for (int i 0; i height; i){for (int j 0; j width; j){dst_y[i * width j] src_data[k];tmp 2 * ((i / 2) * (width / 2) (j / 2));dst_u[tmp] src_data[k];dst_u[tmp 1] src_data[k];}}return; }/************************************ function: get the mapping table between the undistorted picture and distorted picture* input: *cam_param: *the intrinsic param and distortion coeffs file of camera. (using opencv data file)* mapping_table[DST_HEIGHT][DST_WIDTH]: the mapping table* width: the destination picture width* height: the destination picture height* type: the model of distortion 1: pinhole, 2: fisheye* return: 0:success, -1 fail. * ********************************/ int get_mapping_table(const char *cam_param, distort_coor mapping_table[DST_HEIGHT][DST_WIDTH], const uint16_t width, const uint16_t height, uint8_t mode_type) {/** read the intrinsic_matrix and distortion_coeffs of camera */FILE *fp fopen(cam_param, rb);if (NULL fp){printf(fopen fail: %s, errno%d\n, cam_param, errno);return -1;}/** 畸变参数(k1, k2为径向畸变系数p1, p2为切向畸变系数) */double k1 0.0, k2 0.0, p1 0.0, p2 0.0;/** 内参 fx, fy是以像素为单位的焦距cx, cy是基准点(通常在图像的中心)*/double fx 0.0, fy 0.0, cx 0.0, cy 0.0;fread(fx, sizeof(double), 1, fp);fseek(fp, sizeof(double), SEEK_CUR);fread(cx, sizeof(double), 1, fp);fseek(fp, sizeof(double), SEEK_CUR);fread(fy, sizeof(double), 1, fp);fread(cy, sizeof(double), 1, fp);fseek(fp, 3 * sizeof(double), SEEK_CUR);fread(k1, sizeof(double), 1, fp);fread(k2, sizeof(double), 1, fp);fread(p1, sizeof(double), 1, fp);fread(p2, sizeof(double), 1, fp);fclose(fp);int rows height, cols width;/** 计算去畸变后图像的内容(v, u为非畸变图像的高和宽) */for (int v 0; v rows; v){for (int u 0; u cols; u){uint32_t u_distorted 0, v_distorted 0; /** 畸变后的图像坐标 */float x1, y1, x2, y2; /** 其中x1, y1为正常图像在归一化坐标系的坐标x2, y2是畸变图像在归一化坐标系的坐标 */if (1 mode_type) /** 针孔畸变模型 */{/** 将image_undistort的坐标通过内参转换到归一化坐标系下此时得到的归一化坐标是对的 */x1 (u - cx) / fx;y1 (v - cy) / fy;/** 根据畸变模型求得在归一化坐标系下畸变图像的坐标 */float r2;r2 pow(x1, 2) pow(y1, 2);x2 x1 * (1 k1 * r2 k2 * pow(r2, 2)) 2 * p1 * x1 * y1 p2 * (r2 2 * x1 * x1);y2 y1 * (1 k1 * r2 k2 * pow(r2, 2)) p1 * (r2 2 * y1 * y1) 2 * p2 * x1 * y1;/** 求得畸变图像在像素坐标系下的坐标 */u_distorted (uint32_t)fx * x2 cx;v_distorted (uint32_t)fy * y2 cy;}else /** 鱼眼畸变模型 (圆形鱼眼图像) */{/** 将image_undistort的坐标通过内参转换到归一化坐标系下此时得到的是正常图像的归一化坐标 */x1 (u - (cx SRC_VER_DEV)) / fx;y1 (v - (cy SRC_UPR_DEV)) / fy;/** 根据畸变模型求得在归一化坐标系下畸变图像的坐标 */float r2;float r;float theta;float theta1;r2 pow(x1, 2) pow(y1, 2);r sqrt(r2);theta atan(r);theta1 theta * (1 k1 * pow(theta, 2) k2 * pow(theta, 4) p1 * pow(theta, 6) p2 * pow(theta, 8));x2 theta1 * x1 / r;y2 theta1 * y1 / r;/** 求得畸变图像在像素坐标系下的坐标 */u_distorted (uint32_t)fx * x2 cx;v_distorted (uint32_t)fy * y2 cy;} mapping_table[v][u].u_distorted u_distorted;mapping_table[v][u].v_distorted v_distorted;}}return 0; }void undistort_img(uint8_t *yuv_raw, uint8_t *yuv_undistort, const distort_coor mapping_table[DST_HEIGHT][DST_WIDTH], uint16_t width, uint16_t height) {uint32_t tmp2  0;for (int i  0; i  height; i){for (int j 0; j width; j){tmp2 mapping_table[i][j].v_distorted * SRC_WIDTH * 3 (int)mapping_table[i][j].u_distorted * 3;memcpy(yuv_undistort, yuv_raw tmp2, 3);yuv_undistort 3;}}return; }int main(int argc, char *argv[]) {static distort_coor mapping_table[DST_HEIGHT][DST_WIDTH] {0};uint8_t *yuvImg_NV12  (uint8_t *)calloc(rows * cols * 3, sizeof(uint8_t));uint8_t *yuvImg_444packed (uint8_t *)calloc(rows * cols * 3, sizeof(uint8_t));uint8_t *yuvImg_undistort (uint8_t *)calloc(DST_WIDTH * DST_HEIGHT * 3, sizeof(uint8_t));uint8_t *yuvImg_undistort_nv12  (uint8_t *)calloc(DST_WIDTH * DST_HEIGHT * 3, sizeof(uint8_t));if (-1 get_mapping_table(camParam.dat, mapping_table, DST_WIDTH, DST_HEIGHT, 2)){printf(get_mapping_table error!\n);return 0;}for (int i  0; i  1000; i){NV12_to_YUV444packed(yuvImg_NV12, yuvImg_444packed, DST_WIDTH, DST_HEIGHT);undistort_img(yuvImg_444packed, yuvImg_undistort, mapping_table, DST_WIDTH, DST_HEIGHT);YUV444packed_to_NV12(yuvImg_undistort, yuvImg_undistort_nv12, DST_WIDTH, DST_HEIGHT); }free(yuvImg_I420);free(yuvImg_NV12);free(yuvImg_undistort);free(yuvImg_undistort_i420);free(yuvImg_undistort_nv12);return 0; }上述程序只提供大概思路保证百分之百不能运行哈比方说相机内参文件和图片来源都没有。我的思路是先将YUV图像转成yuv444packed的格式不清楚yuv格式的需要自行百度喽因为这样像RGB似的好处理。去完畸变后再转回来。opencv同样要从yuv转到rgb格式再转回来因为后续编码和OSD还要在yuv图像的基础上做。去畸变的思路参考了opencv用了查表的思想。可以看到获取映射表的函数其中计算非常复杂进行了大量的幂计算和乘法竟然还有8次幂。可以想象如果每帧图像都这样计算那得是多么恐怖。查表就简单多了。可以看到整个undistort_img函数不过10行找到对应的像素计算好偏移后直接拷贝就行了。那么我们将opencv和自己写的函数各运行1000次两者均用最近邻插值算法对比一下看看效率怎么样测试平台    imx8q处理器       Cortex-A35核心数       4最大主频    1.2GHzopencv和自写c语言去畸变比较上面是opencv处理1000张640x480图片总共耗时22.16秒下面是自写函数c代码处理1000张640x480图片耗时76.13秒。磨刀不误砍柴工别急看一下进度条故事还长听我娓娓道来。。。先来了解一下arm的neon协处理器。他是用来执行simd指令的。simd叫做单指令多数据操作。具体能干啥且看下图上面描绘了sisd指令和simd指令的对比简单来讲就是simd可以把你4条指令执行完的事我一条指令就给搞定。我理解为就是把寄存器位数扩宽了一个寄存器里可以存好几个数。neon中有32个这样的寄存器每个寄存器128位。可以存1个128位的数或者2个64位的。。。和16个8位的。好了simd了解这些就行了。关于指令问题我们结合代码看。对了还需要了解下内联汇编asm asm-qualifiers ( AssemblerTemplate : OutputOperands: InputOperands: Clobbers: GotoLabels)内联汇编的基本格式如上被4个冒号分为5部分第一部分是汇编语句然后是输出部分、后面是输入部分接着是破坏描述部分我不知道咋翻译最后是什么标签我没用过。不需要的部分可以省略。这里只做引入详细的使用方法请参考gnu网站。https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html图穷匕首现那么我们开始表演吧根据上面我们自己写的代码需要优化三个函数。先把结果代码放上去然后我们逐句讲解void NV12_to_YUV444packed(uint8_t *src_date, uint8_t *dst_data, int width, int height) {#ifdef __ARM_NEON__uint8_t *src_y src_date;uint8_t *src_uv src_date width * height;asm volatile (mov x1, %[hei] \n //x1 for height loopmov x2, %[src_y] \n //x2 for src_y pointermov x3, %[src_uv] \n //x3 for src_uv pointermov x4, %[dst_data] \n //x4 for dst pointerheight_loop_nv12_444pack:mov x0, %[wid] \n //x0 for width loopand x5, x1, #1 \n //get if height is oddmul x5, x5, %[wid] \nsub x3, x3, x5 \nwidth_loop_nv12_444pack:ld1 {v0.16b}, [x2], #16 \n //load 16byte yuv_y to v0 register ld2 {v3.8b, v4.8b}, [x3], #16 \n //load 16byte yuv_uv to v3/v4 register zip1 v1.16b, v3.16b, v3.16b \n //duplicate each byte in v1zip1 v2.16b, v4.16b, v4.16b \nst3 {v0.16b, v1.16b, v2.16b}, [x4], #48 \n //store v0, v1, v2 to memorysub x0, x0, #16 \ncmp x0, #0 \nbgt width_loop_nv12_444pack \nsub x1, x1, #1 \ncmp x1, #0 \nbgt height_loop_nv12_444pack \n: [dst_data] r (dst_data): [src_y] r (src_y), [src_uv] r (src_uv), [wid] r (width), [hei] r (height): memory, x0, x1, x2, x3, x4, x5, v0, v1, v2, v3, v4);#elseuint8_t *src_y src_date;uint8_t *src_uv src_date width * height;uint32_t tmp 0;int k 0;for (int i 0; i height; i){for (int j 0; j width; j){dst_data[k] src_y[i * width j];tmp 2 * ((i / 2) * (width / 2) (j / 2));dst_data[k] src_uv[tmp];dst_data[k] src_uv[tmp 1];}}#endifreturn; }void YUV444packed_to_NV12(uint8_t *src_data, uint8_t *dst_data, int width, int height) {#ifdef __ARM_NEON__uint8_t *dst_y dst_data;uint8_t *dst_uv dst_data width * height;asm volatile (mov x1, %[hei] \n //x1 for height loopmov x2, %[src] \n //x2 for src pointermov x3, %[dst_y] \n //x3 for dst_y pointermov x4, %[dst_uv] \n //x4 for dst_uv pointerheight_loop_444pack_nv12:mov x0, %[wid] \n //x0 for width loopand x5, x1, #1 \n //get if height is oddmul x5, x5, %[wid] \nsub x4, x4, x5 \nwidth_loop_444pack_nv12:ld3 {v0.16b, v1.16b, v2.16b}, [x2], #48 \n //load 48byte yuv_y to v0,v1,v2 register uzp1 v1.16b, v1.16b, v1.16b \n //store half of u in high section of v1 uzp1 v2.16b, v2.16b, v2.16b \n //store half of v in high section of v2 st1 {v0.16b}, [x3], #16 \n //store v0 to dst_y memoryst2 {v1.8b, v2.8b}, [x4], #16 \n //store v1, v2 to dst_uv memorysub x0, x0, #16 \ncmp x0, #0 \nbgt width_loop_444pack_nv12 \nsub x1, x1, #1 \ncmp x1, #0 \nbgt height_loop_444pack_nv12 \n: [dst_y] r (dst_y), [dst_uv] r (dst_uv): [src] r (src_data), [wid] r (width), [hei] r (height): memory, x0, x1, x2, x3, x4, x5, v0, v1, v2);#elseuint8_t *dst_y dst_data;uint8_t *dst_u dst_data width * height;uint32_t tmp 0;int k 0;for (int i 0; i height; i){for (int j 0; j width; j){dst_y[i * width j] src_data[k];tmp 2 * ((i / 2) * (width / 2) (j / 2));dst_u[tmp] src_data[k];dst_u[tmp 1] src_data[k];}}#endifreturn; }void undistort_img(uint8_t *yuv_raw, uint8_t *yuv_undistort, const distort_coor mapping_table[DST_HEIGHT][DST_WIDTH], uint16_t width, uint16_t height) {#ifdef __ARM_NEON__uint32_t tmp[4] {0};tmp[0] 3;tmp[1] SRC_WIDTH;asm volatile (mov x1, %[hei] \n //x1 for height loopmov x2, %[map] \n //x2 for map pointermov x3, %[yuv444_dst] \n //x3 for dst pointerld1 {v2.4s}, [%[tmp]] \n height_loop:mov x0, %[wid] \n //x0 for width loopwidth_loop:ld2 {v0.4s, v1.4s}, [x2], #32 \n //v0 is u_distort, v1 is v_distort mul v0.4s, v0.4s, v2.4s[0] \nmul v1.4s, v1.4s, v2.4s[0] \nmul v1.4s, v1.4s, v2.4s[1] \nadd v0.4s, v0.4s, v1.4s \nmov w4, v0.4s[0] \n //get the offset of the first pixelldr x4, [%[yuv_raw], x4]\nstr x4, [x3], #3 \n //store the first pixels yuv into memorymov w4, v0.4s[1] \nldr x4, [%[yuv_raw], x4]\nstr x4, [x3], #3 \nmov w4, v0.4s[2] \nldr x4, [%[yuv_raw], x4]\nstr x4, [x3], #3 \nmov w4, v0.4s[3] \nldr x4, [%[yuv_raw], x4]\nstr x4, [x3], #3 \nsub x0, x0, #4 \ncmp x0, #0 \nbgt width_loop \nsub x1, x1, #1 \ncmp x1, #0 \nbgt height_loop \n: [yuv444_dst] r (yuv_undistort): [yuv_raw] r (yuv_raw), [wid] r (width), [hei] r (height), [map] r (mapping_table), [tmp] r (tmp): memory, x0, x1, x2, x3, x4, v0, v1, v2);#elseuint32_t tmp2 0;for (int i 0; i height; i){for (int j 0; j width; j){tmp2 mapping_table[i][j].v_distorted * SRC_WIDTH * 3 (int)mapping_table[i][j].u_distorted * 3;memcpy(yuv_undistort, yuv_raw tmp2, 3);yuv_undistort 3;}}#endifreturn; }我们来看undistort_img这个函数竟然把10行代码写了这么多哈啊哈分析C代码就是通过映射表找到非畸变图像对应畸变图像的坐标然后计算出非畸变图像对于基地址的偏移将yuv三个分量的数据拷贝到非畸变图像即可。用个图表示如下查表过程从图中可知畸变图像和非畸变图像的像素不是一一对应的实际上是毫无规律的这个得查映射表根据映射表的坐标值算出偏移才能执行拷贝。不然我直接memcpy宽x高x3不就行了嘛。看程序流程每次循环过程中都需要计算一个非畸变图像的偏移值。虽然这个计算量和创建映射表时进行的高次幂计算没法比。但是架不住每个像素都要计算一次啊有乘有加的。这就轮到simd显摆了。可以看到虽然非畸变图像对应畸变图像的内存不是连续的。但是映射表的内存是连续的啊。非畸变00像素的映射表信息肯定存在映射表的前8个字节。01像素的往后排。这样我们就可以一次读取好几个像素的映射表。一次计算出好几个偏移然后再分别拷贝呗。说干就干输出部分[yuv444_dst] r (yuv_undistort)表示作为输出结果左边的变量在汇编代码中用右边是c代码。号表示可读写。r表示数据作为通用寄存器。输入部分[yuv_raw] r (yuv_raw), [wid] r (width), [hei] r (height), [map] r (mapping_table), [tmp] r (tmp)表示输入的原始数据。没有修饰符号修饰表示只读。破坏描述部分memory, x0, x1, x2, x3, x4, v0, v1, v2我理解为只要是你使用到的寄存器和内存都要在这里显式的指明以便编译器知道。汇编循环图中我圈出来的功能很好理解是完成c语言中的for循环。里面用到了mov、sub、cmp和bgt指令。我们一个一个说。mov x1, %[hei] \n表示将变量height的值放入x1寄存器中。%[]跟上输入部分的变量就相当于使用这个变量。在内联汇编中每一行汇编语句都要跟个换行。sub x0, x0, #4 \n表示将x0寄存器减4再放到x0中。这里可以看出来我们一次处理了4个像素。cmp x0, #0 \n表示将x0中的值和立即数0比较比较结果会影响spsr寄存器中的标志位N、S、C、V关于SPSR想要了解更多请自行翻阅arm手册。   SPSR寄存器bgt width_loop \n就好理解了就是如果上步比较的结果大于0就跳到标签width_loop 在内联汇编中同一进程内标签不允许重名即使在不同函数、不同文件内也不行。好了循环的就介绍完了。这两句是将映射表和目的地址的指针放到x2和x3寄存器中没啥好说的吧。介绍这两句需要先看学一下ld1和ld2指令的意义。LD2 {V0.8H, V1.8H}, [X0]ld2是simd指令。 可以看到ld2将内存中数据取出交叉放到v0和v1寄存器中。那么也可以理解ld3是取出放到3个v寄存器中。这样可以区分想要的数据。上述代码ld2 {v0.4s, v1.4s}, [x2], #32 \n就是把映射表中的数据取出放到v0和v1寄存器中。至于.4s表示的是这个寄存器存的是4个字长的数据就是4个4字节的数据刚好128bit。刚好映射表中存的也是4字节的数据uv坐标以此存储。那么v0中存的就是4个像素的u坐标v1中存的就是4个像素的v坐标。这样就实现uv坐标的分离方便单独操作。至于v2中存的是我想要用来当乘数的3和图像宽度。因为simd的乘法指令貌似不允许操作立即数。ld2最后面跟的立即数32表示的是读取完内存后将指针往后偏移32个字节以方便下次读取。因为有两个寄存器每个寄存器存放16字节所以要偏移32字节呀。刚才说x2中存放的是指针。那么[x2]就相当于解引用了找到地址对应的数据。后面的乘法指令和加法指令完成的就是c语言中tmp2的功能将偏移量最后放到了v0寄存器中。其中v0.4s[0]表示v0寄存器的第一个通道存储的也就是第一个像素的偏移值。同理还有二、三、四通道。哦对了ld1由于只有一个寄存器那就不交叉存放啦就直接读取16字节放到寄存器中就行啦。这一部分是将刚才计算的偏移量存放到w4寄存器中w4是x4的低半部分因为是64位cpu因此x4是64bit。所以w4是使用的低32bit高位置零。ldr x4, [%[yuv_raw], x4]\n是将原图中相对于基地址偏移x4地址上的数据加载到x4寄存器中。也就是具体像素的yuv数值啦。虽然yuv只占3个字节但是我们取出来了8个字节。没关系我们只要看前3个字节就好了。因为后面的数据会被下一次执行时覆盖掉。str x4, [x3], #3 \n表示的是将上面取出来的3字节yuv数值放到目的地址中去然后将目的地址往后偏移3个字节以便下次存放。看吧我就说会被覆盖掉吧再往后就是重复的操作了因为我们刚刚计算了4个像素的。因此我们后面重复操作了3次只是改了v0的通道而已。至此汇编代码就分析完啦。由于篇幅问题关于yuv色彩空间模型的转换就不分析啦感兴趣的可以留个作业自己分析分析那么我们看下速度吧opencv和自写汇编语言去畸变比较同样的上面是opencv处理1000张的结果耗时基本没变。下面是用汇编SIMD加速的处理1000张的耗时。可以看到速度是opencv的三倍多是自身c语言的11倍多后记软件性能调优从来不是一件容易的事汇编代码现在用的人很少了又因为和cpu紧密相关因此即使你认识人懂汇编的他也不一定能帮到你。比方说我在色彩空间模型转换中用到的这三句and x5, x1, #1 \n  mul x5, x5, %[wid] \n sub x4, x4, x5 \n其中x1存放的是循环中控制高度循环次数的数据我想做的效果就是当x1为奇数时x4就减去一个宽度值内存指针回退本来使用tst指令加subeq条件指令两句代码就能完成了。但是我的编译怎么也不支持subeq。后来找了半天才在arm开发手册中看到这句The A64 instruction set does not support conditional execution for every instruction. Predicated execution of instructions does not offer sufficient benefit to justify its significant use of opcode space.原来在v7架构中绝大部分指令都支持条件指令这还是它们宣传的一大卖点。但是v8中就给取消了。真真给跪了。无关的话有的时候你的所作所为会给你打上一辈子的标签。比方说毕业很多年了但是你的学校背书仍然能影响你的职业生涯虽然这种影响越来越小但是不可否认有些机会你会擦肩而过。
http://www.zqtcl.cn/news/525583/

相关文章:

  • 在线原型设计网站wordpress菜单页内跳转
  • 做电影网站要买什么抖音推广怎么收费
  • 专业的公司网站开发网站按钮设计
  • 南宁网站建设是什么深圳公司有哪些
  • 杭州手机申请网站登录怎么做电子商务网站
  • 青岛个人接网站建设wordpress 转载文章
  • 网上做网站任务网络营销传播的核心内容
  • 做黑界头像网站成考过来人的忠告
  • 宁波网站建设是哪家便宜织梦网站数据库备份文件夹
  • 在北京大学生做家教的网站淘宝网页
  • 英铭网站建设网站如何推广引流
  • 关于电子商务网站建设的现状企业公示信息查询系统山西
  • 网站开发 翻译长春建站企业
  • dedecms网站网站解析一般什么时候
  • 制作网站的技术北京律师24小时电话
  • 可拖拽 网站建设如何做自媒体和网站签约赚点击
  • 做网站选哪个语言怎么登录百度app
  • 国发网站建设网站优化主要优化哪些地方
  • 快速微信网站开发定制网站建设费用预算
  • 网站制作叫什么知名网站建设制作
  • 网络营销网站建设公司h5应用
  • 网站开发合同要上印花税吗南江红鱼洞水库建设管理局网站
  • 疏通下水道网站怎么做wordpress 恢复初始化
  • 电脑商业网站怎的做软文推广渠道
  • 自己做网站需要买什么如何做微信商城网站
  • 有了网站开发app是不是更容易自建网站管理
  • 网站将要准备建设的内容有哪些做外贸有效的网站
  • 网站设计博客网站内容添加
  • 网站建站行业新闻微盟开店怎么收费
  • 网站的建设参考文献郑州网站建设中国建设建设银行