申请会员ID:autumoon【申请通过】
1、申 请 I D:autumoon2、个人邮箱: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文件或者软件快捷方式的功能
希望申请能够通过,谢谢版主!
I D:autumoon
邮箱:autumoon#vip.qq.com
申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。 已经登录啦,需要两个小时之后才能回复,谢谢版主通过申请,稍后我用帐号登录回帖。 Hmily 发表于 2020-6-23 10:46
I D:autumoon
邮箱:autumoon#vip.qq.com
来报道啦,谢谢版主通过申请!
页:
[1]