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

价格对比网站开发并且图片越大越好

价格对比网站开发,并且图片越大越好,克拉玛依网站建设,wordpress 增加备案扫描图片数据 应用情景图像数据扫描的难点颜色空间缩减#xff08;color space reduction#xff09;查询表 扫描算法计算查询表统计运算时长连续内存3种扫描方法C风格的扫描方法迭代器方法坐标方法LUT方法 算法效率对比结论 应用情景 图像数据扫描的难点 在上一篇文章《基… 扫描图片数据 应用情景图像数据扫描的难点颜色空间缩减color space reduction查询表 扫描算法计算查询表统计运算时长连续内存3种扫描方法C风格的扫描方法迭代器方法坐标方法LUT方法 算法效率对比结论 应用情景 图像数据扫描的难点 在上一篇文章《基本图像容器——Mat》中已经详细描述了OpenCV储存图像数据的形式图像的每个像素储存为一个矩阵中的数据项矩阵的每个数据项包括各个颜色通道的值如RGB3通道包含红、绿、蓝共3个通道的颜色值。所以矩阵的值的列数为矩阵的列数乘以颜色通道数如下图所示OpenCV默认的BGR格式的数据有3个颜色通道所以实际有m*3列数值行数则不变 如果我们使用uchar8bits类型去储存每个像素的值那么像素的每个颜色通道可以有256个可能的值 2 8 256 2^8256 28256这样的话如果是3通道的数据那每个数据项就有 25 6 3 16 , 777 , 216 256^316,777,216 256316,777,216种可能的颜色值了。如果矩阵很大那就会给算法的执行带来很大压力。 颜色空间缩减color space reduction 为了减轻扫描图像数据的算法压力可以将现有的颜色值除以某个值从而缩小颜色值的值域。例如将所有0-9的颜色值都用0替代将所有10-19的颜色值都用10替代依此类推。用数学公式来表示 I n e w ( I o l d 10 ) ∗ 10 I_{new} ( \frac{I_{old}} {10})*10 Inew​(10Iold​​)∗10 I n e w I_{new} Inew​缩减之后的颜色值以下简称缩减值 I o l d I_{old} Iold​缩减之前的原始颜色值以下简称原始值 推而广之如果想要应用其他的缩减率比如2也就是说0-1都用0来代替2-3都用1来代替那除数就会变成2将这个除数用 d d d来表示并称之为缩减因子则公式会变成 I n e w ( I o l d d ) ∗ d I_{new} ( \frac{I_{old}} {d})*d Inew​(dIold​​)∗d I n e w I_{new} Inew​缩减之后的颜色值以下简称缩减值 I o l d I_{old} Iold​缩减之前的原始颜色值以下简称原始值 d d d缩减因子 注意uchar类型被整数除了之后得出的结果依然是uchar类型 但是对每个颜色值都执行上述的除法和乘法运算仍然会消耗很多算力。然而由于每个颜色值的值域是有限的比如uchar类型是[0, 256]所以如果能直接计算出所有可能的缩减结果并进行赋值运算会节省很多算力。所以就产生了“查询表” 查询表 查询表就是储存与原始值一一对应的缩减值的数组一维或多维。一旦数据类型确定查询表的大小就确定不变了。比如uchar类型的查询表就只有256个缩减值因为原始值总共就只有256种可能。然后运用这个查询表将每个原始值都替换成查询表中对应的值就不必对每一个原始值都进行缩减计算了而只是简单的查询和赋值这样就能节省算力。而且原始值越多节省效果越好。这就是查询表的优势。 扫描算法 该实例将3种算法放在一个main函数中main函数接收一个参数数组argv[]其中有可以有3个或4个参数 默认参数程序名调试时不需要用户指定图片文件路径缩减因子即上述公式中的 d d d图片读取格式以灰度格式读取则传递“G”如不指定该参数则默认采用BGR格式读取 静态函数help()详细描述了该方法的使用 static void help() {std::cout \n-------------------------------------------------------------------------- endl 这个程序展示如何在OpenCV中扫描图片(cv::Mat) 根据输入图片路径以及缩减因子大于0的整数 endl 这个程序分别使用了C风格方法、迭代器方法、坐标方法和LUT方法进行扫描 endl 参数输入 endl 程序名 图片路径 缩减因子 [G] endl 如果加了参数G则使用灰度格式读入图片 endl -------------------------------------------------------------------------- endl endl; }在main函数中首先对输入的参数进行分析和处理 if (argc 3) {//参数数量小于3个则退出函数并输出提示cout Not enough parameters endl;return -1; }Mat I, J; if (argc 4 !strcmp(argv[3], G)) //当参数数量为4且最后一个参数是G时I imread(argv[1], IMREAD_GRAYSCALE); //以灰度格式读取图片并储存在Mat对象I中 elseI imread(argv[1], IMREAD_COLOR); //否则以BGR格式读取图片并储存在Mat对象I中if (I.empty()) {//如果读取的数据为空则退出函数并输出提示cout The image argv[1] could not be loaded. endl;return -1; }计算查询表 接下来根据传入的参数数组中的第3个参数即argv[2]计算查询表 int divideWith { 0 }; //1-4行将字符串转换成数字并储存在变量divdeWith中作为缩减因子 stringstream s; s argv[2]; s divideWith; if (!s || !divideWith) {//如果无法接收第3个参数或者参数为0则退出函数并输出提示cout Invalid number entered for dividing. endl;return -1; }uchar table[256]; //用一个长度为256的一维数组来储存查询表 for (int i { 0 }; i 256; i) //计算查询表计算结果为uchar类型table[i] static_castuchar(divideWith * (i / divideWith));在将字符串转换成数字的过程中使用了C中的字符串流stringstream字符串流对象s接收参数字符串argv[2]然后将其传给整数变量divideWith作为后面进行缩减运算的缩减因子。 计算查询表的for循环块进行了256次循环将[0, 255]中的所有整数依次进行了缩减运算得出256个uchar类型的缩减值并依次储存在一个uchar类型的数组中。 统计运算时长 这个程序为了比较不同扫描方法的速度使用了使用了OpenCV中的cv::getTickCount()和cv::getTickFrequency()函数进行计时。前者返回某个运行节点的CPU的tick数一个tick为CPU频率的倒数后者返回CPU每秒的tick数。如果获取事件起始点和结束点的CPU的tick数然后用它们的差除以CPU每秒的tick数就能得到事件起始点和结束点之间的时间差单位为秒。具体代码如下 double t { static_castdoublegetTickCount() }; //将返回值类型从原本的int型转换为double型 //扫描函数 t (static_castdoublegetTickCount() - t)/getTickFrequency(); //转换为double型后除法运算结果中的小数就会被保留 cout 用时 t 秒 endl;这几行代码放在每个扫描方法的前后从而能为每个扫描方法计算运行时间便可比较它们的运行速度 连续内存 虽然储存图像数据的Mat对象可能是个二维甚至多维矩阵但是在内存中矩阵是被按行分成若干个一维数组储存的。这些一维数组可能被放在一起形成一个连续的内存空间也可能被分开储存。OpenCV中的cv::Mat::isContinuous()可以判断矩阵在内存中是否是连续的。被储存在一个连续内存中的矩阵扫描起来会更快。 3种扫描方法 如果没耐心对比每种方法的思路可以直接结论部分看每种方法的优缺点然后找到相应方法的章节进行进一步阅读。 但是除了LUT方法前三种方法都需要自己编写将替换原始值的语句该语句中对查询表的索引思路比较绕为了避免重复本文只在第一种方法即C风格的扫描方法中对此进行了详细解释如果看不懂可以到该章节进行参考。 C风格的扫描方法 C风格的扫描方法少不了要进行C风格的二维数组的遍历操作包括行指针、列指针等。 //! [scan-c] Mat ScanImageAndReduceC(Mat I, const uchar* const table) {//只接收uchar类型的矩阵CV_Assert(I.depth() CV_8U); //depth函数返回每个通道的数值的类型如CV_8U为8比特无符号字符串类型int channels { I.channels() }; //channels函数返回矩阵中的颜色通道数int nRows { I.rows };int nCols { I.cols * channels }; //实际列数为矩阵列数*颜色通道数if (I.isContinuous()){//如果是储存在连续空间则按一维数组来处理nCols * nRows; //一维数组的实际列数为原列数*行数nRows 1; //一维数组行数为1}int i, j;uchar* p; //uchar类型的p指针用来储存扫描结果for (i 0; i nRows; i){p I.ptruchar(i); //ptr模板函数根据行数i返回矩阵第i行的行指针并在尖括号中指定返回的指针类型for (j 0; j nCols; j){//原来的p[j]是个列指针实际为矩阵i行j列的值即颜色的原始值//因为查询表示按0-255的顺序排列的//所以以p[j]为下标访问查询表正好能访问到原始值对应的缩减值p[j] table[p[j]]; //将查询表中的缩减值赋值给p指针中对应的位置//由于p是指向Mat对象I中的矩阵的所以I实际上也被更改了}}return I; //返回被更改的I } //! [scan-c]注意该函数传入的第2个参数为uchar类型的常量指针常量即这个指针指向的对象不能被修改指针的地址也不能被修改这样才能保证查询表在函数运行结束之后仍然没变可以被再次利用。 该函数最核心的部分就是对数组进行遍历操作的for循环语句。这里巧妙地使用列指针即原始值作为访问查询表数组的下标从而找到对应的缩减值。具体思路写在注释里了读者可以参阅。 当矩阵是被储存在连续的内存空间中的时候实际上是对一个一维数组进行遍历i为1只循环1次。 如果确定矩阵是储存在连续内存空间中的那么还有另外一种方法可以完成对它的遍历 uchar* p { I.data }; //data是Mat类的public数据成员它储存了Mat对象首行的行指针for(unsinged int i { 0 }; i ncol*nrows; i)//行指针自增之后就变成了列指针即一维数组第i列的地址//再进行解引用操作就得到了第i列的值*p table[*p];迭代器方法 使用迭代器比用数组指针更加方便因为不用考虑行指针、列指针的问题没有嵌套的for循环只需要一层for循环。 但是迭代器只能代替矩阵中的数据项。也就是说如果是单通道的灰度格式的图片数据迭代器正好代替矩阵中的每个颜色值但是如果是3通道的BGR格式的图片数据迭代器实际上代替的是一个长度为3的数组其中包含了3个颜色值分别为蓝、绿、红值在这种情况下还必须对迭代器进行进一步的操作。 //! [scan-iterator] Mat ScanImageAndReduceIterator(Mat I, const uchar* const table) {CV_Assert(I.depth() CV_8U);const int channels{ I.channels() };switch (channels){//switch语句实现对两种情况的分别处理case 1: //单通道的灰度图片数据{MatIterator_uchar it, end; //声明起始和终止迭代器for (it I.beginuchar(), end I.enduchar(); it ! end; it)//begin和end函数分别返回指向第一个和最后一个数据项的迭代器*it table[*it]; //对迭代器进行解引用操作可获得对应的数据项break;}case 3: //3通道的BGR图片数据{MatIterator_Vec3b it, end;for (it I.beginVec3b(), end I.endVec3b(); it ! end; it){//还需要对迭代器中的3个值分别进行赋值操作//*it[0]为数据项在第一个颜色通道的值(*it)[0] table[(*it)[0]];(*it)[1] table[(*it)[1]];(*it)[2] table[(*it)[2]];}}}return I; } //! [scan-iterator]如果在3通道的情况没有对迭代器中的数组进行进一步的操作的话那改变的只是每个像素的蓝色值。因为OpenCV将RGB转换成了BGR第一个值是蓝色值。 坐标方法 这种方法其实一般是用来确定需要某个数据项的行数和列数的也称为随机获取所以并不建议用来扫描图像数据。 这种方法也需要对单通道和3通道数据进行分情况的处理。 在单通道数据中该方法运用了数据项的动态地址并返回数据项的引用这由函数cv::Mat::at()来实现 在3通道数据中该方法运用了Mat_类型达到了同样的目的。/Mat_类型为储存了每个数据项的类型信息的Mat类型Mat_类型可以用(row, col)方式来访问这种访问等同于在Mat类型中用at(row, col)来进行访问 具体解释见代码注释 //! [scan-random] Mat ScanImageAndReduceRandomAccess(Mat I, const uchar* const table) {CV_Assert(I.depth() CV_8U);const int channels{ I.channels() };switch (channels){case 1:{for (int i{ 0 }; i I.rows; i)for (int j{ 0 }; j I.cols; j)//at函数获取矩阵在i行j列的数据项的地址并返回它的引用I.atuchar(i, j) table[I.atuchar(i, j)];break;}case 3:{//Vec3b类型为OpenCV中定义的3字节数据类型用来储存长度为3的uchar类型数组正好Mat_Vec3b _I I; //这里不能用C20的初始化语法会报错无法转化参数类型for (int i{ 0 }; i I.rows; i)for (int j{ 0 }; j I.cols; j){_I(i, j)[0] table[_I(i, j)[0]];_I(i, j)[1] table[_I(i, j)[1]];_I(i, j)[2] table[_I(i, j)[2]];}I _I;break;}}return I; } //! [scan-random]LUT方法 在OpenCV的core模块有一个专门用来修改图片数据矩阵中的值的方法cv::LUT()。其具体用法如下 //! [table-init] Mat lookUpTable(1, 256, CV_8U); //用Mat构造函数创建uchar类型的1维矩阵 uchar* p lookUpTable.ptr(); //获取矩阵的首地址 for (int i 0; i 256; i)p[i] table[i]; //将之前计算的查询表中的数值复制到矩阵中 //! [table-init]//! [table-use] LUT(I, lookUpTable, J); //! [table-use]cv::LUT()函数使用了3个参数 需要进行修改的原始矩阵输入矩阵查询表矩阵接收修改结果的矩阵输出矩阵 该函数没有返回值。只用一条语句就实现了用查询表矩阵中的缩减值替换输入矩阵中的原始值然后输出替换结果。 算法效率对比 将3种方法整合在一个cpp文件中并统计每个方法的运算时长。整合后的代码如下 #include opencv2/core.hpp #include opencv2/core/utility.hpp #include opencv2/imgcodecs.hpp #include opencv2/highgui.hppimport iostream; import sstream;using namespace cv; using namespace std;static void help() {std::cout \n-------------------------------------------------------------------------- endl 这个程序展示如何在OpenCV中扫描图片(cv::Mat) 根据输入图片路径以及缩减因子大于0的整数 endl 这个程序分别使用了C风格方法、迭代器方法、坐标方法和LUT方法进行扫描 endl 参数输入 endl 程序名 图片路径 缩减因子 [G] endl 如果加了参数G则使用灰度格式读入图片 endl -------------------------------------------------------------------------- endl endl; }Mat ScanImageAndReduceC(Mat I, const uchar* table); Mat ScanImageAndReduceIterator(Mat I, const uchar* table); Mat ScanImageAndReduceRandomAccess(Mat I, const uchar* table);int main(int argc, char* argv[]) {help();if (argc 3){std::cout 缺少参数 endl;return -1;}Mat I, J;if (argc 4 !strcmp(argv[3], G))I imread(argv[1], IMREAD_GRAYSCALE);elseI imread(argv[1], IMREAD_COLOR);if (I.empty()){std::cout 图片 argv[1] 打不开。 endl;return -1;}//! [dividewith]int divideWith{ 0 };stringstream s;s argv[2];s divideWith;if (!s || !divideWith){std::cout 无效缩减因子。 endl;return -1;}uchar table[256];for (int i{ 0 }; i 256; i)table[i] static_castuchar(divideWith * (i / divideWith));//! [dividewith]const int times{ 100 };double t;t static_castdouble(getTickCount());for (int i{ 0 }; i times; i){Mat clone_i{ I.clone() };J ScanImageAndReduceC(clone_i, table);}t 1000 * (static_castdouble(getTickCount()) - t) / getTickFrequency();t / times;std::cout C风格方法每运行 times 次平均耗时 t 毫秒。 endl;t static_castdouble(getTickCount());for (int i{ 0 }; i times; i){Mat clone_i{ I.clone() };J ScanImageAndReduceIterator(clone_i, table);}t 1000 * (static_castdouble(getTickCount()) - t) / getTickFrequency();t / times;std::cout 迭代器方法每运行 times 次平均耗时 t 毫秒。 endl;t static_castdouble(getTickCount());for (int i{ 0 }; i times; i){Mat clone_i{ I.clone() };ScanImageAndReduceRandomAccess(clone_i, table);}t 1000 * (static_castdouble(getTickCount()) - t) / getTickFrequency();t / times;std::cout 坐标方法每运行 times 次平均耗时 t 毫秒。 endl;//! [table-init]Mat lookUpTable(1, 256, CV_8U);uchar* p{ lookUpTable.ptr() };for (int i{ 0 }; i 256; i)p[i] table[i];//! [table-init]t static_castdouble(getTickCount());for (int i{ 0 }; i times; i)//! [table-use]LUT(I, lookUpTable, J);//! [table-use]t 1000 * (static_castdouble(getTickCount()) - t) / getTickFrequency();t / times;std::cout LUT方法每运行 times 次平均耗时 t 毫秒。 endl;return 0; }//! [scan-c] Mat ScanImageAndReduceC(Mat I, const uchar* const table) {// accept only char type matricesCV_Assert(I.depth() CV_8U);int channels{ I.channels() };int nRows{ I.rows };int nCols{ I.cols * channels };if (I.isContinuous()){nCols * nRows;nRows 1;}int i, j;uchar* p;for (i 0; i nRows; i){p I.ptruchar(i);for (j 0; j nCols; j){p[j] table[p[j]];}}return I; } //! [scan-c]//! [scan-iterator] Mat ScanImageAndReduceIterator(Mat I, const uchar* const table) {// accept only char type matricesCV_Assert(I.depth() CV_8U);const int channels{ I.channels() };switch (channels){case 1:{MatIterator_uchar it, end;for (it I.beginuchar(), end I.enduchar(); it ! end; it)*it table[*it];break;}case 3:{MatIterator_Vec3b it, end;for (it I.beginVec3b(), end I.endVec3b(); it ! end; it){(*it)[0] table[(*it)[0]];(*it)[1] table[(*it)[1]];(*it)[2] table[(*it)[2]];}}}return I; } //! [scan-iterator]//! [scan-random] Mat ScanImageAndReduceRandomAccess(Mat I, const uchar* const table) {// accept only char type matricesCV_Assert(I.depth() CV_8U);const int channels{ I.channels() };switch (channels){case 1:{for (int i{ 0 }; i I.rows; i)for (int j{ 0 }; j I.cols; j)I.atuchar(i, j) table[I.atuchar(i, j)];break;}case 3:{Mat_Vec3b _I I;for (int i{ 0 }; i I.rows; i)for (int j{ 0 }; j I.cols; j){_I(i, j)[0] table[_I(i, j)[0]];_I(i, j)[1] table[_I(i, j)[1]];_I(i, j)[2] table[_I(i, j)[2]];}I _I;break;}}return I; } //! [scan-random] 要调试程序需要输入参数的main函数。在VS中可以在项目属性中提前输入参数如下图中黑体的th.jpg 10就是用空格隔开的两个参数。注意第一个参数可以不用输入默认为项目程序名。 我这里使用的th.jpg是一个1920*1200的图片。这是为了测试处理比较大的图片的时候各种方法的性能。 调试运行结果为 结论 从运行结果可以看到用时最短的是LUT方法这是因为OpenCV库使用了多线程方法加快了运行速度。但这并不代表LUT方法永远是最好的其他方法的优缺点如下 给简单的小图片写扫描程序的时候用C风格的数组方法更好因为没必要动用多线程迭代器方法更安全但是相对较慢坐标的方法需要获取动态引用是在调试模式中耗时最多的方法但是在发行模式中可能会比迭代器方法更快不过肯定没有迭代器方法安全 ** 文章较长不免有遗漏或笔误欢迎大家指正**
http://www.w-s-a.com/news/381543/

相关文章:

  • 韶关网站推广做网站要哪些人员
  • 建设银行网站链接开发公司与物业公司交接清单
  • 网站定位广告企业建网站有这个必要吗
  • 网站模板 商标黄冈建设工程信息网
  • 做鞋子的招聘网站有哪些微网站
  • 项目网站开发建网站 多少钱
  • wordpress做门户seo培训价格
  • 百度关键词优化软件如何wordpress站点地图优化
  • 使用cnnic证书的网站营销公司有哪些
  • 做电子杂志用什么网站如何将网站生成二维码
  • 三点水网站建设洛阳市建设厅网站
  • 哪家做网站便宜网络推广培训吧
  • 网站怎么做支付非凡软件站
  • 北京谁会做网站开发熊岳网站怎么做
  • 南阳哪有做网站公司定制网站本地企业
  • 平板上做网站的软件邀约网站怎么做请柬
  • 企业网站成品源码邯郸做网站流程
  • 建设网站需要什么技术两个网站放在同一个服务器 备案
  • 焦作做网站推广天津网络关键词排名
  • siteserver cms网站访问量挂机宝可以做网站吗
  • 普宁网站建设公司网络商城设计
  • wordpress主题 外贸网站wordpress安装后输入帐号登不进去
  • 陕西省西安市建设局网站永登网站设计与建设
  • 广东网站设计招工.免费咨询贷款
  • 做试题网站在线做c 题的网站
  • 青岛发现51例阳性南京专业网站优化公司
  • 南昌建站如何上wordpress
  • 洛阳网站建设优惠公司建筑企业上市公司有哪些
  • 营销型网站建设营销型网站建设手机网站设计需要学什么
  • 在线视频网站 一级做爰片南通网站建设找哪家