这几天基础OpenCV,练习写了一个去黑边程序,新手代码,记录一下。
该方法为扫描线法,遇到非黑边内的(0,0,0)黑色时,用坐标排除(不在边界上跳过)。
PS:这里还提供一种区域增长思路,找图片黑色区域,面积最大的区域为需要去除的黑边。
基本思路
- 遍历导入图片,遍历像素,找到黑边所在的矩形框坐标,剪切图片(一分为四)。
- 根据矩形坐标,计算新的地理位置信息。
- 删除带有黑边的图片和tfw文件信息
- 打开和写入保存tif图片和tfw位置信息
找到黑边所在的矩形区域,分割图片。
//传入图片 返回图片分割的图片 得到切点坐标
QVector<Mat> separationTif(Mat &iplImg, Point2d &cutPnt)
{
QVector<Mat> vecMat;
int width = iplImg.size().width;
int height = iplImg.size().height;
int begin_wid = 0, end_wid = 0, begin_heig = 0, end_heig = 0; //标记黑边 坐标
int i = 0, j = 0;
int blackNumber = 0;
for (j = 0; j < height; ++j)
{
for (i = 0; i < width; ++i)
{
int tmpb, tmpg, tmpr;
tmpb = cvGet2D(&(IplImage)iplImg, j, i).val[0];
tmpg = cvGet2D(&(IplImage)iplImg, j, i).val[1];
tmpr = cvGet2D(&(IplImage)iplImg, j, i).val[2];
if (tmpb == 0 && tmpg == 0 && tmpr == 0) //这里RGB数字可以灵活变动
{
++blackNumber;
}
}
}
if (blackNumber == 0) //不存在黑边
{
vecMat << iplImg;
return vecMat;
}
else
{
for (j = 0; j < height; ++j)
{
bool flag = false;
for (i = 0; i < width; ++i)
{
int tmpb, tmpg, tmpr;
tmpb = cvGet2D(&(IplImage)iplImg, j, i).val[0];
tmpg = cvGet2D(&(IplImage)iplImg, j, i).val[1];
tmpr = cvGet2D(&(IplImage)iplImg, j, i).val[2];
if (tmpb == 0 && tmpg == 0 && tmpr == 0)
{
if (i > 0 && i < width - 1 && j > 0 && j < height - 1)
;
else
{
flag = true;
begin_heig = j;
break;
}
}
}
if (flag) break;
}
for (j = height - 1; j >= begin_heig; --j)
{
bool flag = false; //判断
for (i = 0; i < width; ++i)
{
int tmpb, tmpg, tmpr;
tmpb = cvGet2D(&(IplImage)iplImg, j, i).val[0];
tmpg = cvGet2D(&(IplImage)iplImg, j, i).val[1];
tmpr = cvGet2D(&(IplImage)iplImg, j, i).val[2];
if (tmpb == 0 && tmpg == 0 && tmpr == 0)
{
if (i > 0 && i < width - 1 && j > 0 && j < height - 1)
;
else
{
flag = true;
end_heig = j;
break;
}
}
}
if (flag) break;
}
for (i = 0; i < width; ++i)
{
bool flag = false;
for (j = 0; j < height; ++j)
{
int tmpb, tmpg, tmpr;
tmpb = cvGet2D(&(IplImage)iplImg, j, i).val[0];
tmpg = cvGet2D(&(IplImage)iplImg, j, i).val[1];
tmpr = cvGet2D(&(IplImage)iplImg, j, i).val[2];
if (tmpb == 0 && tmpg == 0 && tmpr == 0)
{
if (i > 0 && i < width - 1 && j > 0 && j < height - 1)
;
else
{
flag = true;
begin_wid = i;
break;
}
}
}
if (flag) break;
}
for (i = width - 1; i >= begin_wid; --i)
{
bool flag = false;
for (j = 0; j < height; ++j)
{
int tmpb, tmpg, tmpr;
tmpb = cvGet2D(&(IplImage)iplImg, j, i).val[0];
tmpg = cvGet2D(&(IplImage)iplImg, j, i).val[1];
tmpr = cvGet2D(&(IplImage)iplImg, j, i).val[2];
if (tmpb == 0 && tmpg == 0 && tmpr == 0)
{
if (i > 0 && i < width - 1 && j > 0 && j < height - 1)
;
else
{
flag = true;
end_wid = i;
break;
}
}
}
if (flag) break;
}
if (begin_wid > 0 && begin_wid < (width - 1))
cutPnt.x = begin_wid;
else
cutPnt.x = end_wid;
if (begin_heig > 0 && begin_heig < (height - 1))
cutPnt.y = begin_heig;
else
cutPnt.y = end_heig;
if (cutPnt.x != 0 || cutPnt.y != 0)
{ //裁剪的中心点不在(0,0)时 剪切图像
cout << "裁剪的中心点为:" << endl;
qDebug() << cutPnt.x << cutPnt.y;
Mat img0 = Mat(iplImg, Rect(0, 0, cutPnt.x + 1, cutPnt.y + 1)); //左上
Mat img1 = Mat(iplImg, Rect(0, cutPnt.y, cutPnt.x + 1, height - cutPnt.y - 1)); //左下
Mat img2 = Mat(iplImg, Rect(cutPnt.x, 0, width - cutPnt.x - 1, cutPnt.y + 1)); //右上
Mat img3 = Mat(iplImg, Rect(cutPnt.x, cutPnt.y, width - cutPnt.x - 1, height - cutPnt.y - 1)); //右下
vecMat << img0 << img1 << img2 << img3;
}
else
{
qDebug() << "Do not have black_border";
vecMat << iplImg;
}
return vecMat;
}
}
计算图片分割后每张图的tfw位置信息
//传入剪切点的坐标和tfw文件信息 返回tfw
QVector<QList<QString>> getTfw(Point2d &cutPnt, QList<QString> &listString)
{
double resolutionX = listString.at(0).trimmed().toDouble(); //A 去\n然后转化成数字
double ranslation = listString.at(1).trimmed().toDouble(); //D
double rotate = listString.at(2).trimmed().toDouble(); //B
double resolutionY = listString.at(3).trimmed().toDouble(); //E
double actualX = listString.at(4).trimmed().toDouble(); //C
double actualY = listString.at(5).trimmed().toDouble(); //F
double x = resolutionX * cutPnt.x + rotate * cutPnt.y + actualX; //x'=Ax+By+C 计算新的位置坐标
double y = ranslation *cutPnt.x + resolutionY * cutPnt.y + actualY; //y'=Dx+Ey+F
QList<QString> listString0, listString1, listString2, listString3;
for (int i = 0; i<listString.size(); ++i)
{
listString0 << listString.at(i);
listString1 << listString.at(i);
listString2 << listString.at(i);
listString3 << listString.at(i);
}
listString1[5] = QString::number(y, 10, 3) + "\n";
listString2[4] = QString::number(x, 10, 3) + "\n";
listString3[4] = QString::number(x, 10, 3) + "\n";
listString3[5] = QString::number(y, 10, 3) + "\n";
QVector<QList<QString>> vecTfw;
if (cutPnt.x == 0 && cutPnt.y == 0)
return vecTfw << listString0;
else
{
vecTfw << listString0 << listString1 << listString2 << listString3;
return vecTfw;
}
}
删除带有黑边的图片
//去除不符合标准的图片和tfw (对应的图片有黑边)
void dele(QVector<Mat> &vecTif, QVector<QList<QString>> &vecTfw)
{
if (vecTif.size() == 1)
return ;
else
{
for (int i = 0; i < vecTif.size(); ++i)
{
int blackNumber = 0;
for (int j = 0; j < vecTif.at(i).size().height; j++)
{
for (int k = 0; k < vecTif.at(i).size().width; k++)
{
int tmpb, tmpg, tmpr;
tmpb = cvGet2D(&(IplImage)vecTif.at(i), j, k).val[0];
tmpg = cvGet2D(&(IplImage)vecTif.at(i), j, k).val[1];
tmpr = cvGet2D(&(IplImage)vecTif.at(i), j, k).val[2];
if (tmpb == 0 && tmpg == 0 && tmpr == 0)
{
++blackNumber;
}
}
}
if (blackNumber > 100) //不局限与100 可以适当调整
{
vecTif.remove(i, 1); //从vector中移除从 i开始的count个元素
vecTfw.remove(i, 1); //只有一个图片被删除
}
}
}
}
写入保存tif图片和tfw位置信息
//传入打开和保存的文件夹 写入文件
void removeBlack(QString &openFolderPath, QString &saveFolderPath)
{
//打开文件夹文件夹下tif文件路径
QDir dirImg(openFolderPath);
// 设置过滤器
QStringList filtersTif;
filtersTif << "*.tif";
dirImg.setNameFilters(filtersTif);
QStringList m_imgList = dirImg.entryList();
QDir dirText(openFolderPath);
QStringList filtersTfw;
filtersTfw << "*.tfw";
dirText.setNameFilters(filtersTfw);
QStringList m_textList = dirText.entryList();
for (int i = 0; i < m_imgList.size(); ++i)
{
QString imgPath = openFolderPath + "//" + m_imgList.at(i); //图片路径
QString tfwPath = openFolderPath + "//" + m_textList.at(i); //文件路径
qDebug() << "OpenPath:" << endl << imgPath;
string sourcePath = string((const char *)imgPath.toLocal8Bit()); //Qstring 转string
Mat iplImg = imread(sourcePath, CV_LOAD_IMAGE_ANYCOLOR | CV_LOAD_IMAGE_ANYDEPTH);
QList<QString> listString; //一个文件的所有字符
QFile file(tfwPath);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
while (!file.atEnd())
{
QByteArray line = file.readLine();
QString str(line);
//qDebug() << str;
listString << str;
}
file.close();
}
QString saveTifName = m_imgList.at(i).section('.', 0, 0); //从打开文件中 提取 保存文件的部分路径
Point2d cutPnt;
QVector<Mat> vecMat = separationTif(iplImg, cutPnt);
QVector<QList<QString>> vecTfw = getTfw(cutPnt, listString);
dele(vecMat, vecTfw);
qDebug() << vecMat.size() << vecTfw.size();
if (vecMat.size() != 0)
{
for (int i = 0; i < vecMat.size(); ++i)
{
QString temp;
temp.setNum(i);
QString qsaveTifPath = saveFolderPath + "\\" + saveTifName + "__" + temp + ".tif";
QString qsavetfwPath = saveFolderPath + "\\" + saveTifName + "__" + temp + ".tfw";
string savePath = string((const char *)qsaveTifPath.toLocal8Bit()); //QString 转string
if (vecMat.at(i).size().width != 0 && vecMat.at(i).size().height != 0)
{
imwrite(savePath, vecMat.at(i));
qDebug() << "SavePath:" << endl << qsaveTifPath;
QString str,allStr;
foreach(str, vecTfw.at(i))
{
allStr = allStr + str;
}
QFile file(qsavetfwPath);
file.open(QIODevice::WriteOnly | QIODevice::Text);
file.write(allStr.toUtf8());
file.close();
}
}
}
}
qDebug() << "OK";
}
至此所有处理函数全部完成,在主程序中只需要调用removeBlack()函数,然后 传入路径就可以。
.
.
.
源码:https://download.csdn.net/download/qq_42610313/11540662
出现问题:
1、处理图片速度较慢,测试了60+图片,发现有一张图片暴露问题。
2、当遍历有少量黑色像素时,存在Bug。
2、打开处理后的所有图片,发现有少量漏洞,