OpenCvSharp 棋盘格标定助手_棋盘格标定工具-程序员宅基地

技术标签: C#  相机标定  OpenCvSharp  opencv  

  • 使用的是VS调用OpenCvSharp资源库进行一个Winform操作界面编写,网上找了很多开源的程序,发现根本用不了的,用的时候还需要你配置各种电脑系统变量,显得好麻烦。现在弄了个简单的标定助手,可以完美运行,带有棋盘格图像生成工具,操作简单,源码也不复杂。
    这里是
using OpenCvSharp;//需要引用OpenCvSharp
using OpenCvSharp.Extensions;
using Size = OpenCvSharp.Size;

        public Mat ChessBoardMat;
        /// <summary>
        /// 生成棋盘格图像
        /// </summary>
        /// <param name="BoardSize">输入棋盘格角点数量的大小</param>
        /// <param name="ImagePixel">输入棋盘格的像素大小</param>
        /// <returns></returns>
		public Bitmap GenChessBoard(OpenCvSharp.Size BoardSize, OpenCvSharp.Size ImagePixel)
        {
    
            int perBoardPixel = ImagePixel.Height / BoardSize.Height;
            int basisHeight = (ImagePixel.Height - perBoardPixel * BoardSize.Height) / 2;
            int basisWidth = (ImagePixel.Width - perBoardPixel * BoardSize.Width) / 2;
            if (basisHeight < 0 || basisWidth < 0)
            {
    
                return null;
            }
            ChessBoardMat = new Mat(ImagePixel, MatType.CV_8UC1, Scalar.All(255));
            int flag;
            for (int j = 0; j < BoardSize.Height; j++)
            {
    
                for (int i = 0; i < BoardSize.Width; i++)
                {
    
                    flag = (i + j) % 2;
                    if (flag == 0)
                    {
    
                        for (int n = j * perBoardPixel; n < (j + 1) * perBoardPixel; n++)
                            for (int m = i * perBoardPixel; m < (i + 1) * perBoardPixel; m++)
                                ChessBoardMat.At<byte>(n + basisHeight, m + basisWidth) = 0;
                    }
                }
            }
            return BitmapConverter.ToBitmap(ChessBoardMat);//返回棋盘格图像结果
            //Cv2.ImWrite("chessBoard1.bmp", image);//这里是保存生成的图像,可以不使用
        }

结果如下:
棋盘格生成工具点击“导入图像”后,选择采集的棋盘格图像所在文件夹,结果如下:
棋盘格标定助手 导入图像

可以在图像列表看到导入结果,在参数设置那里设置正确的棋盘格角点数量后,在图像列表双击图像路径,可以实现棋盘格图像角点的提取显示:
棋盘格标定助手 图像角点显示
可以提取角点之后就可以标定啦!
棋盘格标定助手  标定结果标定完成后,可以进行畸变矫正,这个是进行畸变矫正的结果,在这里并没有写保存矫正结果的代码。
所以只是看看就好

然后所有结果都出来了,并且自动保存结果在图像文件里内!
标定结果数据查看

这里是核心源码,至于Winform的操作代码,这里就不放了。感兴趣的朋友可以去下载看看

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Size = OpenCvSharp.Size;

namespace CvCalibrate
{
    
    public class Calib_Class
    {
    
        public Mat ChessBoardMat;
        #region 初始化变量
        /*内外参数*/
        Mat cameraMatrix = new Mat(3, 3, MatType.CV_64FC1, Scalar.All(0)); /* 摄像机内参数矩阵 */
        List<int> point_counts = new List<int>();  // 每幅图像中角点的数量
        Mat distCoeffs = new Mat(1, 12, MatType.CV_64FC1, Scalar.All(0)); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */  /*加上薄棱镜畸变模型,启用畸变系数S1、S2、S3和S4*/
        List<Mat> TranslationMats = new List<Mat>();  /* 每幅图像的旋转向量 */
        List<Mat> RotationMats = new List<Mat>(); /* 每幅图像的平移向量 */
        /*Mat数据类型转换double的结果*/
        public double[,] CameraParameterArray = new double[3, 3];//摄像机内参数矩阵
        public double[] DistCoeffsArray = new double[12];//摄像机的5个畸变系数:k1,k2,p1,p2,k3 /*加上薄棱镜畸变模型,启用畸变系数S1、S2、S3和S4*/
        public Vec3d[] Rotation; //存放所有图像的3*1旋转向量,每一副图像的旋转向量为一个mat
        public Vec3d[] Translation;//存放所有图像的3*1平移向量,每一副图像的平移向量为一个mat
        public double MeanError = 0.0; /* 所有图像的平均误差的总和 */
        public List<double> TotalError = new List<double>(); /* 每幅图像的平均误差 */
        public Size BoardImageSize = new Size();
        #endregion
        /// <summary>
        /// 角点提取,返回Bitmap数据
        /// </summary>
        /// <param name="imageInput">图像输入</param>
        /// <param name="BoardSize">棋盘格角点数量大小</param>
        /// <returns></returns>
        public Bitmap FindChessboardCorners(Image imageInput, OpenCvSharp.Size BoardSize)
        {
    
            try
            {
    
                Point2f[] image_points_buf;  /* 缓存每幅图像上检测到的角点 */
                List<Point2f[]> image_points_seq = new List<Point2f[]>(); /* 保存检测到的所有角点 */
                Mat image = BitmapConverter.ToMat(new Bitmap(imageInput));
                /* 提取角点 */
                Cv2.FindChessboardCorners(image, BoardSize, out image_points_buf);
                Mat view_gray = new Mat();
                Cv2.CvtColor(image, view_gray, ColorConversionCodes.RGB2GRAY);
                /* 亚像素精确化 */
                Point2f[] SubPix_points = Cv2.CornerSubPix(view_gray, image_points_buf, new Size(3, 3), new Size(-1, -1), TermCriteria.Both(30, 0.1));
                image_points_seq.Add(SubPix_points);  //保存亚像素角点
                /* 在图像上显示角点位置 */
                Cv2.DrawChessboardCorners(view_gray, BoardSize, image_points_buf, true); //用于在图片中标记角点
                return BitmapConverter.ToBitmap(view_gray);
            }
            catch (Exception)
            {
    
                return new Bitmap(imageInput);
            }

        }
        public double Fovx/*沿水平传感器轴的视野*/, Fovy/*沿竖直传感器轴的视野*/, FocalLength/*透镜焦距mm*/, AspectRatio/*fy/fx*/;
        public Point2d Principal/*主点坐标mm*/;
        /// <summary>
        /// 运行标定
        /// </summary>
        /// <param name="ChessBoardFiles">棋盘格图像文件路径</param>
        /// <param name="BoardNum">棋盘格角点数量大小</param>
        /// <param name="ErrorMessage">返回判断的消息</param>
        /// <returns></returns>
        public bool Calibrate(List<string> ChessBoardFiles, Size BoardNum, string ErrorMessage)
        {
    
            int image_count = 0;  /* 图像数量 */
            Size image_size = new Size();  /* 图像的尺寸 */
            if (ChessBoardFiles.Count() > 0)
            {
    

                #region 角点提取                 
                Point2f[] image_points_buf;  /* 缓存每幅图像上检测到的角点 */
                List<Point2f[]> image_points_seq = new List<Point2f[]>(); /* 保存检测到的所有角点 */
                /*读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化*/
                for (int i = 0; i < ChessBoardFiles.Count(); i++)
                {
    
                    try
                    {
    
                        Mat imageInput = Cv2.ImRead(ChessBoardFiles[i]);
                        image_count++;
                        if (image_count == 1)  //读入第一张图片时获取图像宽高信息
                        {
    
                            BoardImageSize.Width = image_size.Width = imageInput.Cols;
                            BoardImageSize.Height = image_size.Height = imageInput.Rows;
                        }
                        image_points_buf = FindChessboardCorners(imageInput, BoardNum, ErrorMessage);
                        if (image_points_buf.Count() > 0)
                        {
    
                            image_points_seq.Add(image_points_buf);
                        }
                        else
                        {
    
                            ErrorMessage += "图 " + i + ", ";
                        }

                    }
                    catch (Exception Err)
                    {
    
                        ErrorMessage = " 角点提取失败!" + "\n\r" + Err.Message;
                        return false;
                    }
                }
                #endregion

                #region 标定板初始化
                //棋盘三维信息
                // 初始化标定板上角点的三维坐标
                //生成一个标准的标定板角点坐标集合
                Size square_size = new Size(20, 20);  //初始化标定板上每个棋盘格的大小 
                List<List<Point3f>> object_points = new List<List<Point3f>>(); // 保存标定板上角点的三维坐标
                if (BoardNum.Width > 0)
                {
    
                    try
                    {
    
                        for (int t = 0; t < image_count; t++)
                        {
    
                            List<Point3f> tempPointSet = new List<Point3f>();
                            for (int i = 0; i < BoardNum.Height; i++)
                            {
    
                                for (int j = 0; j < BoardNum.Width; j++)
                                {
    
                                    Point3f realPoint;
                                    // 假设标定板放在世界坐标系中z=0的平面上
                                    realPoint.X = i * square_size.Width;
                                    realPoint.Y = j * square_size.Height;
                                    realPoint.Z = 0;
                                    tempPointSet.Add(realPoint);
                                }
                            }
                            object_points.Add(tempPointSet);
                            // 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 
                            point_counts.Add(BoardNum.Width * BoardNum.Height);
                        }
                    }
                    catch (Exception Err)
                    {
    
                        ErrorMessage = "初始化棋盘格失败!" + "\n\r" + Err.Message;
                    }
                }
                else
                {
    
                    ErrorMessage = "棋盘格大小设置错误!";
                    return false;
                }

                #endregion

                #region 开始标定
                try
                {
    
                    /*薄棱镜畸变模型,启用畸变系数S1、S2、S3和S4*/
                    /*迭代标准*/
                    TermCriteria criteria = new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.MaxIter, 30, 0.01);
                    Cv2.CalibrateCamera(object_points, image_points_seq, image_size, CameraParameterArray, DistCoeffsArray, out Rotation, out Translation, CalibrationFlags.ThinPrismModel, criteria);

                }
                catch (Exception Err)
                {
    
                    ErrorMessage = "标定失败!" + "\n\r" + Err.Message;
                    return false;
                }
                #endregion

                #region 数据类型转换
                for (int r = 0; r < CameraParameterArray.GetLength(0); r++)
                {
    
                    for (int c = 0; c < CameraParameterArray.GetLength(1); c++)
                    {
    
                        cameraMatrix.At<double>(r, c) = CameraParameterArray[r, c];
                    }
                }

                for (int r = 0; r < DistCoeffsArray.Length; r++)
                {
    
                    distCoeffs.At<double>(0, r) = DistCoeffsArray[r];
                }
                for (int r = 0; r < Rotation.Length; r++)
                {
    
                    Mat TempRotation = new Mat(3, 1, MatType.CV_64FC1);

                    TempRotation.At<double>(0, 0) = Rotation[r].Item0;
                    TempRotation.At<double>(1, 0) = Rotation[r].Item1;
                    TempRotation.At<double>(2, 0) = Rotation[r].Item2;
                    RotationMats.Add(TempRotation);

                    Mat TempTrans = new Mat(3, 1, MatType.CV_64FC1);

                    TempTrans.At<double>(0, 0) = Translation[r].Item0;
                    TempTrans.At<double>(1, 0) = Translation[r].Item1;
                    TempTrans.At<double>(2, 0) = Translation[r].Item2;
                    TranslationMats.Add(TempTrans);
                }
                #endregion

                #region 重新投影计算,误差计算
                double total_err = 0.0;            // 所有图像的平均误差的总和 
                double Error;                      //每幅图像的平均误差 

                for (int i = 0; i < image_count; i++)
                {
    
                    try
                    {
    
                        List<Point3f> tempPointSet = object_points[i];
                        /* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
                        Mat NewProjectPoints = new Mat();/* 保存重新计算得到的投影点 */
                        Mat jacobian = new Mat();//雅可比矩阵
                        Cv2.ProjectPoints(InputArray.Create<Point3f>(tempPointSet), RotationMats[i], TranslationMats[i], cameraMatrix, distCoeffs, NewProjectPoints, jacobian, 0);
                        List<Point2f> NewProjectPointsToPoint2f = new List<Point2f>(); /* 保存重新计算得到的投影点,格式转换 */
                        {
    
                            string[] PointsArray = Cv2.Format(NewProjectPoints, FormatType.CSV).Split('\n');//分割出来最后一个回车符号占了一个空间
                            for (int r = 0; r < PointsArray.Length - 1; r++)
                            {
    
                                Point2f Temp = new Point2f();//临时缓存
                                string[] PointTemp = PointsArray[r].Split(',');
                                Temp.X = float.Parse(PointTemp[0]);
                                Temp.Y = float.Parse(PointTemp[1]);
                                NewProjectPointsToPoint2f.Add(Temp);
                            }
                        }

                        /* 计算新的投影点和旧的投影点之间的误差*/
                        Point2f[] OldProjectPoints = image_points_seq[i];
                        Mat tempImagePointMat = new Mat(1, OldProjectPoints.Length, MatType.CV_32FC2);
                        Mat image_points2Mat = new Mat(1, NewProjectPoints.Rows, MatType.CV_32FC2);
                        for (int j = 0; j < OldProjectPoints.Count(); j++)
                        {
    
                            image_points2Mat.At<Vec2f>(0, j) = new Vec2f(NewProjectPointsToPoint2f[j].X, NewProjectPointsToPoint2f[j].Y);
                            tempImagePointMat.At<Vec2f>(0, j) = new Vec2f(OldProjectPoints[j].X, OldProjectPoints[j].Y);
                        }
                        Error = Cv2.Norm(image_points2Mat, tempImagePointMat, NormTypes.L2);//当前图像的投影误差
                        total_err += Error * Error;
                        TotalError.Add(Error);
                        MeanError += Error /= point_counts[i];//总的平均误差
                        double 重投影误差3 = (total_err / image_count);
                    }
                    catch (Exception Err)
                    {
    
                        ErrorMessage = "误差计算失败!" + "\n\r" + Err.Message;
                        return false;
                    }
                }
                #endregion

                #region 旋转矩阵计算
                List<Mat> AllRotation_Matrix = new List<Mat>(); /* 保存每幅图像的旋转矩阵 */
                for (int i = 0; i < image_count; i++)
                {
    
                    try
                    {
    
                        Mat rotation_matrix = new Mat(3, 3, MatType.CV_64FC1, Scalar.All(0));
                        /* 将旋转向量转换为相对应的旋转矩阵 */
                        /*Rodrigues()可以将旋转向量转化为旋转矩阵,也可以将旋转矩阵转化为旋转向量。旋转向量指定了旋转轴,同时它的模长也指定了旋转角度。*/
                        Cv2.Rodrigues(TranslationMats[i], rotation_matrix);
                        AllRotation_Matrix.Add(rotation_matrix);
                    }
                    catch (Exception Err)
                    {
    
                        ErrorMessage = "旋转矩阵计算失败!" + "\n\r" + Err.Message;
                        return false;
                    }

                }
                #endregion
                //Mat Rvec = new Mat();
                //Mat Tvec = new Mat();
                //Cv2.SolvePnP(InputArray.Create<Point3f>(object_points[0]), InputArray.Create<Point2f>(image_points_seq[0]), cameraMatrix, distCoeffs, Rvec, Tvec);
                //double[] R = MatToDouble(Rvec);
                //double[] T = MatToDouble(Tvec);
                Cv2.CalibrationMatrixValues(cameraMatrix, BoardImageSize, 5.3, 7.2, out Fovx, out Fovy, out FocalLength, out Principal, out AspectRatio);

            }
            else
            {
    
                ErrorMessage = "输入图像路径为空!";
                return false;
            }
            return true;
        }

        private delegate Point2f[] FindCornersDelegate(Mat Src, Size BoardNum, string ErrorMessage);

        /// <summary>
        /// 角点提取
        /// </summary>
        /// <param name="Src">图源</param>
        /// <param name="BoardNum">棋盘格角点数量大小</param>
        /// <param name="ErrorMessage">返回判断的消息</param>
        /// <returns></returns>
        private static Point2f[] FindChessboardCorners(Mat Src, Size BoardNum, string ErrorMessage)
        {
    
            Point2f[] image_points_buf = new Point2f[BoardNum.Height * BoardNum.Width];  /* 缓存每幅图像上检测到的角点 */
            Point2f[] SubPix_points = new Point2f[BoardNum.Height * BoardNum.Width];
            try
            {
    
                /* 提取角点 */
                if (!Cv2.FindChessboardCorners(Src, BoardNum, out image_points_buf))
                {
    
                    //粗提取角点,若无法提取则返回失败,反之进行精细化提取
                    ErrorMessage = " 角点提取失败!";
                }
                else
                {
    
                    Mat view_gray = new Mat();
                    Cv2.CvtColor(Src, view_gray, ColorConversionCodes.RGB2GRAY);
                    SubPix_points = Cv2.CornerSubPix(view_gray, image_points_buf, new Size(3, 3), new Size(-1, -1), TermCriteria.Both(30, 0.1));//对粗提取的角点进行精确化
                    //Cv2.Find4QuadCornerSubpix(view_gray, image_points_buf, new Size(5, 5)); //对粗提取的角点进行精确化
                }
            }
            catch (Exception Err)
            {
    
                ErrorMessage = Err.Message;
            }
            return SubPix_points;
        }

        #region 类型转换
        static double[] MatToDouble(Mat InMat)
        {
    
            if (InMat.Rows >= 1 && InMat.Cols == 1)
            {
    
                double[] OutDouble = new double[InMat.Rows];
                for (int i = 0; i < InMat.Rows; i++)
                {
    
                    OutDouble[i] = InMat.At<double>(i);
                }
                return OutDouble;
            }
            else if (InMat.Rows == 1 && InMat.Cols >= 1)
            {
    
                double[] OutDouble = new double[InMat.Cols];
                for (int i = 0; i < InMat.Cols; i++)
                {
    
                    OutDouble[i] = InMat.At<double>(i);
                }
                return OutDouble;
            }
            else
            {
    
                return null;
            }
        }
        static double[,] MatToDouble2D(Mat InMat)
        {
    
            if (InMat.Rows >= 1 && InMat.Cols >= 1)
            {
    
                double[,] OutDouble = new double[InMat.Rows, InMat.Cols];
                for (int i = 0; i < InMat.Rows; i++)
                {
    
                    for (int j = 0; j < InMat.Cols; j++)
                    {
    
                        OutDouble[i, j] = InMat.At<double>(i, j);
                    }
                }
                return OutDouble;
            }
            else
            {
    
                return null;
            }
        }
        static Mat DoubleToMat(double[] InDouble)
        {
    
            Mat OutMat = new Mat(1, InDouble.Length, MatType.CV_64FC1);
            for (int r = 0; r < InDouble.Length; r++)
            {
    
                OutMat.At<double>(0, r) = InDouble[r];
            }
            return OutMat;
        }
        static Mat Double2DToMat(double[,] InDouble)
        {
    
            Mat OutMat = new Mat(InDouble.GetLength(0), InDouble.GetLength(1), MatType.CV_64FC1);
            for (int r = 0; r < InDouble.GetLength(0); r++)
            {
    
                for (int c = 0; c < InDouble.GetLength(1); c++)
                {
    
                    OutMat.At<double>(r, c) = InDouble[r, c];
                }

            }
            return OutMat;
        }
        #endregion



       
    }
}

付费资源,各取所需吧,花了时间和心思做出来的东西让我免费共享是不可能的。
项目资源下载
对了,下载的源码里面有畸变矫正功能的,但是参数那些暂时没设置正确,所以矫正出来的图像似乎没那么准确,有兴趣的朋友可以去研究下,其实所有的代码都是可以参考OpenCv的,只不过是放在了C#下,有些变量类型略有不同,具体怎么样看看它的类就知道了。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Yoto_Jo/article/details/117574528

智能推荐

windows系统MySQL 5.7 解压缩版安装配置方法_mysql5.7 windows安装教程 解压-程序员宅基地

文章浏览阅读677次。本教程安装的版本为5.7.25 下载mysql压缩包并解压 下载地址:http://download.west263.net/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BD%AF%E4%BB%B6/mysql/mysql5.7.25-win32.zip 解压到指定路径,如D:\SOFT_PHP_PACKAGE\mysql 初始化数据库 打开cmd窗口输入 d: cd d:\SOFT_PHP_PACKAGE\mysql\bin mysqld.._mysql5.7 windows安装教程 解压

Android六大基本布局详解_安卓六大布局-程序员宅基地

文章浏览阅读5.5k次。Android六大基本布局详解_安卓六大布局

Java事件监听设计模式(通俗易懂)_java监听事件-程序员宅基地

文章浏览阅读1.6k次。事情监听设计模式(通俗易懂)这样一个场景:小狗要吃饭,爸爸妈妈收到他吃饭的消息有哪些类呢Dog:小狗,要被人监听了(事件源)PeopleEventLister:监听者的抽象接口,到时具体哪个类要监听小狗吃饭就要实现他PeopleListersManager:管理监听者的类代码小狗类package com.example.demo.event;import java.util.EventObject;/*** @program: demo* @description:_java监听事件

第十七节 Linux系统编程-开发板实现 TFTP 文件传输(一)_易百纳ss928开发板实现tftp传输-程序员宅基地

文章浏览阅读1.4k次。-------------------------------------资源来源于网络,仅供自学使用,如有侵权,联系我必删.第一:本章导读本章介绍如何使用 TFTP 服务器在开发板和虚拟机的 Ubuntu 进行传文件同一网段的概念概念需要结合 IP 地址以及子网掩码1)IP 地址:下图 Ubuntu 的 IP 地址是 192.168.0.109再来看一..._易百纳ss928开发板实现tftp传输

Qt快捷键_qt调到最后一行代码快捷键-程序员宅基地

文章浏览阅读280次。Shift + Home 选中当前位置到行首的所有内容shift + End 选中当前位置到行尾的所有内容alt + enter 为声明添加定义,或者为定义添加声明ctrl + alt + up 将当前行复制到上一行ctrl + alt + down 将当前行复制到下一行Ctrl + Shift + R 全局修改变量或者函数名Ctrl + ..._qt调到最后一行代码快捷键

RK3588平台开发系列讲解(I/O篇)Linux 磁盘 I/O 的性能指标_rk3588 io口速率-程序员宅基地

文章浏览阅读1.1k次,点赞31次,收藏18次。事实上,饱和度通常也没有其他简单的观测方法,不过,你可以把观测到的,平均请求队列长度或者读写请求完成的等待时间,跟基准测试的结果(比如通过 fio)进行对比,综合评估磁盘的饱和情况。剩下的部分,则是从各个角度来分别表示进程的 I/O 情况,包括线程 ID、I/O 优先级、每秒读磁盘的大小、每秒写磁盘的大小、换入和等待 I/O 的时钟百分比等。一般来说,我们在为应用程序的服务器选型时,要先对磁盘的 I/O 性能进行基准测试,以便可以准确评估,磁盘性能是否可以满足应用程序的需求。_rk3588 io口速率

随便推点

自动签名配置_生成签名时输入密钥库口令:-程序员宅基地

文章浏览阅读239次。自动签名配置_生成签名时输入密钥库口令:

php中使用html输入框,HTML表单和输入文本框-程序员宅基地

文章浏览阅读533次。HTML 表单用于收集用户输入的各类信息。1)例子文本域这个例子演示了如何在一个 HTML 页中创建文本域。用户可以在文本域中输入文本。密码框这个例子演示了如何在 HTML 页中创建密码输入框。(本文后面提供更多的例子)2)表单表单是包含表单内各个元素的表单域。表单域内的表单元素允许用户输入相关信息(如文本输入框、文本区域、下拉列表、单选按钮、复选按钮,等等)。表单由标记 定义。3)输入表单中最..._php网页怎么做到和html网页一样的输入表单

Qt for Android——关于版本的选择(ABI和CPU版本)_incompatible devices-程序员宅基地

文章浏览阅读7k次,点赞3次,收藏10次。1. 前景介绍 之前在开发Qt for Android程序的时候,不知道如何选择套件的版本,乱选一通,经常是程序开发完,到了运行选择设备的时候告诉我设备不匹配,不支持这个ABI。下面就来讲讲这些版本。2. Qt中套件对应的版本 在我们安装Qt的时候,会有Android相关的选项供我们勾选,我选择了如下图所示的三个选项: 从名字已经可以..._incompatible devices

html+css实例总结--遮罩、轮播图的实现_css 轮播图图片没有完全遮住-程序员宅基地

文章浏览阅读1k次,点赞2次,收藏10次。用html和css结合实现遮罩图和轮播图_css 轮播图图片没有完全遮住

PCB中加入任意LOGO图文说明_pcb板防静电标识-程序员宅基地

文章浏览阅读1.3k次。我们在网上找到任意一张图片,我找的是防静电图(原文件名:防静电.jpg) 首先我们要对下载下来的图片进行处理否则Altium designer6.9会提示装载的图片不是单色的,用Photoshop CS打开开始下载的图片(原文件名:试图1.jpg) 选择 图像→模式→灰度(原文件名:试图2.jpg) 在选择 图像→模式→位图(原文_pcb板防静电标识

Sobel算子边缘检测原理及实现-程序员宅基地

文章浏览阅读1.1w次,点赞11次,收藏63次。写在前面Prewitt算子同样是一种一阶微分算子,它的卷积算子和Prewitt算子非常类似,仅仅是系数不同,但Sobel算子对于像素位置的影响做了加权,与Prewitt算子、Roberts算子相比效果更好。优点对边缘定位较为准确,能较好地处理灰度渐变和噪声较多的图像,计算简单,可分别计算水平和垂直边缘,如EasyPR用其定位车牌。原理首先我们看Sobel算子: ..._sobel算子边缘检测原理

推荐文章

热门文章

相关标签