OpenCV实战——实现高效图像扫描循环

news2024/12/23 19:58:26

OpenCV实战——实现高效图像扫描循环

    • 0. 前言
    • 1. 测量代码运行时间
    • 2. 计算不同扫描算法的执行时间
    • 4. 完整代码及运行结果
    • 相关链接

0. 前言

在《像素操作》一节中,我们介绍了扫描图像以处理其像素的不同方法。在本节中,我们将学习比较这些方法的计算效率。编写图像处理函数时,效率通常是一个需要考虑的问题。设计函数时,经常需要检查代码的计算效率,以检测处理中可能减慢程序速度的瓶颈操作。
但是,需要注意的是,除非必要,否则不应以降低程序明确性为代价进行优化。简单的代码相对更容易调试和维护,只有对程序效率至关重要的代码部分才应进行大量优化。

1. 测量代码运行时间

为了测量函数或某一部分代码的执行时间,可以使用 OpenCV 函数 cv::getTickCount(),此函数可以提供自上次启动计算机以来发生的时钟周期数。由于通常我们希望以秒为单位给出代码部分的执行时间,因此我们需要使用另一种方法 cv::getTickFrequency() 来获取每秒的周期数。总体而言,为了获得给定函数(或某一部分代码)的执行时间,通常使用以下方式:

(1) 获取开始时间点:

const int64 start = cv::getTickCount();

(2) 调用要测量的函数或代码:

colorReduce(image);

(3) 计算执行时间,使用结束时间点减去开始时间点再除以每秒周期数:

double duration = (cv::getTickCount()- start)/cv::getTickFrequency();

在以上代码中,度量了 colorReduce() 函数的执行时间,绝对运行时间会因机器而异。同时,运行时间还取决于用于生成可执行文件的特定编译器。

2. 计算不同扫描算法的执行时间

(1) 首先,我们比较使用指针扫描图像一节中介绍的三种计算颜色减少的函数。可以看到,整数除法公式和按位运算符的执行时间几乎相同,即 20 毫秒。然而,基于模运算符的版本需要 31 毫秒。这表示最快和最慢的程序之间几乎有 50% 的差异,因此,重要的是要花时间确定在图像循环中获取计算结果的最高效方法。当指定需要重新分配的输出图像而不是就地处理时,执行时间变为 22 毫秒,额外时间用于内存分配的开销。

(2) 在循环中,应该避免重复计算可以预先计算的值。例如,以下减色函数的内部循环:

int nc = image.cols * image.channels();
uchar div2 = div>>1;
for (int i=0; i<nc; i++){
    *data++ += div2
}

如果将其替换为以下内容:

for (int i=0; i<image.cols*image.channels(); i++){
    *data++ += div>>1
}

那么在此循环中,就需要一次又一次地计算一行中的元素总数 image.cols*image.channels()和 div>>1;此时,运行时间将延长至 40 毫秒,这明显比原始版本慢(原始版本需要 20 毫秒)。需要注意的是,某些编译器可能能够优化这些类型的循环并仍然可以获得高效的代码。

(3) 使用迭代器的减色函数需要的运行时间为 31 毫秒,迭代器的主要目标是简化图像扫描过程并使其不易出错。

(4) 为完整起见,我们还可以实现使用 at() 方法进行像素访问的函数版本:

for (int j=0; j<nl; j++) {
    for (int i=0; i<nc; i++) {
        image.at<cv::Vec3b>(j,i)[0] = image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
        image.at<cv::Vec3b>(j,i)[1] = image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
        image.at<cv::Vec3b>(j,i)[2] = image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
    }
}

使用此方法,需要大约 43 毫秒的运行时间,相对而言要慢得多,这种方法应该只用于图像像素的随机访问,而非扫描图像时。

(5) 即使处理的元素总数相同,具有较少语句的较短循环通常比在单个语句上执行较长循环更高效。类似地,如果有 N 种不同的计算要应用于一个像素,需要在一个循环中全部应用,而不是编写 N 个连续循环(每个循环完成一个计算)。

(6) 我们还可以进行连续图像测试,在连续图像的情况下使用一个循环,而不是在行和列上使用双循环。对于尺寸较大的图像,这种优化并不重要(仅从 22 毫秒将至 20 毫秒),但总的来说,使用这种策略是一个很好的做法。

(7) 多线程是另一种提高算法效率的方法,尤其是在多核处理器出现之后。OpenMP 和英特尔线程构建块 (Intel Threading Building Blocks, TBB) 是两种流行的 API,用于并发编程以创建和管理线程。此外,C++11 以上版本也提供了对线程的内置支持。

4. 完整代码及运行结果

完整代码 () 示例如下所示:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

// 使用指针扫描图像
void colorReduce(cv::Mat image, int div=64) {
    int nl = image.rows;
    int nc = image.cols * image.channels();
    for (int j=0; j<nl; j++) {
        // 获取输入图像第j行地址
        uchar* data = image.ptr<uchar>(j);
        for (int i=0; i<nc; i++) {
            data[i] = data[i]/div*div + div/2;
        }
    }
}

// 带有输入输出的颜色减少函数
void colorReduceIO(const cv::Mat &image, cv::Mat &result, int div=64) {
    int nl = image.rows;
    int nc = image.cols;
    int nchannels = image.channels();
    // 为输出图像分配内存
    result.create(image.rows, image.cols, image.type());
    for (int j=0; j<nl; j++) {
        // 获取输入与输出图像第j行地址
        const uchar* data_in = image.ptr<uchar>(j);
        uchar* data_out = result.ptr<uchar>(j);
        for (int i=0; i<nc*nchannels; i++) {
            data_out[i] = data_in[i] / div * div + div / 2;
        }
    }
}

// 使用解引用操作的颜色减少函数
void colorReduce1(cv::Mat image, int div=64) {
    int nl = image.rows;
    int nc = image.cols * image.channels();
    uchar div2 = div >> 1;
    for (int j=0; j<nl; j++) {
        uchar* data = image.ptr<uchar>(j);
        for (int i=0; i<nc; i++) {
            *data++ += *data / div * div + div2;
        }
    }
}

// 使用求模运算符的颜色减少函数
void colorReduce2(cv::Mat image, int div=64) {
    int nl = image.rows;
    int nc = image.cols * image.channels();
    uchar div2 = div >> 1;
    for (int j=0; j<nl; j++) {
        uchar* data = image.ptr<uchar>(j);
        for (int i=0; i<nc; i++) {
            int value = *data;
            *data++ = value - value % div + div2;
        }
    }
}

// 使用二进制掩码的颜色减少函数
void colorReduce3(cv::Mat image, int div=64) {
    int nl = image.rows;
    int nc = image.cols * image.channels();
    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)+0.5);
    // 用于舍入像素值的掩码
    uchar mask = 0xFF<<n; // 例如,对于div=16的情况,mask=0xF0
    uchar div2 = 1<<(n-1); // div = div/2
    for (int j=0; j<nl; j++) {
        uchar* data = image.ptr<uchar>(j);
        for (int i=0; i<nc; i++) {
            *data &= mask; // 掩码处理
            *data++ |= div2; // 加div/2
        }
    }
}

// 使用低阶(直接)指针的颜色减少函数
void colorReduce4(cv::Mat image, int div=64) {
    int nl = image.rows;
    int nc = image.cols * image.channels();
    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)+0.5);
    int step = image.step; // 图像有效宽度
    // 用于舍入像素值的掩码
    uchar mask = 0xFF<<n; // 例如,对于div=16的情况,mask=0xF0
    uchar div2 = 1<<(n-1); // div = div/2
    // 获取图像缓冲区指针
    uchar *data = image.data;
    for (int j=0; j<nl; j++) {
        for (int i=0; i<nc; i++) {
            *(data+i) &= mask; // 掩码处理
            *(data+i) |= div2; // 加div/2
        }
        data += step;
    }
}

// 在循环中每次计算行大小的颜色减少函数
void colorReduce5(cv::Mat image, int div=64) {
    int nl = image.rows;
    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)+0.5);
    // 用于舍入像素值的掩码
    uchar mask = 0xFF<<n; // 例如,对于div=16的情况,mask=0xF0
    for (int j=0; j<nl; j++) {
        uchar* data = image.ptr<uchar>(j);
        for (int i=0; i<image.cols* image.channels(); i++) {
            *data &= mask;
            *data++ += div/2;
        }
    }
}

// 图像为连续图像时的颜色减少函数
void colorReduce6(cv::Mat image, int div=64) {
    int nl = image.rows;
    int nc = image.cols;
    if (image.isContinuous()) {
        // 无填充像素
        nc = nc * nl;
        nl = 1; // 1D 阵列
    }
    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)+0.5);
    // 用于舍入像素值的掩码
    uchar mask = 0xFF<<n; // 例如,对于div=16的情况,mask=0xF0
    uchar div2 = 1<<(n-1); // div = div/2
    // 对于连续图像,只需执行一次循环
    for (int j=0; j<nl; j++){
        uchar* data = image.ptr<uchar>(j);
        for (int i=0; i<nc; i++){
            *data &= mask;
            *data++ += div2;
        }
    }
}  

// 使用reshape函数整形连续图像的颜色减少函数
void colorReduce7(cv::Mat image, int div=64) {
    if (image.isContinuous()) {
        // 无填充像素
        image.reshape(
            1,  // 新图像通道数
            1   // 新图像行数
        );
    }
    int nl = image.rows;    // 图像行数
    int nc = image.cols * image.channels();
    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)+0.5);
    // 用于舍入像素值的掩码
    uchar mask = 0xFF<<n; // 例如,对于div=16的情况,mask=0xF0
    uchar div2 = 1<<(n-1); // div = div/2
    // 对于连续图像,只需执行一次循环
    for (int j=0; j<nl; j++){
        uchar* data = image.ptr<uchar>(j);
        for (int i=0; i<nc; i++){
            *data &= mask;
            *data++ += div2;
        }
    }
}

// 使用Mat_迭代器处理3个通道的颜色减少函数
void colorReduce8(cv::Mat image, int div=64) {
    // 获取迭代器
    cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
    uchar div2 = div >> 1;
    for (; it!=itend; ++it) {
        (*it)[0] = (*it)[0]/div*div + div2;
        (*it)[1] = (*it)[1]/div*div + div2;
        (*it)[2] = (*it)[2]/div*div + div2;
    }
}

// 在Vec3b上使用迭代器的颜色减少函数
void colorReduce9(cv::Mat image, int div=64) {
    // 获取迭代器
    cv::MatIterator_<cv::Vec3b> it = image.begin<cv::Vec3b>();
    cv::MatIterator_<cv::Vec3b> itend = image.end<cv::Vec3b>();
    const cv::Vec3b offset(div/2,div/2,div/2);
    for (; it!=itend; ++it) {
        *it = *it/div*div + offset;
    }
}

// 使用带有二进制掩码的迭代器的颜色减少函数
void colorReduce10(cv::Mat image, int div=64) {
    // div 必须是2的幂次
    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)+0.5);
    // 用于舍入像素值的掩码
    uchar mask = 0xFF<<n; // 例如,对于div=16的情况,mask=0xF0
    uchar div2 = 1<<(n-1); // div = div/2
    // 获取迭代器
    cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
    for (; it!=itend; ++it) {
        (*it)[0] &= mask;
        (*it)[0] += div2;
        (*it)[1] &= mask;
        (*it)[1] += div2;
        (*it)[2] &= mask;
        (*it)[2] += div2;
    }
}

// 使用Mat_迭代器的颜色减少函数
void colorReduce11(cv::Mat image, int div=64) {
    // 获取迭代器
    cv::Mat_<cv::Vec3b> cimage = image;
    cv::Mat_<cv::Vec3b>::iterator it = cimage.begin();
    cv::Mat_<cv::Vec3b>::iterator itend = cimage.end();
    uchar div2 = div>>1;
    for (; it!=itend; ++it) {
        (*it)[0] = (*it)[0]/div*div + div2;
        (*it)[1] = (*it)[1]/div*div + div2;
        (*it)[2] = (*it)[2]/div*div + div2;
    }
}

// 使用at方法的颜色减少函数
void colorReduce12(cv::Mat image, int div=64) {
    int nl = image.rows;
    int nc = image.cols;
    uchar div2 = div>>1;
    for (int j=0; j<nl; j++) {
        for (int i=0; i<nc; i++) {
            image.at<cv::Vec3b>(j,i)[0] = image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
            image.at<cv::Vec3b>(j,i)[1] = image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
            image.at<cv::Vec3b>(j,i)[2] = image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
        }
    }
}

// 使用Mat重载运算符的颜色减少函数
void colorReduce13(cv::Mat image, int div=64) {
    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)+0.5);
    // 用于舍入像素值的掩码
    uchar mask = 0xFF<<n; // 例如,对于div=16的情况,mask=0xF0
    image = (image&cv::Scalar(mask,mask,mask))+cv::Scalar(div/2,div/2,div/2);
}

// 使用查找表的颜色减少函数
void colorReduce14(cv::Mat image, int div=64) {
    cv::Mat lookup(1,256,CV_8U);
    for (int i=0; i<256; i++) {
        lookup.at<uchar>(i)=i/div*div+div/2;
    }
    cv::LUT(image, lookup, image);
}
#define NTESTS 15
#define NITERATIONS 10
int main() {
    // 读取图像
    cv::Mat image = cv::imread("1.png");
    // 统计处理图像所用时间
    const int64 start = cv::getTickCount();
    colorReduce(image, 64);
    double duration = (cv::getTickCount() - start) / cv::getTickFrequency();
    // 打印执行时间
    std::cout << "Duration = " << duration << "secs" << std::endl;
    cv::namedWindow("Image");
    cv::imshow("Image", image);
    cv::waitKey();
    // 测试不同函数
    int64 t[NTESTS], tinit;
    // 初始化计时器
    for (int i=0; i<NTESTS; i++) {
        t[i] = 0;
    }
    cv::Mat images[NTESTS];
    cv::Mat result;
    // 需要进行测试的函数
    typedef void(*FunctionPointer)(cv::Mat, int);
    FunctionPointer functions[NTESTS] = {colorReduce, colorReduce1, colorReduce2, colorReduce3, colorReduce4,
        colorReduce5, colorReduce6, colorReduce7, colorReduce8, colorReduce9, colorReduce10, colorReduce11,
        colorReduce12, colorReduce13, colorReduce14
    };
    // typedef void(*FunctionPointer)(cv::Mat, int);
	// FunctionPointer functions[NTESTS] = { colorReduce, colorReduce1, colorReduce2, colorReduce3, colorReduce4,
	// 									  colorReduce5, colorReduce6, colorReduce7, colorReduce8, colorReduce9,
	// 									  colorReduce10, colorReduce11, colorReduce12, colorReduce13, colorReduce14};
    // 重复测试数次
    int n = NITERATIONS;
    for (int k=0; k<n; k++) {
        std::cout << k << " of " << n << std::endl;
        for (int c=0; c<NTESTS; c++) {
            images[c] = cv::imread("1.png");
            // 设定计时器并调用函数
            tinit = cv::getTickCount();
            functions[c](images[c], 64);
            t[c] += cv::getTickCount() - tinit;
            std::cout << ".";
        }
        std::cout << std::endl;
    }
    // short description of each function
	std::string descriptions[NTESTS] = {
		"original version:",
		"with dereference operator:",
		"using modulo operator:",
		"using a binary mask:",
		"direct ptr arithmetic:",
		"row size recomputation:",
		"continuous image:",
		"reshape continuous image:",
		"with iterators:",
		"Vec3b iterators:",
		"iterators and mask:",
		"iterators from Mat_:",
		"at method:",
		"overloaded operators:",
		"look-up table:",
	};
    for (int i=0; i<NTESTS; i++) {
        cv::namedWindow(descriptions[i]);
        cv::imshow(descriptions[i], images[i]);
    }
    // 打印平均执行时间
    std::cout << std::endl << "-----------------------------" << std::endl;
    for (int i=0; i<NTESTS; i++) {
        std::cout << i << ". " << descriptions[i] << 1000.*t[i] / cv::getTickFrequency() / n << "ms" << std::endl;
    }
    cv::waitKey();
    return 0;
}

编译并执行以上代码,可以得到以下结果图像:
结果图像

相关链接

OpenCV实战(1)——OpenCV与图像处理基础
OpenCV实战(2)——OpenCV核心数据结构
OpenCV实战(4)——像素操作

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux篇 一、香橙派Zero2设置开机连接wifi

香橙派Zero2系列文章目录 一、香橙派Zero2设置开机连接wifi 文章目录香橙派Zero2系列文章目录前言一、调试串口的连接说明二、WIFI 连接测试1.首先使用 nmcli dev wifi 命令扫描周围的 WIFI 热点2.找到自己的wifi&#xff0c;准备好wifi名称和密码三、设置开机连接WiFi总结前言…

【MySQL】第10章_创建和管理表

创建和管理表1. 基础知识1.1 一条数据存储的过程1.2 标识符命名规则1.3 MySQL中的数据类型2. 创建和管理数据库2.1 创建数据库2.2 使用数据库查看当前所有的数据库查看当前正在使用的数据库查看指定库下所有的表查看数据库的创建信息使用/切换数据库2.3 修改数据库更改数据库字…

RadZen 最新注册版,2022年末圣诞-happy

RadZen快速简单地生成业务 Web 应用程序&#xff0c;以可视化方式构建和启动 Web 程序&#xff0c;而我们会为您创建新代码。 从信息开始 连接到数据库。Radzen 推断您的信息并生成功能完备的 Web 应用程序。支持 MSSQL REST 服务。 微调添加页面或编辑生成的页面。通过预览您…

vue使用高德地图,marker低于1000,滑动卡顿问题的探究(已解决)

问题描述 vue使用高德地图点标记&#xff0c;刚开始使用的是Marker&#xff0c;但是数目超过300&#xff0c;滑动就卡顿&#xff0c;按文档来说&#xff0c;Marker 类型推荐在数据量为 500 以内时使用&#xff0c;不应该卡顿。后边就开始对这个bug进行两天脑秃的探究了 1.换成…

pytorch-天气识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章地址&#xff1a; 365天深度学习训练营-第P3周&#xff1a;天气识别&#x1f356; 作者&#xff1a;K同学啊一、前期准备 1.设置GPU import torch import torch.nn as nn impor…

MAC苹果系统安装数字证书的方法

MAC苹果系统安装数字证书的方法之工具/原料 Mac OS电脑一台 数字证书 先讲讲安装方法,mac系统默认浏览器是Safari,那小D在这里就以Safari浏览器为例子,讲解一下相关的安装方法 如果已有开通了数字证书的用户,在重装了系统或是在没有安装安装证书的电脑上进行付款时,会提…

【ELM回归预测】基于非洲秃鹫算法优化极限学习机预测附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

FL Studio水果21版本助力原创音乐人(中文完整版All Plugins)

最近&#xff0c;网上算是“风言风语”吧&#xff0c;关于FL Studio是否出21版的说法各异。首先呢&#xff0c;这里先肯定一点&#xff0c;FL Studio即将出FL Studio 21版本&#xff0c;但是正式版已经出来。希望大家不要被网上一些所谓冒充发布的FL Studio21正式版所骗&#x…

信息系统分析与设计:摊位管理信息系统

摊位管理信息系统的分析与设计 1 市场分析 1.1 地摊经济发展背景 1.2 地摊经济逐渐复苏 1.3 地摊经济的放管服 2 目标市场定位 2.1 普通城市居民 2.2 政府相关管理部门 3 系统主要介绍 3.1 系统创新描述 3.2 主要搭建流程 3.3 主要业务模块 3.4 业务流程图 3.5 组…

Vue学习笔记--第二章(尚硅谷学习视频总结)

第二章 Vue组件化编程第二章 Vue组件化编程2.1. 模块与组件、模块化与组件化2.1.1. 模块2.1.2. 组件2.1.3. 模块化2.1.4. 组件化2.2. 非单文件组件2.2.1. 基本使用2.2.2. 组件注意事项2.2.3. 组件的嵌套2.2.4. VueComponent2.2.5. 一个重要的内置关系2.3. 单文件组件第二章 Vue…

【C#基础学习】第十七章、数组

目录 数组 1.数组的类型 1.1 一维数组和矩形数组 1.1.1实例化一维数组和矩形数组 1.2 访问数组元素 1.3 初始化数组 1.3.1 显式初始化一维数组 1.3.2 显式初始化矩形数组 1.3.3 显式初始化的快捷语法 1.3.4 隐式类型数组 1.4 交错数组 1.4.1 声明交错数组 1.4.2 实例…

bump map(凹凸贴图)的一个简单生成方法

用于渲染物体表面&#xff0c;增加真实感的bump map(凹凸贴图)的一个简单生成方法。 1. 在 Perlin Noise Map Generator - OpenProcessing 生成一个perlin noise map&#xff0c; 点击代码按钮&#xff0c;修改生成图像的分辨率 点击 paly 按钮&#xff0c;设置参数&#xf…

学习笔记-3-SVM-10-SVR

细节内容请关注微信公众号&#xff1a;运筹优化与数据科学 ID: pomelo_tree_opt outline 1. Linear regression 2. Support vector regression 3. SVR vs. SVM 4. Linear SVR 5. Kernel SVR ------------------------------------ 1. Linear regression OR里最常使用的…

【从零开始学习深度学习】15. Pytorch实战Kaggle比赛:房价预测案例【含数据集与源码】

基于之前学习的内容&#xff0c;让我们动手实战一个Kaggle比赛的&#xff1a;房价预测实战案例。Kaggle是一个著名的供机器学习爱好者交流的平台&#xff0c;该房价预测实战网址&#xff1a;https://www.kaggle.com/competitions/house-prices-advanced-regression-techniques …

浅析Linux 内存布局

【推荐文章】 路由选择协议——RIP协议 纯干货&#xff0c;linux内存管理-内存管理架构&#xff08;建议收藏&#xff09; 轻松学会linux下查看内存频率,内核函数,cpu频率 X86体系结构 在X86体系结构下&#xff0c;物理内存地址一般从0x0000_0000开始&#xff0c;而Linux内核主…

微信小程序实战之获取用户信息并保存唯一实例

前言 这是我参加掘金启航计划的第二篇文章&#xff0c;这次总结的是获取用户信息并联合 mobx 状态管理库&#xff0c;保存全局唯一的用户对象。 本篇文章基于 微信云开发 &#xff0c;数据从云数据库中取出&#xff0c;使用微信云数据库API进行获取数据&#xff0c;希望观众老…

Altium Designer飞线不从过孔里面出线如何解决?

出现以上飞线不从过孔出线的原因是其拓扑结构所导致&#xff0c;解决方式就是设置下拓扑结构。 1、执行菜单栏命令“设计-规则”&#xff0c;或者快捷键DR&#xff0c;快速打开“PCB规则及约束编辑器”对话框&#xff0c;如图1所示。 2、在对应的对话框中&#xff0c;选择“Rou…

postgres源码解析41 btree索引文件的创建--2

本文将从btbuild函数作为入口从源码角度进行讲解btree文件的创建流程&#xff0c;执行SQL对应为CREATE TABLE wp_shy(id int primary key, name carchar(20))。知识回顾见&#xff1a;postgres源码解析41 btree索引文件的创建–1 执行流程图梳理 _bt_spools_heapscan 执行流程…

2153年,人类已被AI所奴役。就在这一天,作为一名被俘虏的“搜查部队”士兵,你来到了A0007号城外的反抗军基地中

2153年&#xff0c;地球。   人类&#xff0c;已被AI所奴役。   这个AI的缩写名为——PTA&#xff0c;或称“辟塔”。      辟塔的原型&#xff0c;是一个用于分析网络用户消费倾向并立即给出相关引导的软广告程序。   很快&#xff0c;辟塔便成了广大商家的宠儿&…

【华为上机真题 2022】求解连续数列

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…