2 图片的分割处理和亚像素精度处理(c++和python)

        本文的图片处理分为图片分割、图像的亚像素坐标处理。亚像素处理的原理可以看论文一种基于多项式插值改进的亚像素细分算法,该论文的详解及c++的代码实现可以看博文基于多项式插值的亚像素边缘定位算法_基于多项式插值的亚像素算法-CSDN博客。下面的内容很多来自以上博文的内容,我只是对部分代码做了稍微的修改。

        注意,前三部分是亚像素的理论和c++的代码实现,第四部分是python的代码实现。

一. 背景

        在测量或者定位的应用中会涉及到边缘检测, 但是像 OpenCV 这样的开源库中边缘检测算法的精度在像素级别, 比如 Canny, Sobel blablabla. 要想提高精度就需要用到 亚像素 的技术, 在不改变硬件成本的前提下提高检测精度。

二、代码实现(龟速版)

1.梯度幅值

梯度检测包含幅值和方向,用8个方向模板在检测图像上进行卷积运算(模板滑过的地方的像素值和模板中对应的值相乘, 最后全部加总), 得到 8 张梯度图像, 方向模板如下. 模板的方向就是和 X 轴的夹角。

以下代码生成 8 个方向模板

#include<opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include<list>
#include<vector>
#include <typeinfo>


using namespace cv;
using namespace std;


#define KERNEL_SUM 8

Mat kernels[KERNEL_SUM];

int k = 0;
kernels[k++] = (Mat_<float>(3, 3) <<  1,  2,  1,  0,  0,  0, -1, -2, -1);	// 270°
kernels[k++] = (Mat_<float>(3, 3) <<  2,  1,  0,  1,  0, -1,  0, -1, -2);	// 315°
kernels[k++] = (Mat_<float>(3, 3) <<  1,  0, -1,  2,  0, -2,  1,  0, -1);	// 0°
kernels[k++] = (Mat_<float>(3, 3) <<  0, -1, -2,  1,  0, -1,  2,  1,  0);	// 45°

flip(kernels[0], kernels[k++], 0);											// 90°

kernels[k++] = (Mat_<float>(3, 3) << -2, -1,  0, -1,  0,  1,  0,  1,  2);	// 135°

flip(kernels[2], kernels[k++], 1);											// 180°

kernels[k++] = (Mat_<float>(3, 3) <<  0,  1,  2, -1,  0,  1, -2, -1,  0);	// 225°

        接下来利用模板卷积生成梯度图像, 假设要检测的图像长下面这个样, 边缘是模糊的

        利用 filter2D 函数计算梯度

// 梯度图像
Mat gradients[KERNEL_SUM];
// 检测图像, 路径自己更改, 注意要是单通道图像
Mat imgsrc = imread("E:\\vs\\daima\\opencvs\\OpencvPractice\\1.jpg");//IMREAD_ANYCOLOR

for (int k = 0; k < KERNEL_SUM; k++)
{
    filter2D(imgsrc, gradients[k], CV_16S, kernels[k]);// CV_64F    //

    Mat imgshowt;
    normalize(gradients[k], imgshowt, 0, 255, NORM_MINMAX);
    cv::Mat rlt;
    imgshowt.convertTo(rlt, CV_8UC1);//将imgshowt转换为无符号单通道的整型并赋值给rlt  CV_8UC1
    cvtColor(rlt, gradients[k], cv::COLOR_BGR2GRAY);//将rlt转换为灰度图并赋值给gradients[k]
    
    imshow("img", rlt);
    waitKey(0);
}

        注意,这里和前面博文的代码稍有不同,因为原图像是3通道的图像(我在前面博文截取了个圆的图片用来处理),在以上代码中用filter2D函数后将处理的图片修改为了单通道图片,并且显示了8个卷积核处理的不同图片,结果如下(截取了前博文的图片,但效果相同):

        从图中可以看出各模板可以检测出指定方向的边缘, 其中白色表示该方向最大梯度值, 黑色表示反向最大梯度值。

梯度幅值最大值处的点就是边缘的整数坐标。

2. 梯度方向

        梯度方向指向梯度幅值最大的方向, 现在已经有 8 张幅值图像, 只要比较其中最大的幅值即可得到其方向, 例如第 0 张梯度图像在 (i, j) 处的幅值比其他图像都大, 则 (i, j) 点的梯度方向是 270°. 下面我们用代码求出最大幅度值和方向。

//2. 梯度方向
  // (1. 角度列表
const short angle_list[] = { 270, 315, 0, 45, 90, 135, 180, 225 };
// (2. 总幅值矩阵
Mat amplitude(imgsrc.rows, imgsrc.cols, CV_8UC1, Scalar::all(0));

// (3. 角度矩阵, 后面初始化成 -64 只是为了归一化之后能显示角度 0
Mat angle(imgsrc.rows, imgsrc.cols, CV_16SC1, Scalar::all(-64));//CV_16SC1   -64

for (int r = 0; r < imgsrc.rows; r++)
{

    short* pAng = angle.ptr<short>(r);

    for (int c = 0; c < imgsrc.cols; c++)
    {
        // 找出最大值
        for (int i = 0; i < KERNEL_SUM; i++)
        {
            if (amplitude.ptr<unsigned char>(r)[c] < gradients[i].ptr<unsigned char>(r)[c])
            {
                amplitude.ptr<unsigned char>(r)[c] = gradients[i].ptr<unsigned char>(r)[c];
                pAng[c] = angle_list[i];
            }
        }
        
    }
}


Mat imgshow;

imshow("amplitude", amplitude);
imwrite("D:\Datas\\1.jpg",amplitude);
waitKey(0);

normalize(angle, imgshow, 0, 255, NORM_MINMAX);
imgshow.convertTo(imgshow, CV_8UC1);
imshow("amplitude", imgshow);
waitKey(0);

        完成之后显示幅值和角度图像如下:

 

3. 单像素边缘

        以上的方式处理的图像边缘是比较粗的,那么我们如何将边缘的粗细提取成单像素呢,就是找最亮的像素点作为边缘。这个也比较简单, 只要在 3 × 3 的邻域中根据梯度的方向比较中心点 “左右” 的两个点幅值就可以知道了. 左右我打了引号, 有可能是在对角方向和上下方向. 如果不能理解的话, 把幅值图放大如下, 仿佛看到了马赛克

        我们发现在梯度方向幅值从小到大, 再变小, 我们只需要判断中间是否大于两边(“左右”)的幅值,如果同时大于两边,则是边缘点, 如果不是同时大于两边, 则不是边缘点, 下面用代码实现此判断。

 // 3.单像素边缘,整数坐标边缘图像
 //cout << "===============> start 单像素边缘 <================" << endl;
 Mat edge(imgsrc.rows, imgsrc.cols, CV_8UC1, Scalar::all(0));

 for (int r = 1; r < imgsrc.rows - 1; r++)
 {
     // 3 * 3 邻域, 所以用 3 个指针, 一个指针指一行
     const unsigned char* pAmp1 = amplitude.ptr<unsigned char>(r - 1);
     const unsigned char* pAmp2 = amplitude.ptr<unsigned char>(r);
     const unsigned char* pAmp3 = amplitude.ptr<unsigned char>(r + 1);

     const short* pAng = angle.ptr<short>(r);
     unsigned char* pEdge = edge.ptr<unsigned char>(r);

     for (int c = 1; c < imgsrc.cols - 1; c++)
     {
         switch (pAng[c])
         {
         case 270:
             if (pAmp2[c] > pAmp1[c] && pAmp2[c] >= pAmp3[c])
             {
                 pEdge[c] = 255;
             }
             break;
         case 90:
             if (pAmp2[c] >= pAmp1[c] && pAmp2[c] > pAmp3[c])
             {
                 pEdge[c] = 255;
             }
             break;

         case 315:
             if (pAmp2[c] > pAmp1[c - 1] && pAmp2[c] >= pAmp3[c + 1])
             {
                 pEdge[c] = 255;
             }
             break;
         case 135:
             if (pAmp2[c] >= pAmp1[c - 1] && pAmp2[c] > pAmp3[c + 1])
             {
                 pEdge[c] = 255;
             }
             break;

         case 0:
             if (pAmp2[c] > pAmp2[c - 1] && pAmp2[c] >= pAmp2[c + 1])
             {
                 pEdge[c] = 255;
             }
             break;
         case 180:
             if (pAmp2[c] >= pAmp2[c - 1] && pAmp2[c] > pAmp2[c + 1])
             {
                 pEdge[c] = 255;
             }
             break;

         case 45:
             if (pAmp2[c] >= pAmp1[c + 1] && pAmp2[c] > pAmp3[c - 1])
             {
                 pEdge[c] = 255;
             }
             break;
         case 225:
             if (pAmp2[c] > pAmp1[c + 1] && pAmp2[c] >= pAmp3[c - 1])
             {
                 pEdge[c] = 255;
             }
             break;

         default:
             break;
         }
     }
 }
 imshow("edg", edge);//总共有462个点为255(白色)
 waitKey(0);

 //cout << "===============> end 单像素边缘 <================" << endl;

        该代码得到的图片显示结果如下:

        现在的边缘是不是只有一个像素宽了, 到这里可以算是完成了 50% 工作了, 还有两个问题需要解决, 一是如何求出亚像素坐标, 二是怎样表示亚像素坐标

4. 亚像素坐标

        根据以下公式可计算亚像素坐标, 其实这个公式是二次多项式拟合(剪切前面博文的内容)

        下面代码中, sin 前面的负号非常关键, 因为图像的 y 方向和直角坐标系的 y 方向相反

// 4. 亚像素坐标
cout << "===============> start 亚像素坐标 <================" << endl;

// 根号2
const double root2 = sqrt(2.0);
// 三角函数表
double tri_list[2][KERNEL_SUM] = { 0 };

for (int i = 0; i < KERNEL_SUM; i++)
{
    tri_list[0][i] = cos(angle_list[i] * CV_PI / 180.0);
    // sin前面的负号非常关键, 因为图像的y方向和直角坐标系的y方向相反
    tri_list[1][i] = -sin(angle_list[i] * CV_PI / 180.0);
    
}

// vector 方式记录小数坐标
vector<Point3f> vPts;
// Mat 方式记录小数坐标, 注意这里是双通道
Mat coordinate(imgsrc.rows, imgsrc.cols, CV_32FC2, Scalar::all(0));

for (int r = 1; r < imgsrc.rows - 1; r++)
{
    // 3 * 3 邻域, 所以用3个指针, 一个指针指一行
    const short* pAmp1 = amplitude.ptr<short>(r - 1);
    const short* pAmp2 = amplitude.ptr<short>(r);
    const short* pAmp3 = amplitude.ptr<short>(r + 1);

    const short* pAng = angle.ptr<short>(r);
    const short* pEdge = edge.ptr<short>(r);

    float* pCoordinate = coordinate.ptr<float>(r);

    for (int c = 1; c < imgsrc.cols - 1; c++)
    {
        if (pEdge[c])
        {
            int nAngTmp = 0;
            double dTmp = 0;

            switch (pAng[c])
            {
            case 270:
                nAngTmp = 0;
                dTmp = ((double)pAmp1[c] - pAmp3[c]) / (pAmp1[c] + pAmp3[c] - 2 * pAmp2[c] + 0.000001) * 0.5;
                break;

            case 90:
                nAngTmp = 4;
                dTmp = -((double)pAmp1[c] - pAmp3[c]) / (pAmp1[c] + pAmp3[c] - 2 * pAmp2[c] + 0.000001) * 0.5;
                break;

            case 315:
                nAngTmp = 1;
                dTmp = ((double)pAmp1[c - 1] - pAmp3[c + 1]) / (pAmp1[c - 1] + pAmp3[c + 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
                break;

            case 135:
                nAngTmp = 5;
                dTmp = -((double)pAmp1[c - 1] - pAmp3[c + 1]) / (pAmp1[c - 1] + pAmp3[c + 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
                break;

            case 0:
                nAngTmp = 2;
                dTmp = ((double)pAmp2[c - 1] - pAmp2[c + 1]) / (pAmp2[c - 1] + pAmp2[c + 1] - 2 * pAmp2[c] + 0.000001) * 0.5;
                break;

            case 180:
                nAngTmp = 6;
                dTmp = -((double)pAmp2[c - 1] - pAmp2[c + 1]) / (pAmp2[c - 1] + pAmp2[c + 1] - 2 * pAmp2[c] + 0.000001) * 0.5;
                break;

            case 45:
                nAngTmp = 3;
                dTmp = ((double)pAmp3[c - 1] - pAmp1[c + 1]) / (pAmp1[c + 1] + pAmp3[c - 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
                break;

            case 225:
                nAngTmp = 7;
                dTmp = -((double)pAmp3[c - 1] - pAmp1[c + 1]) / (pAmp1[c + 1] + pAmp3[c - 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
                break;

            default:
                break;
            }

            const double x = c + dTmp * tri_list[0][nAngTmp];
            const double y = r + dTmp * tri_list[1][nAngTmp];
            const short z = angle_list[nAngTmp];
            //cout << dTmp<< "    "<< tri_list[0][nAngTmp]<<"   "<< dTmp * tri_list[0][nAngTmp] << endl;

            // vector方式
            vPts.push_back(Point3f((float)x, (float)y, (short)z));

            // Mat 方式
            pCoordinate[c << 1] = (float)x;
            pCoordinate[(c << 1) + 1] = (float)y;
        }
    }
}

//cout << "" << vPts.size() << endl;//总共有462个点为255(白色)
//for (size_t i = 0; i < vPts.size(); i++)
//{
//    cout << vPts[i].z << ":    " << vPts[i].x << ",    " << vPts[i].y <<endl;
//}

cout << "===============> end 亚像素坐标 <================" << endl;
return 0;

        注意,以上代码大家要了解为什么不同角度下加减不同,了解这个很重要。

三、龟速测试

至此, 龟速版本的代码已经完成了, 找一张其他图像试试? 找 lena 来试试。

        看到边缘图像有的同学可能要伤心了, 女神怎么变成这样了, 那么多边缘被检测出来了, 我们不需要那么多边缘啊. 同学别急, 检测出来那么多边缘是因为我们没有对梯度幅度进行筛选, 你想一下, 我们在计算单像素边缘的时候只要满足中间大于两边就算边缘, 女神图像中有一些中间只比两边大了一点点, 所以这种边缘可以去除,  我们想要的是比较强烈的边缘,解决办法就是设定一个阈值, 当梯度值大于阈值是才算真正的边缘。

将单像素边缘检测修改如下:

// 3.单像素边缘,整数坐标边缘图像
//cout << "===============> start 单像素边缘 <================" << endl;

// 阈值
double thres = 128;	// 此处为增加

Mat edge(imgsrc.rows, imgsrc.cols, CV_8UC1, Scalar::all(0));

for (int r = 1; r < imgsrc.rows - 1; r++)
{
    // 3 * 3 邻域, 所以用 3 个指针, 一个指针指一行
    const unsigned char* pAmp1 = amplitude.ptr<unsigned char>(r - 1);
    const unsigned char* pAmp2 = amplitude.ptr<unsigned char>(r);
    const unsigned char* pAmp3 = amplitude.ptr<unsigned char>(r + 1);

    const short* pAng = angle.ptr<short>(r);
    unsigned char* pEdge = edge.ptr<unsigned char>(r);

    for (int c = 1; c < imgsrc.cols - 1; c++)
    {
        
        // 以下判断为增加部分
		if (pAmp2[c] < thres)
		{
			continue;
		}
		//

        switch (pAng[c])
        {
        case 270:
            if (pAmp2[c] > pAmp1[c] && pAmp2[c] >= pAmp3[c])
            {
                pEdge[c] = 255;
            }
            break;
        case 90:
            if (pAmp2[c] >= pAmp1[c] && pAmp2[c] > pAmp3[c])
            {
                pEdge[c] = 255;
            }
            break;

        case 315:
            if (pAmp2[c] > pAmp1[c - 1] && pAmp2[c] >= pAmp3[c + 1])
            {
                pEdge[c] = 255;
            }
            break;
        case 135:
            if (pAmp2[c] >= pAmp1[c - 1] && pAmp2[c] > pAmp3[c + 1])
            {
                pEdge[c] = 255;
            }
            break;

        case 0:
            if (pAmp2[c] > pAmp2[c - 1] && pAmp2[c] >= pAmp2[c + 1])
            {
                pEdge[c] = 255;
            }
            break;
        case 180:
            if (pAmp2[c] >= pAmp2[c - 1] && pAmp2[c] > pAmp2[c + 1])
            {
                pEdge[c] = 255;
            }
            break;

        case 45:
            if (pAmp2[c] >= pAmp1[c + 1] && pAmp2[c] > pAmp3[c - 1])
            {
                pEdge[c] = 255;
            }
            break;
        case 225:
            if (pAmp2[c] > pAmp1[c + 1] && pAmp2[c] >= pAmp3[c - 1])
            {
                pEdge[c] = 255;
            }
            break;

        default:
            break;
        }
    }
}
imshow("edg", edge);//总共有462个点为255(白色)
imwrite("D:\Datas\\2.jpg", edge);
waitKey(0);

//cout << "===============> end 单像素边缘 <================" << endl;

四、python的代码实现

由于上面已经讲清楚原理和步骤,python代码和c++相似。代码如下:

import math
import numpy as np
import cv2
import matplotlib.pyplot as plt

#8个卷积核,分别对应0,45,90,135,180,225,270,315
#度数
angle_index = np.array((0,45,90,135,180,225,270,315)) #(1,8)
#卷积核
keras = np.zeros((8,3,3)) #存放8个3X3的卷积核
keras[0] = np.array(([1,  0, -1],[2,  0, -2],[1,  0, -1]), dtype="float32")# 0
keras[1] = np.array(([0, -1, -2],[1,  0, -1],[2,  1,  0]), dtype="float32")# 45
keras[2] = np.array(([-1,  -2, -1],[0,  0, 0],[1,  2, 1]), dtype="float32")# 90
keras[3] = np.array(([-2,  -1, 0],[-1,  0, 1],[0,  1, 2]), dtype="float32")# 135
keras[4] = np.array(([-1,  0, 1],[-2,  0, 2],[-1,  0, 1]), dtype="float32")# 180
keras[5] = np.array(([0,  1, 2],[-1,  0, 1],[-2,  -1, 0]), dtype="float32")# 225
keras[6] = np.array(([1,  2, 1],[0,  0, 0],[-1,  -2, -1]), dtype="float32")# 270
keras[7] = np.array(([2,  1, 0],[1,  0, -1],[0,  -1, -2]), dtype="float32")# 315


if __name__ == "__main__":
    # 1.输入图片路径,读取图片
    img1 = cv2.imread(r"imgs/1.jpg") #imgs/1.jpg
    # 2.灰度化处理图像
    grayImage = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)

    # 3.创建存放8个边缘处理的图片的容器
    dst = np.zeros((8,grayImage.shape[0],grayImage.shape[1]))

    # 循环8个卷积核,对不同的卷积核进行filter2D处理
    for i in range(8):
        dst[i] = cv2.filter2D(grayImage, -1, keras[i])

    # 4.梯度方向
        #对比上面8个图片,找出zui
    # (1 创建一个将8个半圆合并成一个圆的图片
    amplitude = np.zeros((grayImage.shape[0],grayImage.shape[1]))
    angle = np.zeros((grayImage.shape[0],grayImage.shape[1])) #每个坐标对应的角度


    for i in range(grayImage.shape[0]):
        for j in range(grayImage.shape[0]):
            st = 0
            for k in range(8):
                if amplitude[i][j] < dst[k][i][j]:
                    amplitude[i][j] = dst[k][i][j]
                    st = angle_index[k]
            angle[i][j] = st

    cv2.imshow("ss",amplitude)
    cv2.waitKey(0)
    cv2.imshow("ss", angle)
    cv2.waitKey(0)

    # 5.单像素边缘
      #在amplitude中找到单个像素点
    #(1 创建一个单个像素点的图片处理器
    edge = np.zeros((grayImage.shape[0], grayImage.shape[1]))
    for r in range(1,grayImage.shape[0]-1):
        pAmp1 = amplitude[r-1]
        pAmp2 = amplitude[r]
        pAmp3 = amplitude[r + 1]
        for c in range(1, grayImage.shape[1] - 1):

            if angle[r][c] == 270:
                if (pAmp2[c] > pAmp1[c] and pAmp2[c] >= pAmp3[c]):
                    edge[r][c] = 255
            if angle[r][c] == 90:
                if (pAmp2[c] >= pAmp1[c] and pAmp2[c] > pAmp3[c]):
                    edge[r][c] = 255
            if angle[r][c] == 315:
                if (pAmp2[c] > pAmp1[c - 1] and pAmp2[c] >= pAmp3[c + 1]):
                    edge[r][c] = 255
            if angle[r][c] == 135:
                if (pAmp2[c] >= pAmp1[c - 1] and pAmp2[c] > pAmp3[c + 1]):
                    edge[r][c] = 255
            if angle[r][c] == 0:
                if (pAmp2[c] > pAmp2[c - 1] and pAmp2[c] >= pAmp2[c + 1]):
                    edge[r][c] = 255
            if angle[r][c] == 180:
                if (pAmp2[c] >= pAmp2[c - 1] and pAmp2[c] > pAmp2[c + 1]):
                    edge[r][c] = 255
            if angle[r][c] == 45:
                if (pAmp2[c] >= pAmp1[c + 1] and pAmp2[c] > pAmp3[c - 1]):
                    edge[r][c] = 255
            if angle[r][c] == 225:
                if (pAmp2[c] > pAmp1[c + 1] and pAmp2[c] >= pAmp3[c - 1]):
                    edge[r][c] = 255

    cv2.imshow("tt",edge)
    cv2.waitKey(0)

    # 6.亚像素坐标
    root2 = math.sqrt(2.0) #根号2
    tri_list = np.zeros((2,8))#三角函数表
    for i in range(8):
        tri_list[0][i] = math.cos(angle_index[i] * math.pi / 180.0)
        tri_list[1][i] = -math.sin(angle_index[i] * math.pi / 180.0)#sin前面的负号非常关键, 因为图像的y方向和直角坐标系的y方向相反
    vPts = [] #记录小数坐标(x,y)

    for r in range(1,grayImage.shape[0]-1):
        # 3 * 3 邻域, 所以用3个指针, 一个指针指一行
        pAmp1 = amplitude[r - 1]
        pAmp2 = amplitude[r]
        pAmp3 = amplitude[r + 1]
        for c in range(1, grayImage.shape[1] - 1):
            dTmp = 0
            nAngTmp = 0
            if angle[r][c] == 270:
                nAngTmp = 6
                dTmp = (pAmp1[c] - pAmp3[c]) / (pAmp1[c] + pAmp3[c] - 2 * pAmp2[c] + 0.00001) * 0.5
            elif angle[r][c] == 90:
                nAngTmp = 2
                dTmp = -(pAmp1[c] - pAmp3[c]) / (pAmp1[c] + pAmp3[c] - 2 * pAmp2[c] + 0.00001) * 0.5
            elif angle[r][c] == 315:
                nAngTmp = 7
                dTmp = (pAmp1[c - 1] - pAmp3[c + 1]) / (pAmp1[c - 1] + pAmp3[c + 1] - 2 * pAmp2[c] + 0.00001) * root2 * 0.5
            elif angle[r][c] == 135:
                nAngTmp = 3
                dTmp = -(pAmp1[c - 1] - pAmp3[c + 1]) / (pAmp1[c - 1] + pAmp3[c + 1] - 2 * pAmp2[c] + 0.00001) * root2 * 0.5
            elif angle[r][c] == 0:
                nAngTmp = 0
                dTmp = (pAmp2[c - 1] - pAmp2[c + 1]) / (pAmp2[c - 1] + pAmp2[c + 1] - 2 * pAmp2[c] + 0.00001) * 0.5
            elif angle[r][c] == 180:
                nAngTmp = 4
                dTmp = -(pAmp2[c - 1] - pAmp2[c + 1]) / (pAmp2[c - 1] + pAmp2[c + 1] - 2 * pAmp2[c] + 0.00001) * 0.5
            elif angle[r][c] == 45:
                nAngTmp = 1
                dTmp = (pAmp3[c - 1] - pAmp1[c + 1]) / (pAmp1[c + 1] + pAmp3[c - 1] - 2 * pAmp2[c] + 0.00001) * root2 * 0.5
            elif angle[r][c] == 225:
                nAngTmp = 5
                dTmp = -(pAmp3[c - 1] - pAmp1[c + 1]) / (pAmp1[c + 1] + pAmp3[c - 1] - 2 * pAmp2[c] + 0.00001) * root2 * 0.5

            x = c + dTmp * tri_list[0][nAngTmp]
            y = r + dTmp * tri_list[1][nAngTmp]
            vPts.append([x,y])

        运行结果和c++的相似,大家可以自行选择编程语言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/720226.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分析医药零售数据该用哪个BI数据可视化工具?

数据是企业决策的重要依据&#xff0c;可以用于现代企业大数据可视化分析的BI工具有很多&#xff0c;各有各擅长的领域。那么哪个BI数据可视化工具分析医药零售数据又好又快&#xff1f; 做医药零售数据分析首推奥威BI数据可视化工具&#xff01; 奥威BI数据可视化工具做医药…

移动应用开发大作业报告

1 基本信息 1.1 系统名称 中华字典 1.2 开发运行环境 开发环境&#xff1a;Windows 10 专业版&#xff0c;JDK 1.8&#xff0c;AndroidStudio 运行环境&#xff1a;Java SE Runtime Environment (JRE) 8 1.3 使用的核心技术 JFrame&#xff1a;作为实现界面的窗体类&…

YOLOV8识别物体,并返回物体的像素坐标

一、YOLOV8的相关文件修改 1. 进入路径文件&#xff1a; C:\Users\82370\.conda\envs\Ayolo8\Lib\site-packages\ultralytics\engine\result.py&#xff08;此处路径为你的anacod安装的虚拟环境Ayolo8位置&#xff09; conda create -n Ayolo8 python3.11 # 虚拟环境安装代码…

天锐绿盾 | -公司电脑文件防泄密软件

天锐绿盾是一款专为企业设计的电脑文件防泄密系统&#xff0c;它结合了多种安全功能&#xff0c;旨在从源头上保障企业数据的安全。 www.drhchina.com 以下是关于天锐绿盾的详细介绍&#xff1a; 一、产品概述 天锐绿盾&#xff0c;又名绿盾信息安全管理软件&#xff0c;是一…

Linux安装MySQL以及远程连接

1、Linux安装MySQL 1.1、准备解压包 MySQL5.x解压包 提取码&#xff1a;9y7n 1.2、通过rpm脚本安装 切记安装顺序&#xff1a;common --> libs --> client --> server 因为它们之间存在依赖关系&#xff0c;所以务必按照顺序安装 安装前请确保当前目录/文…

山体滑坡监测利器:传感器与智能监测平台的应用

山体滑坡&#xff0c;这一地质灾害的代名词&#xff0c;指的是山坡上的土体或岩体在重力作用下&#xff0c;因自然或人为因素而向下滑动的现象。滑坡具有突发性、隐蔽性、危害性和破坏性等特征&#xff0c;因此&#xff0c;对于山体滑坡的监测工作显得尤为重要。本文将探讨山体…

算法设计与分析 实验3 回溯法求地图填色问题

目录 一、实验目的 二、背景知识 三、实验内容 四、算法思想 未优化的回溯算法 节点选择-最小剩余值准则&#xff08;MRV&#xff09; 节点选择-最多约束准则&#xff08;DH&#xff09; 颜色选择-最少约束选择 数据结构的选择 向前探查 颜色轮换&#xff08;贪心置…

Python机器学习完整流程:从数据清洗到推理落地

目录 一、引言 二、数据清洗 数据加载与初步探索 缺失值处理 异常值处理 特征编码与转换 数据集划分 三、模型训练 四、模型文件生成 五、模型部署与推理落地 六、总结 一、引言 在当今数据驱动的时代&#xff0c;机器学习已成为解决复杂问题的有力工具。而…

揭秘:5步打造移动应用铜墙铁壁!

在数字化时代的浪潮中&#xff0c;移动应用&#xff08;APP&#xff09;的安全与合规性问题日益显著&#xff0c;成为了开发者、企业和用户共同关注的焦点。面对这一挑战&#xff0c;通付盾APP尽职调查报告应运而生&#xff0c;犹如一座灯塔&#xff0c;照亮了移动应用安全前行…

银河麒麟系统项目部署

使用服务器信息 软件&#xff1a;VMware Workstation Pro 虚拟机&#xff1a;ubtun 内存&#xff1a;20G 虚拟机连接工具&#xff1a; MobaXterm Redis连接工具&#xff1a; RedisDesktopManager 镜像&#xff1a;F:\Kylin-Server-10-8.2-Release-Build09-20211104-X86_64…

精准测试:代码覆盖率与测试覆盖率

在日常的测试过程当中&#xff0c;不管是人工进行接口测试还是接口自动化&#xff0c;以及RD写的单元测试&#xff0c;我们一般使用代码覆盖率来衡量测试的完备程度&#xff0c;这篇文章就带大家认识一下代码覆盖率这个常用质量完备度的指标 代码覆盖率测试与测试覆盖率在软件…

“非遗+全身动作捕捉设备”如何打造交互式叙事新消费场景?

在数字化时代&#xff0c;非遗传承渠道逐渐数字化、科技化&#xff0c;利用“虚拟人全身动作捕捉设备”技术提升了非遗文化的社会能见度&#xff0c;让非遗文化重新吸引年轻人的目光。 “虚拟人全身动作捕捉设备”&#xff0c;可以让虚拟人化身虚拟主持人、虚拟主播、虚拟嘉宾…

3D三维模型展示上传VR全景创建H5开源版开发

3D三维模型展示上传VR全景创建H5开源版开发 新增三级分类&#xff08;项目分类、项目、默认场景&#xff09; 新增热点 前台创建项目、场景 场景跳转、提示信息 新增热点图标选择 新增预览场景是显示关联场景 新增3D模型展示功能 当然可以&#xff01;以下是一个关于3D三维模…

HarmonyOS 页面路由(Router)

1. HarmonyOS页面路由(Router) 页面路由指在应用程序中实现不同页面之间的跳转和数据传递。HarmonyOS提供了Router模块&#xff0c;通过不同的url地址&#xff0c;可以方便地进行页面路由&#xff0c;轻松地访问不同的页面。本文将从页面跳转、页面返回和页面返回前增加一个询问…

Python安装失败,报0x80070643-安装时发生严重错误。

背景 之前安装了3.12.4&#xff0c;因为没用到&#xff0c;就用Revo Uninstaller Pro卸载了&#xff0c;连注册表都清理了。后面看到别人写的一个工具不符合预期&#xff0c;想对源码修改下&#xff0c;用到了Python,于是重新安装&#xff0c;出现上面报错。 解决方法尝试 因…

在Pycharm使用Github Copilot

文章目录 1.GitHub Copilot 是什么2.注册GitHub Copilot3.官方使用文档4.安装 GitHub Copilot插件5.在Pycharm中使用6.相关功能键7.启用或禁用 GitHub Copilot 1.GitHub Copilot 是什么 GitHub Copilot 是一款 AI 编码助手&#xff0c;可帮助你更快、更省力地编写代码&#xff…

基于javassm实现的物流管理系统

开发语言&#xff1a;Java 框架&#xff1a;ssm 数据库&#xff1a;mysql 系统页面展示 4.1登陆页面 平台登录&#xff1a;主要是做权限分配和安全限制等操作。可以把快递员&#xff0c;客户&#xff0c;派单员等人员角色区分开来。 4.2注册页面 用户注册界面&#xff1a;…

固定式土壤墒情监测仪—土壤状况进行长期跟踪和分析

TH-TS600 固定式土壤墒情监测仪是一种专门用于长期、连续、自动监测土壤墒情的设备。能够实时监测土壤的水分、温度、湿度等关键参数&#xff0c;确保农民和管理者能即时获取土壤状况信息&#xff0c;便于及时做出农业决策。由于是自动监测&#xff0c;数据采集的准确性和可靠性…

目标检测数据集 - 手机屏幕表面表面缺陷检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;手机屏幕表面缺陷检测数据集&#xff0c;真实采集高质量手机屏幕表面含缺陷图片数据&#xff0c;数据集含多款不同型号和品牌的手机屏幕表面图片数据&#xff0c;包括苹果手机屏、三星手机屏、华为手机屏等数据。数据标注标签包括 Bubble 气泡/水滴、Scr…

动手学深度学习(Pytorch版)代码实践 -深度学习基础-13Kaggle竞赛:2020加州房价预测

13Kaggle竞赛&#xff1a;2020加州房价预测 # 导入所需的库 import numpy as np import pandas as pd import torch import hashlib import os import tarfile import zipfile import requests from torch import nn from d2l import torch as d2l# 读取训练和测试数据 train_…