OpenCV实战(9)——基于反向投影直方图检测图像内容

news2024/9/25 13:25:51

OpenCV实战(9)——基于反向投影直方图检测图像内容

    • 0. 前言
    • 1 反向投影直方图
    • 2. 反向投影颜色直方图
    • 3. 完整代码
    • 小结
    • 系列链接

0. 前言

直方图是图像内容的一个重要特征。如果查看显示特定纹理或特定对象的图像区域,则该区域的直方图可以看作是一个函数,该函数给出了给定像素属于特定纹理或对象的概率。在本节中,将介绍直方图反投影的概念,以及如何将其用于检测特定的图像内容。

1 反向投影直方图

(1) 假设我们希望检测图像中的特定内容,例如,图中天空的云彩。首先选择一个包含目标对象的感兴趣区域 (Region of Interest, ROI),在图像中使用矩形框绘制此区域:

// 定义 ROI
cv::Mat imageROI;
imageROI = image(cv::Rect(200, 33, 24, 30));

图像感兴趣区域
(2) 使用 Histogram1D 类计算此 ROI 的直方图:

Histogram1D h;
cv::Mat hist = h.getHistogram(imageROI);

(3) 通过对这个直方图进行归一化,得到一个函数,该函数给出了给定强度值的像素属于定义区域的概率:

cv::normalize(h, histogram, 1.0);

(4) 反向投影直方图包括将输入图像中的每个像素值替换为其在归一化直方图中读取的相应概率值。OpenCV 使用 cv::calcBackProject() 函数执行此任务:

cv::calcBackProject(
    &image,
    1,              // 一次仅使用一张图片
    channels,       // 指定直方图维度
    histogram,     // 所用直方图
    result,         // 结果方向投影图像
    ranges,         // 值范围
    255.0           // 缩放因子
);

使用以上函数可以得到以下概率图,属于 ROI 的概率从亮(低概率)到暗(高概率):

反向投影直方图
(5) 如果我们对该图像应用阈值,我们可以获得最可能的云像素:

cv::threshold(result, result, threshold, 255.0, cv::THRESH_BINARY);

结果如下图所示:

检测结果

上图结果并不令人十分满意,因为除了云之外,许多其他区域也会被错误地检测,但本节最重要的目的是要理解概率函数是从简单直方图中提取的。图像中的许多其他像素与云像素具有相同的强度,并且在反向投影直方图中将相同强度的像素替换为相同的概率值。改善检测结果的一种解决方案是使用颜色信息,为此,我们需要修改对 cv::calBackProject 的调用。
cv::calBackProject 函数类似于 cv::calcHist 函数。第一个参数指定输入图像;然后需要列出使用的通道索引,传递给函数的直方图作为一个输入参数;维度应该与通道列表数组之一匹配;与 cv::calcHist 一样,ranges 参数以浮点数组的形式指定输入直方图的 bin 边界,每个数组指定每个通道的范围(最小值和最大值);输出结果 result 是计算出的概率图。由于每个像素都被在对应 bin 位置的直方图中找到的值替换,因此生成的图像的值介于 0.01.0 之间(假设已提供归一化直方图作为输入);最后一个参数用于通过将这些值乘以给定因子来重新调整这些值。

2. 反向投影颜色直方图

(1) 多维直方图也可以反向投影到图像上,我们需要定义一个封装反向投影过程的类。首先,我们定义所需的属性并初始化数据:

class ContentFinder {
    private:
        // 直方图参数
        float hranges[2];
        const float* ranges[3];
        int channels[3];
        float threshold;
        cv::Mat histogram;
        cv::SparseMat shistogram;
        bool isSparse;
    public:
        ContentFinder() : threshold(0.01f), isSparse(false) {
            ranges[0] = hranges;
            ranges[1] = hranges;
            ranges[2] = hranges;
        }

(2) 接下来,我们定义一个阈值参数,用于创建显示检测结果的二值图像。如果此参数设置为负值,则将返回原始概率图:

// 设置阈值
void setThreshold(float t) {
    threshold = t;
}
// 获取阈值
float getThreshold() {
    return threshold;
}

(3) 归一化输入直方图:

// 设置参考直方图
void setHistogram(const cv::Mat& h) {
    cv::normalize(h, histogram, 1.0);
}

(4) 要反向投影直方图,只需指定图像、范围(假设所有通道具有相同的范围)和使用的通道列表:

cv::Mat find(const cv::Mat& image) {
    cv::Mat result;
    hranges[0] = 0.0;
    hranges[1] = 256.0;
    channels[0] = 0;
    channels[1] = 1;
    channels[2] = 2;
    return find(image, hranges[0], hranges[1], channels);
}
// 检索属于参考直方图的像素
cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels) {
    cv::Mat result;
    hranges[0] = minValue;
    hranges[1] = maxValue;
    for (int i=0; i<histogram.dims; i++) {
        this->channels[i] = channels[i];
    }
    cv::calcBackProject(
        &image,
        1,
        channels,
        histogram,
        result,
        ranges,
        255.0
    );
    if (threshold>0.0) {
        cv::threshold(result, result, 255.0*threshold, 255.0, cv::THRESH_BINARY);
    }
    return result;
}

(5) 使用 BGR 直方图尝试检测蓝天区域。我们将首先加载彩色图像,定义感兴趣的区域,并在减色后的色彩空间中计算 3D 直方图:

// 加载彩色图像
ColorHistogram hc;
cv::Mat color = cv::imread("2.png");
imageROI = color(cv::Rect(0, 0, 100, 45));
// 获取 3D 直方图
hc.setSize(8);
cv::Mat shist = hc.getHistogram(imageROI);

(5) 接下来,需要计算直方图并使用 find 方法检测图像的天空部分:

// 创建内容检测器
ContentFinder finder;
finder.setHistogram(shist);
finder.setThreshold(0.05f);
result1 = finder.find(color);

对彩色图像的检测结果如下所示:

彩色图像检测结果
BGR 颜色空间通常不是识别图像中颜色对象的最佳颜色空间。为了便于计算,在计算直方图之前减少了颜色数量,提取的直方图表示天空区域的典型颜色分布。尝试将其反投影到另一个图像上,它同样可以检测天空部分。使用由多个天空图像构建的直方图可以提高检测的准确性。
在这种情况下,计算稀疏直方图会降低内存使用,我们可以尝试使用 cv::SparseMat 完成此任务。此外,如果我们需要检测具有鲜艳颜色的对象,使用 HSV 颜色空间的色调通道会更有效。在其他情况下,使用感知均匀空间(例如 L*a*b* )的色度分量可能是更好的选择。

3. 完整代码

头文件 colorhistogram.h 如下所示:

#if !defined COLHISTOGRAM
#define COLHISTOGRAM

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

class ColorHistogram {
    private:
        int histSize[3];
        float hranges[2];
        const float* ranges[3];
        int channels[3];
    public:
        ColorHistogram() {
            // 默认参数
            histSize[0] = histSize[1] = histSize[2] = 256;
            hranges[0] = 0.0;
            hranges[1] = 256.0;
            ranges[0] = hranges;
            ranges[1] = hranges;
            ranges[2] = hranges;
            channels[0] = 0;
            channels[1] = 1;
            channels[2] = 2;
        }
        // 设置直方图尺寸
        void setSize(int size) {
            histSize[0] = histSize[1] = histSize[2] = size;
        }
        cv::Mat getHistogram(const cv::Mat &image) {
            // 计算直方图
            cv::Mat hist;
            hranges[0] = 0.0;
            hranges[1] = 256.0;
            channels[0] = 0;
            channels[1] = 1;
            channels[2] = 2;
            cv::calcHist(&image,
                    1,              // 使用一张图片计算直方图
                    channels,       // 使用的通道
                    cv::Mat(),      // 不使用掩码
                    hist,           // 结果
                    3,              // 3D 直方图
                    histSize,       // bins 数量
                    ranges          // 像素值范围
            );
            return hist;
        }
        // 计算直方图
        cv::SparseMat getSparseHistogram(const cv::Mat& image) {
            cv::SparseMat hist(3, histSize, CV_32F);
            hranges[0] = 0.0;
            hranges[1] = 256.0;
            channels[0] = 0;
            channels[1] = 1;
            channels[2] = 2;
            // 计算直方图
            cv:: calcHist(
                &image,
                1,
                channels,
                cv::Mat(),
                hist,
                3,
                histSize,
                ranges
            );
            return hist;
        }
        // 计算 1D Hue 直方图
        cv::Mat getHueHistogram(const cv::Mat& image, int minSaturation=0) {
            cv::Mat hist;
            // 转换为 HSV 色彩空间
            cv::Mat hsv;
            cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
            cv::Mat mask;
            if (minSaturation>0) {
                std::vector<cv::Mat> v;
                cv::split(hsv, v);
                cv::threshold(v[1], mask, minSaturation, 255, cv::THRESH_BINARY);
            }
            hranges[0] = 0.0;
            hranges[1] = 180.0;
            channels[0] = 0;
            // 计算直方图
            cv::calcHist(
                &hsv,
                1,
                channels,
                mask,
                hist,
                1,
                histSize,
                ranges
            );
            return hist;
        }
        // 计算 2D ab 直方图
        cv::Mat getabHistogram(const cv::Mat& image) {
            cv::Mat hist;
            // 转换为 Lab 色彩空间
            cv::Mat lab;
            cv::cvtColor(image, lab, cv::COLOR_BGR2Lab);
            hranges[0] = 0;
            hranges[1] = 256.0;
            channels[0] = 1;
            channels[1] = 2;
            // 计算直方图
            cv::calcHist(
                &lab,
                1,
                channels,
                cv::Mat(),
                hist,
                2,
                histSize,
                ranges
            );
            return hist;
        }
};

#endif

头文件 contentFinder.h 如下所示:

#if !defined OFINDER
#define OFINDER

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

class ContentFinder {
    private:
        // 直方图参数
        float hranges[2];
        const float* ranges[3];
        int channels[3];
        float threshold;
        cv::Mat histogram;
        cv::SparseMat shistogram;
        bool isSparse;
    public:
        ContentFinder() : threshold(0.01f), isSparse(false) {
            ranges[0] = hranges;
            ranges[1] = hranges;
            ranges[2] = hranges;
        }
        // 设置阈值
        void setThreshold(float t) {
            threshold = t;
        }
        // 获取阈值
        float getThreshold() {
            return threshold;
        }
        // 设置参考直方图
        void setHistogram(const cv::Mat& h) {
            isSparse = false;
            cv::normalize(h, histogram, 1.0);
        }
        void setHistogram(const cv::SparseMat& h) {
            isSparse = true;
            cv::normalize(h, shistogram, 1.0, cv::NORM_L2);
        }
        cv::Mat find(const cv::Mat& image) {
            cv::Mat result;
            hranges[0] = 0.0;
            hranges[1] = 256.0;
            channels[0] = 0;
            channels[1] = 1;
            channels[2] = 2;
            return find(image, hranges[0], hranges[1], channels);
        }
        // 检索属于参考直方图的像素
        cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels) {
            cv::Mat result;
            hranges[0] = minValue;
            hranges[1] = maxValue;
            if (isSparse) {
                for (int i=0; i<shistogram.dims(); i++) {
                    this->channels[i] = channels[i];
                }
                cv::calcBackProject(
                    &image,
                    1,              // 一次仅使用一张图片
                    channels,       // 指定直方图维度
                    shistogram,     // 所用直方图
                    result,         // 结果方向投影图像
                    ranges,         // 值范围
                    255.0           // 缩放因子
                );
            } else {
                for (int i=0; i<histogram.dims; i++) {
                    this->channels[i] = channels[i];
                }
                cv::calcBackProject(
                    &image,
                    1,
                    channels,
                    histogram,
                    result,
                    ranges,
                    255.0
                );
            }
            if (threshold>0.0) {
                cv::threshold(result, result, 255.0*threshold, 255.0, cv::THRESH_BINARY);
            }
            return result;
        }
};

#endif

主函数代码 contentFinder.cpp 如下所示:

#include <iostream>
using namespace std;

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

#include "histogram.h"
#include "contentFinder.h"
#include "colorhistogram.h"

int main() {
    // 读取输入图像
    cv::Mat image = cv::imread("example.jpeg",0);
    if (!image.data) return 0;
    // 定义 ROI
    cv::Mat imageROI;
    imageROI = image(cv::Rect(1000, 10, 120, 120));
    // 显示参考块
    cv::namedWindow("Reference");
    cv::imshow("Reference", imageROI);
    // 计算参考直方图
    Histogram1D h;
    cv::Mat hist = h.getHistogram(imageROI);
    cv::namedWindow("Reference Hist");
    cv::imshow("Reference Hist", h.getHistogramImage(imageROI));
    // 创建内容检测器
    ContentFinder finder;
    // 反向投影直方图
    finder.setHistogram(hist);
    finder.setThreshold(-1.0f);
    cv::Mat result1;
    result1 = finder.find(image);
    cv::Mat tmp;
    result1.convertTo(tmp, CV_8U, -1.0, 255.0);
    cv::namedWindow("Backprojection result");
    cv::imshow("Backprojection result", tmp);
    // 获取二值反向投影
    finder.setThreshold(0.12f);
    result1 = finder.find(image);
    cv::rectangle(image, cv::Rect(1000, 10, 120, 120), cv::Scalar(0, 0, 0));
    cv::namedWindow("Image");
    cv::imshow("Image", image);
    cv::namedWindow("Detection Result");
    cv::imshow("Detection Result", result1);
    // 加载彩色图像
    ColorHistogram hc;
    cv::Mat color = cv::imread("example.jpeg");
    imageROI = color(cv::Rect(1000, 10, 120, 120));
    // 获取 3D 直方图
    hc.setSize(8);
    cv::Mat shist = hc.getHistogram(imageROI);
    finder.setHistogram(shist);
    finder.setThreshold(0.05f);
    result1 = finder.find(color);
    cv::namedWindow("Color Detection Result");
    cv::imshow("Color Detection Result", result1);
    // 第二张彩色图像
    cv::Mat color2 = cv::imread("example_2.jpeg");
    cv::namedWindow("Second Image");
    cv::imshow("Second Image", color2);
    // 反向投影直方图
    cv::Mat result2 = finder.find(color2);
    cv::namedWindow("Result color (2)");
    cv::imshow("Result color (2)", result2);
    // 计算 ab 色彩直方图
    hc.setSize(256);
    cv::Mat colorhist = hc.getabHistogram(imageROI);
    colorhist.convertTo(tmp, CV_8U, -1.0, 255.0);
    cv::namedWindow("ab histogram");
    cv::imshow("ab histogram", tmp);
    // 反向投影
    finder.setHistogram(colorhist);
    finder.setThreshold(0.05f);
    cv::Mat lab;
    cv::cvtColor(color, lab, cv::COLOR_BGR2Lab);
    int ch[2] = {1, 2};
    result1 = finder.find(lab, 0, 256.0f, ch);
    cv::namedWindow("Result ab (1)");
    cv::imshow("Result ab (1)", result1);
    // 第二张图像
    cv::cvtColor(color2, lab, cv::COLOR_BGR2Lab);
    result2 = finder.find(lab, 0, 256.0, ch);
    cv::namedWindow("Result ab (2)");
    cv::imshow("Result ab (2)", result2);
    // 为参考区域绘制矩形框
    cv::rectangle(color, cv::Rect(0, 0, 100, 45), cv::Scalar(0, 0, 0));
    cv::namedWindow("Color image");
    cv::imshow("Color image", color);
    // 计算 Hue 直方图
    hc.setSize(180);
    colorhist = hc.getHueHistogram(imageROI);
    // 反向投影
    finder.setHistogram(colorhist);
    // 转换色彩空间
    cv::Mat hsv;
    cv::cvtColor(color, hsv, cv::COLOR_BGR2HSV);
    // hue 直方图反向投影
    ch[0] = 0;
    result2 = finder.find(hsv, 0.0f, 180.0f, ch);
    cv::namedWindow("Result Hue (1)");
    cv::imshow("Result Hue (1)", result1);
    // 第二张图像
    color2 = cv::imread("3.png");
    cv::cvtColor(color2, hsv, cv::COLOR_BGR2HSV);
    result2 = finder.find(hsv, 0.0f, 180.0f, ch);
    cv::namedWindow("Result Hue (2)");
    cv::imshow("Result Hue (2)", result2);
    cv::waitKey();
    return 0;
}

小结

直方图是图像内容的一个重要特征,在许多场景中都有着重要用途。本节中,介绍了直方图反向投影的概念,以及如何将其用于检测特定的图像内容。

系列链接

OpenCV实战(1)——OpenCV与图像处理基础
OpenCV实战(2)——OpenCV核心数据结构
OpenCV实战(3)——图像感兴趣区域
OpenCV实战(4)——像素操作
OpenCV实战(5)——图像运算详解
OpenCV实战(6)——OpenCV策略设计模式
OpenCV实战(7)——OpenCV色彩空间转换
OpenCV实战(8)——直方图详解

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

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

相关文章

计算机网络之TCP全连接队列与半连接队列

什么是全、半连接队列 服务端接收syn请求后&#xff0c;内核会把该连接放入到半连接队列&#xff0c;服务端对synack返回ack后&#xff0c;会把连接从半连接队列移除&#xff0c;创建新的全连接&#xff0c;并把新的连接放入全连接队列 查看全连接队列的命令 全连接队列满了…

Deep Learning Tuning Playbook(深度学习调参手册中译版)

前言 由五名研究人员和工程师组成的团队发布了《Deep Learning Tuning Playbook》&#xff0c;来自他们自己训练神经网络的实验结果以及工程师的一些实践建议&#xff0c;目前在Github上已有1.5k星。原项目地址本文为《Deep Learning Tuning Playbook》中文翻译版本&#xff0…

聊一聊微信小程序生命周期你最想知道的一些事

目录 前言 解析 运行机制 总结 前言 &#xff08;1&#xff09;为什么是微信小程序 最近有人问我一些关于微信小程序的问题&#xff0c;今天有时间也就整理了一些微信小程序的相关资料&#xff0c;给大家简单系统总结一些微信小程序的相关知识及生命周期。 &#xff08;2…

使用光泵磁力仪(OPMs)非接触测量视网膜活动

使用光泵磁力仪&#xff08;OPMs&#xff09;非接触测量视网膜活动摘要绪论方法2.1参与者和测量设置2.2刺激2.3数据分析结果讨论原文见&#xff1a; https://www.sciencedirect.com/science/article/pii/S1053811921008016 摘要 光泵磁力仪&#xff08;OPM&#xff09;已被用…

关于提升销量和排名,亚马逊、wish、ebay卖家该怎么做?

新年好&#xff0c;我是龙哥测评&#xff0c;在这里祝愿所有跨境朋友在新的一年里财源滚滚&#xff0c;一切顺利。 接下来龙哥就来说说&#xff0c;为什么店铺销量提升不上去&#xff0c;产品曝光低转化率低&#xff0c;产品有排名但是没销量&#xff0c;等等这些问题&#xf…

5 个我们仍可 Solana 在熊市危机报有希望的原因

Daniel, 2022 年 1 月前面&#xff0c;我们发表了 《9 大指标分析 Solana 的熊市危机》。当查看数据时&#xff0c;有一个很好的论据&#xff0c;即 Solana 在 2021/2022 年期间严重超卖&#xff0c;该网络没有项目、TVL 或用户来维持自己度过长期的熊市。它的崩溃只是更严重&a…

QT/C++——主窗口和事件处理

一、主窗口 上面就是一个主窗口&#xff0c;主窗口中的每一个都是Action 这次新建工程要选择mainwindow #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QAction> #include <QTextEdit> #include <QLabel> #include &l…

LeetCode 剑指 Offer 09. 用两个栈实现队列

题目 解题 这道题是用两个栈实现一个队列&#xff0c;具有一个队尾添加元素和队头删除元素的功能 这里思路是一个进&#xff0c;一个出&#xff0c;里面的元素顺序很关键&#xff0c;要保持一个进栈最先进的元素在另一个出栈的栈顶 第一种方法是&#xff0c;每次进元素&#x…

冰冰学习笔记:信号

欢迎各位大佬光临本文章&#xff01;&#xff01;&#xff01; 还请各位大佬提出宝贵的意见&#xff0c;如发现文章错误请联系冰冰&#xff0c;冰冰一定会虚心接受&#xff0c;及时改正。 本系列文章为冰冰学习编程的学习笔记&#xff0c;如果对您也有帮助&#xff0c;还请各位…

使用Postman快速访问MemFire Cloud应用

“超能力”数据库&#xff5e;拿来即用&#xff0c;应用开发人员再也不用为撰写API而发愁。MemFire Cloud 为开发者提供了简单易用的云数据库&#xff08;表编辑器、自动生成API、SQL编辑器、备份恢复、托管运维&#xff09;&#xff0c;很大地降低开发者的使用门槛。 使用Post…

关于保研(免试攻读硕士学位研究生)的相关政策

文章目录1、保研政策是什么&#xff1f;2、哪些学校可以保研&#xff1f;3、保研的流程/名额是怎样的&#xff1f;1、保研政策是什么&#xff1f; 什么是保研&#xff1f; 官方定义&#xff1a; 保研&#xff08;全称&#xff1a;推荐优秀应届本科毕业生免试攻读硕士学位研究…

Linux基础开发环境,yum 与 vim。

✅<1>主页&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux &#x1f525;<3>创作者&#xff1a;我的代码爱吃辣 &#x1f4ac;<4>前言&#xff1a;Linux必不可少的基础开发环境使用。 目录 一.Linux软件包管理器 yum &#x…

微电网经济优化运行(光伏、储能、柴油机)(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

液晶LCD1602驱动代码

液晶LCD1602简介LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏&#xff08;LCD&#xff09;、控制驱动主电路HD44780及其扩展驱动电路HD44100&#xff0c;以及少量电阻、电容元件和结构件等装配在PCB板上而组成。不同厂家生产的LCD1602芯片可能…

《数据结构》二叉树(性质and遍历)

目录 ​编辑 树的定义 概念 树的定义 二叉树的概念 满二叉树 概念 完全二叉树 概念 二叉树的性质 二叉树的遍历 先序遍历 中序遍历 后序遍历 层序遍历 树的定义 概念 树是一种非线性的数据结构 树的定义 子树是不相交的除了根节点外&#xff0c;每个节点有且仅有一个…

360度客户视频的内容和四种数据类型

提到CRM客户管理系统&#xff0c;您是不是经常听到“360度客户视图”这个词&#xff1f;所谓360度客户视图&#xff0c;让企业做到全方位地建立客户认知&#xff0c;消除客户生命周期所有的信息脱节。下面来详细讲讲&#xff0c;Zoho CRM系统的360度客户视图是什么&#xff1f;…

统计分析工具

百度统计 百度统计访问地址 https://tongji.baidu.com/web/10000370440/homepage/index 创建项目及获取项目标识 1、创建分析网站 在管理页面中点击新增网站&#xff0c;填写完信息 2、获取代码 得到如下代码&#xff1a; <script> var _hmt _hmt || []; (function…

测试开发必知必会:Pytest框架实战

应用场景&#xff1a; pytest 框架可以解决我们多个测试脚本一起执行的问题。 它提供了测试用例的详细失败信息&#xff0c;使得开发者可以快速准确地改正问题。它兼容最新版本的 Python。它还兼容 unittest、doctest 和 nose&#xff0c;开箱即用。接下来我们详细了解下pyte…

数据究竟是什么?

我们搞大数据的&#xff0c;每天都在说“数据”这个词。但是数据究竟是什么&#xff1f;其准确的定义是什么&#xff1f;可能大多数人都没有思考过这个问题。首先&#xff0c;我们来考察下数据这个词的起源。数据这个词在古汉语中是不存在的&#xff0c;而是近代以来&#xff0…

在ObjectARX(VC)中使用MFC-可停靠窗体

目录 前言 一、使用CAD的CAcUiDockControlBar类 二、在入口程序中注册命令 三、窗口实现 四、目录结构 五、注意事项 六、效果展示 前言 CAD中经常会看到这样的窗口&#xff0c;下面就看看是如何实现的。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可…