发表于 2020-6-22 10:58

申请会员ID:autumoon【申请通过】

1、申 请 I D:autumoon
2、个人邮箱:autumoon#vip.qq.com3、原创技术文章:本人测绘行业,做图像处理的,主攻c++ 图像增强,识别 算法等,业余也写一些图像处理小工具,下面展示一下之前做过的自动识别影像中的房屋,道路,绿植,水域的软件。 如下图,需要在正射影像中使用图像自动识别算法,识别出道路,房屋,绿植,水域等。https://img-blog.csdnimg.cn/20200622104647990.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2F1dHVtb29uY2hpbmE=,size_16,color_FFFFFF,t_70

简单说下方法,实际上肯定比较复杂,道路的特点是比较长,直线,通常亮度比较高等;而房屋通常是矩形,蓝色和红色的屋顶用颜色就比较好识别;至于绿植,主要靠颜色,一般直接使用rgb模型,g分量大到一定程度即可。


先看下效果,也有部分识别错误:
https://img-blog.csdnimg.cn/20200622104712686.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2F1dHVtb29uY2hpbmE=,size_16,color_FFFFFF,t_70


核心检测代码:
double IsDefAreaEx(unsigned char* pBuffer, const int& nDetectIndex, CAreaLinesEx* pInput, CAreaLinesEx* pSample /*= nullptr*/, double lfGSD /*= 1.0*/)
{
      //输出用
      double dResult = 0.0;
      
      int nPartialRGBIndex = pInput->GetPartialRGB();

      //获得基础参数
      double dArea = pInput->GetArea();
      double dRectArea = pInput->BoundingRectArea();
      double dVariance = pInput->GetVarianceRef();

      if (dArea < 1e-5 || dRectArea < 1e-5)
      {
                return 0.0;
      }

      //计算区域的部分参数
      CAreaLinesEx ale(*pInput);      //必须备份,不然会改变原来的值
      CAreaRect arc = ale.GetBoundingRect();
      int left = arc.left;
      int down = arc.down;
      int buffer_width = arc.Width();
      int buffer_height = arc.Height();
      if (buffer_width > 10000 || buffer_height > 10000)
      {
                return dResult;
      }
      std::vector<std::vector<cv::Point>> vContours;
      //当前区域索引
      int nFindIndex = -1;
      //直方图统计的宽度
      double dHistWidth = 1.0;
      //区域边界,检测直线的数目
      int nFindLines = 0;
      std::vector<cv::Vec4i> lines;

      //绿植无需检测
      if (nDetectIndex != 1)
      {
                //偏移区域
                ale.Offset(-arc.left, -arc.down, true);
                memset(pBuffer, 0, buffer_width * buffer_height * sizeof(unsigned char));
                ale.WriteBoundBuffer(pBuffer, buffer_width, buffer_height);
                cv::Mat mSrcGray = COpencvUser::TransBufferToMat(pBuffer, buffer_width, buffer_height, 1, 1, false);

                //外扩两个像素,方便检测边缘直线
                cv::Mat mSrcGrayExpand = cv::Mat::zeros(cv::Size(mSrcGray.cols + 4, mSrcGray.rows + 4), mSrcGray.type());
                mSrcGray.copyTo(mSrcGrayExpand(cv::Rect(2,2, mSrcGray.cols, mSrcGray.rows)));
                mSrcGray = mSrcGrayExpand;

                size_t nAreaCount = COpencvUser::GetContours(mSrcGray.clone(), vContours);
                if (nAreaCount)
                {
                        double dMaxArea = 0.0;
                        for (size_t i = 0; i < nAreaCount; ++i)
                        {
                              if (vContours.size() < 4)
                              {
                                        continue;
                              }

                              double dContourArea = cv::contourArea(vContours);

                              if (nFindIndex != -1)
                              {
                                        if (dContourArea > dMaxArea)
                                        {
                                                dMaxArea = dContourArea;
                                                nFindIndex = i;
                                        }
                              }
                              else
                              {
                                        dMaxArea = dContourArea;
                                        nFindIndex = i;
                              }
                        }
                }


                //在区域中搜寻直线
                //cv::Mat mShow;
                //const int nRProp = 3;
                //cv::resize(mSrcGray, mShow, cv::Size(mSrcGray.cols * nRProp, mSrcGray.rows * nRProp));
                //cv::imshow("D:\\hough.tif", mShow);
                cv::Canny(mSrcGray, mSrcGray, 100.0, 200.0);
                //cv::resize(mSrcGray, mShow, cv::Size(mSrcGray.cols * nRProp, mSrcGray.rows * nRProp));
                //cv::imshow("D:\\canny.tif", mShow);
                nFindLines = (int)COpencvUser::HoughLinesProb(mSrcGray.clone(), lines, 15, 2 / lfGSD, 0);

                //cv::Mat mSrcBGR;
                //cv::cvtColor(mSrcGray, mSrcBGR, CV_GRAY2BGR);
                //COpencvUser::HoughLinesShow(mSrcBGR, lines);
                //cv::resize(mSrcBGR, mShow, cv::Size(mSrcGray.cols * nRProp, mSrcGray.rows * nRProp));
                //cv::imshow("D:\\lines.tif", mShow);
                //cv::waitKey();

                //求得截断长度
                if (nFindIndex != -1 && vContours.size() > 4)
                {
                        cv::RotatedRect roRc = cv::fitEllipse(vContours);
                        double dAngle = roRc.angle;
                        //cv::imwrite("D:\\area.tif", mSrcGray);
                        cv::Point2f VerPts;
                        roRc.points(VerPts);
                        double dGap1 = sqrt((VerPts.x - VerPts.x)*(VerPts.x - VerPts.x) + (VerPts.y - VerPts.y)*(VerPts.y - VerPts.y));
                        double dGap2 = sqrt((VerPts.x - VerPts.x)*(VerPts.x - VerPts.x) + (VerPts.y - VerPts.y)*(VerPts.y - VerPts.y));

#ifdef OUTPUT_LINE_IMG
                        cv::Mat mLineBGR;
                        cv::cvtColor(mSrcGray, mLineBGR, CV_GRAY2BGR);
                        cv::RNG rng;
                        for (size_t i = 0; i < 4; ++i)
                        {
                              cv::Point p1 = VerPts;
                              cv::Point p2 = VerPts[(i + 1) % 4];
                              cv::line(mLineBGR, p1, p2, cv::Scalar(rng.uniform(0.0, 255.0),rng.uniform(0.0, 255.0),rng.uniform(0.0, 255.0)));
                        }
                        cv::imwrite("D:\\areaR.tif", mLineBGR);
                        static int nTimes = 0;
                        nTimes++;
#endif // OUTPUT_LINE_IMG

                        cv::Rect bdRc = roRc.boundingRect();
                        if (dGap1 > 10.0)
                        {
                              //用得到的角度,切多边形,得到的线段宽度,求直方图统计值,中间的80%的数据的平均值
                              CSegment sg(VerPts.x, VerPts.y, VerPts.x, VerPts.y);
                              std::vector<POINT32F> vRasterPts = sg.Raster(2.0); //以像素为单位
                              std::vector<CSegment> vGapLines;
                              for (size_t i = 0; i < vRasterPts.size(); ++i)
                              {
                                        double a = 0.0, b = 0.0, c = 0.0;
                                        CSegment::CalcLineABC(dAngle, vRasterPts.x, vRasterPts.y, a, b, c);

                                        POINT32F pt1, pt2;
                                        CSegment::IsLineInterRect(a, b, c, MyRect(bdRc.x, bdRc.y, bdRc.width, bdRc.height), pt1, pt2);

                                        CSegment sgCut(pt1, pt2);
                                        std::vector<POINT32F> vRasterCutPts = sgCut.Raster();
                                        bool bFindStart = false;
                                        POINT32F ptStart, ptEnd;
                                        size_t nRasterCutPtCount = vRasterCutPts.size();
                                        for (size_t j = 0; j < nRasterCutPtCount; ++j)
                                        {
                                                if (cv::pointPolygonTest(vContours, cv::Point2f(vRasterCutPts.x, vRasterCutPts.y), false) >= 0)
                                                {
                                                      if (!bFindStart)
                                                      {
                                                                bFindStart = true;
                                                                ptStart = vRasterCutPts;
                                                      }

                                                      if (bFindStart && j == nRasterCutPtCount - 1)
                                                      {
                                                                ptEnd = vRasterCutPts;
                                                                vGapLines.push_back(CSegment(ptStart, ptEnd));
                                                      }
                                                }
                                                else
                                                {
                                                      if (bFindStart)
                                                      {
                                                                ptEnd = vRasterCutPts;
                                                                vGapLines.push_back(CSegment(ptStart, ptEnd));
                                                                bFindStart = false;
                                                      }
                                                }
                                        }
                                       
                              }

                              std::multimap<double, int> mdiGap;
                              for (size_t j = 0; j < vGapLines.size(); ++j)
                              {
                                        double leng = vGapLines.Length();
                                        mdiGap.insert(std::make_pair(leng, j));
#ifdef OUTPUT_LINE_IMG
                                        cv::line(mLineBGR, cv::Point(vGapLines.x1, vGapLines.y1), cv::Point(vGapLines.x2, vGapLines.y2),
                                                cv::Scalar(rng.uniform(0.0, 255.0),rng.uniform(0.0, 255.0),rng.uniform(0.0, 255.0)));
#endif // OUTPUT_LINE_IMG
                              }

                              //转换map为vector
                              std::vector<double> vLength;
                              size_t nLineCount = mdiGap.size();
                              vLength.reserve(nLineCount);
                              for (auto it = mdiGap.begin(); it != mdiGap.end(); ++it)
                              {
                                        vLength.push_back(it->first);
                              }

                              size_t idxStart = nLineCount * 0.1;
                              size_t idxEnd = nLineCount * 0.9;

                              double dHistLength = 0.0;
                              for (size_t j = idxStart; j < idxEnd; ++j)
                              {
                                        dHistLength += vLength;
                              }
                              dHistLength /= idxEnd - idxStart + 1;

                              dHistWidth = dHistLength * lfGSD;

                              //此时,更新旋转矩形信息
                              pInput->m_ri.bUseful = true;
                              pInput->m_ri.dHistLength = dHistLength;
                              pInput->m_ri.dRotateRectLength = dGap1;
                              pInput->m_ri.dRotateRectWidth = dGap2;
                              pInput->m_ri.nHistLengthCount = idxEnd - idxStart + 1;
                        }
                        else
                        {
                              dHistWidth = MIN(dGap1, dGap2) * lfGSD;

                              //此时,更新旋转矩形信息
                              pInput->m_ri.bUseful = true;
                              pInput->m_ri.dHistLength = MIN(dGap1, dGap2);
                              pInput->m_ri.dRotateRectLength = dGap1;
                              pInput->m_ri.dRotateRectWidth = dGap2;
                              pInput->m_ri.nHistLengthCount = MAX(dGap1, dGap2);
                        }

#ifdef OUTPUT_LINE_IMG
                        cv::imwrite("D:\\areaL.tif", mLineBGR);
#endif // OUTPUT_LINE_IMG
                }
      }

      switch (nDetectIndex)
      {
      case DETECT_HOUSE:
                {
                        //房屋,必须存在至少一组平行或者垂直的线段
                        size_t nPPLines = ParallelOrPerpendLines(lines);
                        if (nPPLines > 0)
                        {
                              //如果检测多组直角,并且他们能够对应起来(平行或者垂直),则更容易是房屋,提高可能性
                              //房屋的实际面积
                              double dRealArea = dArea * lfGSD * lfGSD;
                              double dAreaProbablity = 1.0;
                              double dHouseWidthProb = 1.0;
                              double dLengthWidthProb = 1.0;
                              double dVarianceProb = 1.0;
                              //房屋面积在10-200的范围外,则降低其可能性
                              if (dRealArea < 10.0)
                              {
                                        dAreaProbablity = dRealArea / 10.0;
                              }
                              else if (dRealArea > 200.0)
                              {
                                        dRealArea = 1000.0 / dRealArea;
                              }

                              double dGrayValueProb = 1.0;
                              //如果亮度小于30,则越小越不可能是房子
                              std::vector<uchar> vRGB = CAreaLinesEx::VectorHSV2RGB(ale.GetAvgHSVRef());
                              uchar uValue = (vRGB + vRGB + vRGB) / 3;

                              if (uValue < 30)
                              {
                                        dGrayValueProb = uValue / 30.0;
                              }

                              if (nFindIndex != -1 && vContours.size() > 4)
                              {
                                        cv::RotatedRect roRc = cv::fitEllipse(vContours);
                                        cv::Point2f VerPts;
                                        roRc.points(VerPts);

                                        double dGap1 = sqrt((VerPts.x - VerPts.x)*(VerPts.x - VerPts.x) + (VerPts.y - VerPts.y)*(VerPts.y - VerPts.y));
                                        double dGap2 = sqrt((VerPts.x - VerPts.x)*(VerPts.x - VerPts.x) + (VerPts.y - VerPts.y)*(VerPts.y - VerPts.y));
                                        double droRcArea = dGap1 * dGap2;

                                        //旋转矩形占比
                                        double dAreaRectProb = dArea / droRcArea;

                                        if (dAreaRectProb < 0.2)
                                        {
                                                return dResult;
                                        }

                                        //房屋宽度不应该介于一定的范围
                                        if (dHistWidth < 1.0)
                                        {
                                                dHouseWidthProb = dHistWidth / 1.0;
                                        }
                                        else if (dHistWidth > 10.0)
                                        {
                                                dHouseWidthProb = 10.0 / dHistWidth;
                                        }

                                        //如果区域的长度和宽度的比值过大,则降低可能性
                                        double dLengWidthProp = dGap1 * lfGSD / dHistWidth;
                                        if (dLengWidthProp > 5.0)
                                        {
                                                dLengthWidthProb = 5.0 / dLengWidthProp;
                                        }
                                        //CLOG::Out(_T("dLengthWidthProb = %lf"), dLengthWidthProb);
                                        dResult = dAreaRectProb * dAreaProbablity * dHouseWidthProb /** dLengthWidthProb*/;
                                        //直接增加概率
                                        if (dResult < 0.5 && nPPLines > 1)
                                        {
                                                dResult += 0.5;
                                        }

                                        return dResult;
                              }

                              //正矩形占比
                              return (dArea / dRectArea) * dAreaProbablity * dHouseWidthProb * dGrayValueProb;
                        }
                }
                break;
      case DETECT_GREEN:
                {
                        //绿植
                        if (nPartialRGBIndex == 1 && dVariance > 60.0)
                        {
                              //可以增加平行直线的检测

                              return 1.0;
                        }
                }
                break;
      case DETECT_WATER:
                {
                        //水域
                        if (nPartialRGBIndex != -1)
                        {
                              return dResult;
                        }

                        //水域方差通常较小,而面积比较大,截断距离也较大
                        double dVarianceProb = 1.0;
                        if (dVariance > 20.0)
                        {
                              dVarianceProb = 20 / dVariance;
                        }

                        //面积,小于40则降低可能性
                        //水域的实际面积
                        double dRealArea = dArea * lfGSD * lfGSD;
                        double dAreaProbablity = 1.0;
                        //水域面积,优先考虑40.0以上的区域
                        if (dRealArea < 40.0)
                        {
                              dAreaProbablity = dRealArea / 40.0;
                        }

                        //水域的截断长度,通常较大
                        double dHistWidthProb = 1.0;

                        //截断宽度如果小于30m,则降低可能性
                        if (dHistWidth < 30.0)
                        {
                              dHistWidthProb = dHistWidth / 30.0;
                        }

                        return dVarianceProb * dAreaProbablity * dHistWidthProb;

                }
                break;
      case DETECT_ROAD:
                {
                        if (nPartialRGBIndex != -1 || nFindLines < 2)
                        {
                              return dResult;
                        }

                        //至少存在一条接近平行的线段组
                        std::vector<cv::Vec4i> vParaLines = ParallelLines(lines);
                        size_t nParaLineCount = vParaLines.size();
                        if (nParaLineCount == 0)
                        {
                              return dResult;
                        }

                        //判断检测到的直线中,至少存在两条直线接近平行
                        double dSumLength = 0.0;
                        for (size_t i = 0; i < nParaLineCount; ++i)
                        {
                              dSumLength += V4iLength(vParaLines);
                        }

                        double dRealLength = dSumLength * lfGSD / nParaLineCount;

                        //道路检测到的平行直线组长度和
                        double dLengthProb = 1.0;

                        if (dRealLength < 10)
                        {
                              dLengthProb = dRealLength / 10;
                        }

                        //道路宽度比重
                        double dWidthProbability = 1.0;
                        //区域面积和外界旋转矩形的面积比
                        double dAreaPropProbablity = 1.0;
                        //旋转矩形的宽高比例
                        double drcLengthProb = 1.0;
                        double dLengWidthPropProbility = 1.0;
                        if (nFindIndex != -1 && vContours.size() > 4)
                        {
                              cv::RotatedRect roRc = cv::fitEllipse(vContours);
                              cv::Point2f VerPts;
                              roRc.points(VerPts);

                              double dGap1 = sqrt((VerPts.x - VerPts.x)*(VerPts.x - VerPts.x) + (VerPts.y - VerPts.y)*(VerPts.y - VerPts.y));
                              double dGap2 = sqrt((VerPts.x - VerPts.x)*(VerPts.x - VerPts.x) + (VerPts.y - VerPts.y)*(VerPts.y - VerPts.y));

                              double droRcArea = dGap1 * dGap2;
                              dAreaPropProbablity = 1 - dArea / droRcArea;

                              //需要重新求得道路的统计宽度
                              double dLength = MAX(dGap1, dGap2) * lfGSD;
                              double dLengWidthProp = MAX(dGap1, dGap2) / dHistWidth;

                              if (dLength < 50.0)
                              {
                                        drcLengthProb = dLength/ 50.0;
                              }

                              //如果小于1m则减小可能性
                              if (dHistWidth < 1.0)
                              {
                                        dWidthProbability = dHistWidth;
                              }
                              else if (dHistWidth > 20.0)
                              {
                                        dWidthProbability =20.0 / dHistWidth;
                              }

                              //长宽比例越长,越可能是道路
                              if (dLengWidthProp < 3.0)
                              {
                                        dLengWidthPropProbility = dLengWidthProp / 3.0;
                              }
                        }

#ifdef PROBABLITY_LOG
                        //输出各概率到log
                        CLOG::Out(_T("dLengthProb = %f dWidthProbability = %f dAreaPropProbablity = %f drcLengthProb = %f dLengWidthPropProbility = %f IsRoadArea = %f"),
                              dLengthProb, dWidthProbability, dAreaPropProbablity, drcLengthProb, dLengWidthPropProbility, pInput->IsRoadArea(pSample, lfGSD));
#endif // PROBABLITY_LOG

                        return dLengthProb * dWidthProbability * dAreaPropProbablity * drcLengthProb * dLengWidthPropProbility * pInput->IsRoadArea(pSample, lfGSD);
                }
                break;
      default:
                break;
      }

      return dResult;
}

另外,也展示一个业余写的图像处理小工具,本工具可以显示16位彩色影像(一般的图像处理工具只支持8位),还有快速分类图片等文件,改变尺寸,克隆EXIF,转换图片格式等,采用了多线程处理,支持gif动画的缩放和去色。
https://img-blog.csdnimg.cn/20200622104838443.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2F1dHVtb29uY2hpbmE=,size_16,color_FFFFFF,t_70
最新版软件下载地址:http://pan.baidu.com/s/1eSPG4RS


PictureTools功能简介,快速分类图片等文件,改变尺寸,克隆EXIF,转换图片格式等,采用了多线程处理,支持gif动画的缩放和去色。

详细帖子与教程:https://tieba.baidu.com/p/5915492672


clone exif:
https://imgsa.baidu.com/forum/w%3D580/sign=bfe48ab916d5ad6eaaf964e2b1ca39a3/627d229759ee3d6d380436b64e166d224e4ade49.jpg
还有其他小工具,都免费下载,简单说明一下
CreateTif                创建任意颜色任意大小的tif文件。
GrabFilesFromFilters            通过vs工程中的.filters文件获取对应的文件
GrabHeadFiles                通过cpp等文件,获取必要的头文件等
RenameByTxt                通过txt重命名文件夹下的文件,如果是音乐文件会连带修改标题
VideoClassify                按照视频的分辨率对视频进行分类
VsProjectCleaner            清理vs工程目录

本次更新日志:
1.新增了 视频按照尺寸(也就是分辨率)分类 小工具

历史日志
//针对PictureTools
1.缩放单张gif动画的时候,增加了进度显示功能
2.优化了调整分辨率时自动输出的参数

1.增加了固定宽高的功能
2.增加了去掉颜色的功能

1增加了对输出JPG图像质量的设置
2.去掉了gdal相关的组件,采用纯 CxImage组件

1.增加了转换格式的功能,并提供保持文件EXIF信息和修改时间的功能
2.优化了代码,提高了处理文件的速度

1.增加了多文件和多文件夹拖拽到软件界面的功能
2.增加了多文件和多文件夹拖拽到exe文件或者软件快捷方式的功能

希望申请能够通过,谢谢版主!


Hmily 发表于 2020-6-23 10:46

I D:autumoon
邮箱:autumoon#vip.qq.com

申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。

发表于 2020-6-23 12:40

已经登录啦,需要两个小时之后才能回复,谢谢版主通过申请,稍后我用帐号登录回帖。

autumoon 发表于 2020-6-23 13:09

Hmily 发表于 2020-6-23 10:46
I D:autumoon
邮箱:autumoon#vip.qq.com



来报道啦,谢谢版主通过申请!
页: [1]
查看完整版本: 申请会员ID:autumoon【申请通过】