最近在做项目,涉及到车道的检测。由于在日光下,原本就是白色或者是黄色的车道线会比较得很不清晰,于是很自然的想到了灰度拉伸的方案,从少量数据来看的结果,是好的。以下图为例,左图是三通道图,中间是利用opencv的cvtColor函数转换的灰度图,可以看到在右侧日光照射下的车道显得很不清晰,所以对高灰度值区域进行灰度拉伸,可以得到有图,显然车道清晰了很多。虽然由此左侧的车道没有原本那么明显了,但也还在可以接受的范围。

         然后今天在探索能否实现自适应的拉伸方案时,用了自己手机拍摄的一张图片观察处理前后的灰度直方图,发现了一些特殊的情况。绘制灰度直方图和实现灰度拉伸的函数如下。

void GrayLinearTransform(Mat input, Mat &output, double x1 = 160, double y1 = 120, double x2 = 220, double y2 = 240);
void CheckGrayHist(Mat img)
{if (img.channels() == 3) {/*三通道图像,暂时不符合我们单通道灰度直方的需求.*/cout << "Error" << endl;}else {/*图像是单通道的*///Mat gray;//cvtColor(img, gray, COLOR_BGR2GRAY);int histsize = 256;float range[] = { 0, 256 };const float * histrange(range);//存放直方图的计算结果,是一个256*1的矩阵Mat gray_hist;calcHist(&img, 1, { 0 }, Mat(), gray_hist, 1, &histsize, &histrange, true, false);//cout << gray_hist.cols << "," << gray_hist.rows << endl;//定义每个灰度值在图上的宽度int bin_w = 4;int histImageHeight = 500;int histImageWidth = bin_w*histsize;//画出来是1024x500的图片,定义的时候是先行后列,与后续读取数值是不同的!!Mat histImage(histImageHeight, histImageWidth, CV_8UC3, Scalar(0, 0, 0));//对结果进行归一化,才可以绘制到图像中normalize(gray_hist, gray_hist, 0, histImageHeight, NORM_MINMAX, -1, Mat());for (int i = 1; i < histsize; i++){//绘制直方图,用折线图来替代通常的直方图,意义是一致的line(histImage, Point(bin_w *(i - 1), histImageHeight - cvRound(gray_hist.at<float>(i - 1))),Point(bin_w * i, histImageHeight - cvRound(gray_hist.at<float>(i))), Scalar(255, 255, 255), 2);}//打印图片namedWindow(OUTPUT_TITLE, WINDOW_NORMAL);imshow(OUTPUT_TITLE, histImage);//imwrite("hist01.jpg", histImage);waitKey(0);destroyAllWindows();}
}
void GrayLinearTransform(Mat input, Mat & output, double x1, double y1, double x2, double y2)
{/*灰度拉伸。经过几次试验发现,车道线无论是白色还是黄色,总会是图像中的高亮区域,无论白天或者是有路灯照射的晚上,所以可以把拉伸区域定在高灰度值领域。我们将通过两个点(x1, y1), (x2, y2),其中 255 > x2 > x1, 255 > y2 > y1来确定我们的分段函数的新形式。|  y1/x1*old_gray_value, if old_gray_value <= x1new_gray_value =     |  (y2 - y1)/(x2 - x1)*(old_gray_value - x1) + y1, if x1 < old_gray_value <= x2|  (255 - y2)/(255 - x2)*(old_gray_value - x2) + y2, if x2 < old_gray_value*/for (int col = 0; col < input.cols; ++col){for (int row = 0; row < input.rows; ++row){//ogv = old gray value, 即原图的灰度值double ogv = input.at<uchar>(col, row);if (ogv < x1) {output.at<uchar>(col, row) = static_cast<uchar>(y1 / x1 * ogv);}else if (x1 <= ogv && ogv < x2) {output.at<uchar>(col, row) = static_cast<uchar>((y2 - y1) / (x2 - x1)*(ogv - x1) + y1);}else {output.at<uchar>(col, row) = static_cast<uchar>((255 - y2) / (255 - x2)*(ogv - x2) + y2); }}}
}

       将前后两张直方图进行比较如上。我设置的划分多段函数的两个点分别是(160, 120),(220, 240),但从图片上看也确实可以看出把图片的高亮区域给拉伸得更加清晰了,但是直方图上却出现了很多奇怪的波形。虽然说灰度直方图,并不是完全连续的,没有必须是如第一张图那么连续的道理,但是通常来说这种很严重的波动必然是人为干预引起的。

原因也很显然,因为在做灰度拉伸时,是对灰度值带入到函数中,计算的结果基本都不是一个整数,所以需要取整。例如我们设置的两个点是(160, 120),(220, 240),就意味着把0-160的数值压缩到0-120,简单的就是0-4压缩到0-3。[0, 1, 2, 3, 4] ---> [0, 0.75, 1.5, 2.25, 3],如果是简单的向下取整,那么结果是[0, 0, 1, 2, 3],也就是把1的所有值都加到0中去,所以原本连续的0-4就变得不连续了,变得有一个明显的突起。那如果是把小的范围拉伸到大的范围呢?160-220拉伸到120-240,也就是原本60的跨度变成了120,例如160 --> 120, 161 ---> 122,所以121这个位置就空了出来,也就出现了上图中有一段有一半的点为0的区域,正是对应了120-240中的奇数区域。

说不会有什么影响,这个似乎倒不至于。对于肉眼所看到的图像并没有什么特别的边缘或者干扰,兴许有人会说那不要截断用cvRound有没有用?并没有哈哈哈,因为实质上都是把某个灰度值的所有点都给调整到另一个灰度值,只要是这种"全部"的操作,就会对灰度直方图有如上的影响。不过这种图像结果,实际上更多是因为我画图,并不是真正的直方图,而是将一个一个的点连接起来的折线图。如果画成直方图的形式应该就好了。

/*
for (int i = 1; i < histsize; i++)
{    line(histImage, Point(bin_w *(i - 1), histImageHeight - cvRound(gray_hist.at<float>(i - 1))),Point(bin_w * i, histImageHeight - cvRound(gray_hist.at<float>(i))), Scalar(255, 255, 255), 2);
}
*/
for (int i = 0; i < histsize; i++)
{//绘制直方图line(histImage, Point(bin_w * i, histImageHeight),Point(bin_w * i, histImageHeight - cvRound(gray_hist.at<float>(i))), Scalar(255, 255, 255), 2);
}

关于灰度拉伸的一点思考相关推荐

  1. mysql 手动写时间_关于数据库中如何存储时间的一点思考

    1.切记不要用字符串存储日期 我记得我在大学的时候就这样干过,而且现在很多对数据库不太了解的新手也会这样干,可见,这种存储日期的方式的优点还是有的,就是简单直白,容易上手. 但是,这是不正确的做法,主 ...

  2. 对于表列数据类型选择的一点思考

    对于表列数据类型选择的一点思考 简介 SQL Server每个表中各列的数据类型的选择通常显得很简单,但是对于具体数据类型的选择的不同对性能的影响还是略有差别.本篇文章对SQL Server表列数据类 ...

  3. 关于STM32驱动DS1302实时时钟的一点思考

    关于STM32驱动DS1302实时时钟的一点思考 之前用51驱动过DS1302,没用多久就输出了正确的时间.当时以为这块芯片其实没啥,很简单.但是现在用STM32做项目,用到同样的芯片,以为这有何难, ...

  4. 对高并发流量控制的一点思考

    前言 在实际项目中,曾经遭遇过线上5W+QPS的峰值,也在压测状态下经历过10W+QPS的大流量请求,本篇博客的话题主要就是自己对高并发流量控制的一点思考. 应对大流量的一些思路 首先,我们来说一下什 ...

  5. 关于c语言结构体偏移的一点思考

    注:此处只是利用了编译器的特性来计算结构体偏移 这句话就一笔带过,说得有点牵强附会.以后有时间自己再详细了解一下编译器的特性... more exceptional c++ 中文版 26页 https ...

  6. App用户体验的一点思考

    App用户体验的一点思考 最近我在团队中负责TImers4Me这款Android软件的开发.维护和更新,软件每次在市场上的发布都能得到用户一些有价值的反馈,通过收集整理用户们的使用反馈,我们常能看到一 ...

  7. 对高并发流量控制的一点思考 推荐

    前言 在实际项目中,曾经遭遇过线上5W+QPS的峰值,也在压测状态下经历过10W+QPS的大流量请求,本篇博客的话题主要就是自己对高并发流量控制的一点思考. 应对大流量的一些思路 首先,我们来说一下什 ...

  8. 贝特朗奇论 用计算机,关于贝特朗奇论的一点思考

    关于贝特朗奇论的一点思考 贝特朗奇论这个名字就很奇怪,我最开始以为是贝特朗奇的某个论点或者命题,但是百度了一下发现原来是贝特朗(Bertrand)的"奇论",最初用以批判当时尚不严 ...

  9. 对产品质量的一点思考

    不管是做产品还是做项目,也不管是采用瀑布模型还是敏捷开发,我们都有一个终极目标,就是能按时交付质量可靠的功能,其中质量尤为重要. 本文是我对产品质量的一点思考,如果您所在的团队代码质量很高,很少出BU ...

最新文章

  1. 2021年大数据ZooKeeper(六):ZooKeeper选举机制
  2. c++ argmax
  3. rasp 系统_RASP攻防 —— RASP安全应用与局限性浅析
  4. SQL 查询逻辑处理顺序
  5. CSS中display:block、inline、inline-block
  6. 深入了解EntityFramework——数据注解属性
  7. 5种Python使用定时调度任务的方式
  8. Windows 10 设置 Java 环境变量
  9. html之CSS设计(CSS伪类、优先级、字体属性、背景属性)
  10. 以太坊共识引擎源码分析
  11. python.集合转列表_Python列表、元组、字典、集合
  12. python中io中的+模式_Python----文件的IO操作
  13. Thinking in UML 学习笔记(一)——建立对象模型
  14. php diy,PHP DIY 系列------基础篇:1. PSR
  15. 查看 java heapspace_Java heap space 问题查找
  16. iOS中的UISearchBar
  17. 路由跟踪之tcptraceroute IP延时之tcpping
  18. 你真正付出了全部努力了吗?
  19. 计算机ram结构,2017年全国计算机考试四级复习纲要:RAM的结构、组织及其应用...
  20. itext word转pdf,中文标点换行问题

热门文章

  1. 极1s刷Padavan改华硕[N14U N54U]固件
  2. 科普:原型验证系统VS硬件仿真器,哪一个更好?
  3. 积分中的有理函数拆分
  4. 宛如秋兮 - 个人发展规划(职业规划)
  5. PHP 浮点数的加减乘除
  6. 灰色系统理论及其应用 (二) :优势分析
  7. 阿里云服务器的购买和配置以及搭建项目教程
  8. Pick-up sticks POJ - 2653
  9. 湖北简智音:带你走进抖音短视频制作全过程
  10. 初中计算机学业水平考试知识点,初中信息技术学业水平考试知识点