├── data └── 0.0_1_0 │ ├── lena.jpg │ ├── lena-1.jpg │ └── lena-2.jpg ├── CMakeLists.txt ├── src ├── FindFiles.h ├── FindFiles.cpp ├── DataAugment.h ├── main.cpp └── DataAugment.cpp └── README.md /data/0.0_1_0/lena.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yhlleo/ImageDataAugmentation/HEAD/data/0.0_1_0/lena.jpg -------------------------------------------------------------------------------- /data/0.0_1_0/lena-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yhlleo/ImageDataAugmentation/HEAD/data/0.0_1_0/lena-1.jpg -------------------------------------------------------------------------------- /data/0.0_1_0/lena-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yhlleo/ImageDataAugmentation/HEAD/data/0.0_1_0/lena-2.jpg -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # set project's name 2 | PROJECT( DataAugmentation ) 3 | 4 | ############################################################################### 5 | # CMake settings 6 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3) 7 | 8 | # this command finds OpenCV libraries and sets all required variables 9 | FIND_PACKAGE(OpenCV REQUIRED) 10 | 11 | FILE(GLOB_RECURSE HDRS_FILES *.h *.hpp) 12 | FILE(GLOB_RECURSE SRCS_FILES *.c *.cpp) 13 | 14 | #ADD_LIBRARY(${PROJECT_NAME} ${HDRS_FILES} ${SRCS_FILES}) 15 | #TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OpenCV_LIBS}) 16 | 17 | ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS_FILES} ${HDRS_FILES}) 18 | TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OpenCV_LIBS}) 19 | -------------------------------------------------------------------------------- /src/FindFiles.h: -------------------------------------------------------------------------------- 1 | /* 2 | Find and generate a file list of the folder. 3 | 4 | - Editor: Yahui Liu. 5 | - Data: 2016-03-27 6 | - Email: yahui.cvrs@gmail.com 7 | - Address: Computer Vision and Remote Sensing(CVRS), Lab. 8 | */ 9 | 10 | #ifndef FIND_FILES_H 11 | #define FIND_FILES_H 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | using namespace std; 24 | 25 | class FindFiles 26 | { 27 | public: 28 | FindFiles(){} 29 | ~FindFiles(){} 30 | 31 | public: 32 | std::vector file_lists; 33 | 34 | public: 35 | std::vector findFiles(char* lpPath, char* secName = ".*"); 36 | 37 | }; 38 | 39 | #endif // FIND_FILES_H -------------------------------------------------------------------------------- /src/FindFiles.cpp: -------------------------------------------------------------------------------- 1 | #include "FindFiles.h" 2 | 3 | std::vector FindFiles::findFiles(char* lpPath, char* secName/* = ".*"*/) 4 | { 5 | char szFind[MAX_PATH]; 6 | char szFile[MAX_PATH]; 7 | 8 | WIN32_FIND_DATA FindFileData; 9 | 10 | strcpy(szFind,lpPath); 11 | strcat(szFind,"\\*"); 12 | strcat(szFind,secName); 13 | 14 | HANDLE hFind=::FindFirstFile(szFind,&FindFileData); 15 | 16 | if(INVALID_HANDLE_VALUE == hFind) 17 | { 18 | std::cout << "Empty folder!" << std::endl; 19 | return std::vector(); 20 | } 21 | 22 | do 23 | { 24 | if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 25 | { 26 | if(FindFileData.cFileName[0]!='.') 27 | { 28 | strcpy(szFile,lpPath); 29 | strcat(szFile,"\\"); 30 | strcat(szFile,FindFileData.cFileName); 31 | findFiles(szFile); 32 | } 33 | } 34 | else 35 | { 36 | if ( szFile[0] ) 37 | { 38 | std::string filePath = lpPath; 39 | filePath += "\\"; 40 | filePath += FindFileData.cFileName; 41 | file_lists.push_back(filePath); 42 | } 43 | else 44 | { 45 | std::string filePath = szFile; 46 | filePath += FindFileData.cFileName; 47 | file_lists.push_back(filePath); 48 | } 49 | 50 | // std::cout << szFile << " " << FindFileData.cFileName << std::endl; 51 | } 52 | }while(::FindNextFile(hFind,&FindFileData)); 53 | 54 | ::FindClose(hFind); 55 | return file_lists; 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageDataAugmentation 2 | 3 | 工程源码GitHub: [yhlleo/ImageDataAugmentation](https://github.com/yhlleo/DataAugmentation) 4 | 5 | 编译环境: 6 | 7 | - CMake 8 | - OpenCV 9 | - Visual Studio 2010 10 | 11 | -------------------------------------------------------------- 12 | 13 | 图出了一些问题,可以查看原博客:[求解旋转图像的最大内接矩形](https://blog.csdn.net/YhL_Leo/article/details/51510432) 14 | 15 | ----------------- 16 | 17 | 在机器学习和深度学习中,通常使用为了增加数据(Data Augmentation)可以对数据进行例如一系列的旋转(rotate)、镜像(flip)等操作,本文将讲解如何求取旋转图像的最大内接矩形问题,这里的内接矩形,并不是数学上严格的内接概念,而是获得的矩形是不含如图 1所示的旋转导致的空白区域。 18 | 19 | [
20 | 21 | **图 1**
](https://img-blog.csdn.net/20160527111612913) 22 | 23 | [
24 | 25 | **图 2**
](https://img-blog.csdn.net/20160526204727593) 26 | 27 | 如图2所示,红色矩形ABCD是绿色矩形旋转一定角度后对应的结果,旋转中心定义在图像的中心,坐标轴方向`x->col, y->row`,则可有以下推导: 28 | 29 | [
](https://img-blog.csdn.net/20160526205303517) 30 | 31 | 但是测试过程中就会发现,上面的分析,对于`col`近似等于`row`的图像会有问题,问题出自哪里呢?如图 3,很容得到的解是这样的蓝色的矩形框,因此,除了上面的极值条件,还必须添加相应的边界条件,从而获得紫色的矩形框边界。 32 | 33 | [
34 | 35 | **图 3**
](https://img-blog.csdn.net/20160527111109367) 36 | 37 | 可以看出,两种情况的最大不同就是, 矩形边AB与坐标轴`col`相交的点是否位于绿色矩形框的内部,前面推导的结果对于位于绿色矩形框内部是成立的,对于位于外部的情形,可有如下分析: 38 | 39 | [
](https://img-blog.csdn.net/20160527112236291) 40 | 41 | 以上的推导在顺时针旋转角度a属于`(0, 90)`时成立,当角度位a属于`(90, 180)` 时等价于180-a,而当a为90°或180°时相对简单,更多的旋转角度也是以180°为周期,因此,整个旋转最大内接矩形的求取过程就分析完毕~ 42 | -------------------------------------------------------------------------------- /src/DataAugment.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Image data augmentation: flip and rotate. 4 | 5 | - Editor: Yahui Liu. 6 | - Data: 2016-05-20 7 | - Email: yahui.cvrs@gmail.com 8 | - Address: Computer Vision and Remote Sensing(CVRS), Lab. 9 | */ 10 | 11 | #ifndef DATA_AUGMENT_H 12 | #define DATA_AUGMENT_H 13 | 14 | #include 15 | #include 16 | 17 | #include "cv.h" 18 | #include "highgui.h" 19 | 20 | using namespace std; 21 | using namespace cv; 22 | 23 | enum FlipModel{ 24 | MIRROR_V, // vertical 25 | MIRROR_H // horizontal 26 | }; 27 | 28 | enum RotateModel 29 | { 30 | RAW_SIZE, // raw size of the input image 31 | // TOTAL_RESERVE, // TODO: total rotated image 32 | MAX_RECT_REGION // max rectangle region without blank area 33 | }; 34 | 35 | enum RadMeasure 36 | { 37 | AU_ANG, // angle 38 | AU_RAD // radian 39 | }; 40 | 41 | class DataAugment 42 | { 43 | public: 44 | DataAugment() 45 | { 46 | isColBigger = true; 47 | isColSimilarRow = false; 48 | } 49 | ~DataAugment() {} 50 | 51 | public: 52 | void imageFlip( 53 | cv::Mat& input, 54 | cv::Mat& output, 55 | FlipModel fm = MIRROR_H ); 56 | 57 | void imageRotate( 58 | cv::Mat& input, 59 | cv::Mat& output, 60 | double radian, 61 | RadMeasure radm = AU_RAD, 62 | RotateModel rm = MAX_RECT_REGION ); 63 | 64 | cv::Matx22d rotateMat( 65 | double radian, 66 | RadMeasure rm = AU_RAD ); 67 | 68 | std::vector rotateVertex( 69 | std::vector& vertexs, 70 | cv::Matx22d& rMat ); 71 | 72 | cv::Size getImageRange( 73 | std::vector& vertexs ); 74 | 75 | cv::Point2d getMaxRectRegion( 76 | cv::Point3d& line); 77 | 78 | cv::Point2d getCrossPoint( 79 | std::vector& vertexs/*, 80 | double angle, 81 | RadMeasure radm = AU_ANG*/ ); 82 | 83 | cv::Point2d getSpecialCrossPoint( 84 | std::vector& vertexs ); 85 | 86 | /* 87 | a(x1, y1) and b(x2, y2) are two vertexs of the image rectangle: 88 | a ------ b 89 | | | 90 | | | 91 | d ------ c 92 | 93 | Then, the line passed the two points is: 94 | (x1-x2)*y - (y1-y2)*x - y2*(x1-x2) + x2*(y1-y2) = 0 95 | which can by represented as: 96 | a*y + b*x + c = 0. 97 | */ 98 | cv::Point3d lineFunction( 99 | std::vector& vertexs); 100 | 101 | cv::Point3d lineFunction( 102 | cv::Point2d v1, 103 | cv::Point2d v2 ); 104 | 105 | double p2pDist( cv::Point2d p1, cv::Point2d p2 ); 106 | 107 | private: 108 | bool isColBigger; 109 | bool isColSimilarRow; 110 | 111 | }; 112 | 113 | #endif // DATA_AUGMENT_H -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "cv.h" 6 | #include "highgui.h" 7 | 8 | #include "FindFiles.h" 9 | #include "DataAugment.h" 10 | 11 | using namespace std; 12 | using namespace cv; 13 | 14 | /************************************************************************/ 15 | /* Image Rotate */ 16 | /************************************************************************/ 17 | void main() 18 | { 19 | FindFiles ff; 20 | std::vector fileNames = ff.findFiles("E:\\GitHub\\ImageDataAugmentation\\data\\0.0_1_0"); 21 | 22 | std::vector r_list(15); 23 | r_list[0] = "22.5_1_0"; 24 | r_list[1] = "45.0_1_0"; 25 | r_list[2] = "67.5_1_0"; 26 | r_list[3] = "90.0_1_0"; 27 | r_list[4] = "112.5_1_0"; 28 | r_list[5] = "135.0_1_0"; 29 | r_list[6] = "157.5_1_0"; 30 | r_list[7] = "180.0_1_0"; 31 | r_list[8] = "202.5_1_0"; 32 | r_list[9] = "225.0_1_0"; 33 | r_list[10] = "247.5_1_0"; 34 | r_list[11] = "270.0_1_0"; 35 | r_list[12] = "292.5_1_0"; 36 | r_list[13] = "315.0_1_0"; 37 | r_list[14] = "337.5_1_0"; 38 | 39 | std::string savePath = "E:\\GitHub\\ImageDataAugmentation\\data\\"; 40 | double theta = 22.5; 41 | 42 | for ( int j=0; j<15; j++ ) 43 | { 44 | std::string curPath = savePath + r_list[j] + "\\"; 45 | _mkdir(curPath.c_str()); 46 | 47 | for ( int i=0; i fileNames = ff.findFiles(folder); 79 | // 80 | // for ( int j=0; j(i); 29 | op3 = output.ptr(i); 30 | 31 | for ( int j=0; j(i); 41 | op1 = output.ptr(i); 42 | 43 | for ( int j=0; j(cols/2), 92 | static_cast(rows/2)), 93 | -theta, 94 | 1.0); 95 | 96 | cv::Mat r_input; 97 | cv::warpAffine( 98 | input, 99 | r_input, 100 | R, 101 | input.size()); 102 | 103 | if ( rm == MAX_RECT_REGION ) 104 | { 105 | theta -= int(theta / 180.0) * 180.0; 106 | 107 | std::vector vertex(4); 108 | vertex[0] = cv::Point2d(-(cols-1)/2, (rows-1)/2); 109 | vertex[1] = cv::Point2d((cols-1)/2, (rows-1)/2); 110 | vertex[2] = cv::Point2d((cols-1)/2, -(rows-1)/2); 111 | vertex[3] = cv::Point2d(-(cols-1)/2,-(rows-1)/2); 112 | 113 | 114 | if ( theta > 0.0 && theta < 90.0 ) 115 | { 116 | cv::Matx22d rMat = rotateMat(theta, AU_ANG); 117 | std::vector r_vertext = 118 | rotateVertex( vertex, rMat ); 119 | 120 | cv::Point2d maxp = getCrossPoint(r_vertext/*, theta, AU_ANG*/); 121 | 122 | if ( maxp.x > cols/2 || maxp.y > rows/2 ) 123 | { 124 | maxp = getSpecialCrossPoint(r_vertext); 125 | } 126 | 127 | maxp.x = maxp.x < cols/2 ? maxp.x : cols/2; 128 | maxp.y = maxp.y < rows/2 ? maxp.y : rows/2; 129 | 130 | cv::Rect rect( 131 | cols/2-maxp.x, 132 | rows/2-maxp.y, 133 | 2*std::abs(maxp.x), 134 | 2*std::abs(maxp.y)); 135 | 136 | r_input(rect).copyTo(output); 137 | } 138 | else if ( theta == 90.0 ) 139 | { 140 | cv::Rect rect; 141 | if( cols > rows) 142 | rect = cv::Rect((cols-rows)/2, 0, rows, rows ); 143 | else 144 | rect = cv::Rect(0, (rows-cols)/2, cols, cols ); 145 | 146 | r_input(rect).copyTo(output); 147 | } 148 | else if ( theta > 90.0 ) 149 | { 150 | double theta2 = 180.0 - theta; 151 | cv::Matx22d rMat = rotateMat(theta2, AU_ANG); 152 | std::vector r_vertext = 153 | rotateVertex( vertex, rMat ); 154 | 155 | cv::Point2d maxp = getCrossPoint(r_vertext/*, theta2, AU_ANG*/); 156 | 157 | if ( maxp.x > cols/2 || maxp.y > rows/2 ) 158 | { 159 | maxp = getSpecialCrossPoint(r_vertext); 160 | } 161 | 162 | maxp.x = maxp.x < cols/2 ? maxp.x : cols/2; 163 | maxp.y = maxp.y < rows/2 ? maxp.y : rows/2; 164 | 165 | cv::Rect rect( 166 | cols/2-maxp.x, 167 | rows/2-maxp.y, 168 | 2*std::abs(maxp.x), 169 | 2*std::abs(maxp.y)); 170 | 171 | r_input(rect).copyTo(output); 172 | } 173 | else 174 | { 175 | r_input.copyTo(output); 176 | } 177 | } 178 | else 179 | { 180 | r_input.copyTo(output); 181 | } 182 | } 183 | 184 | cv::Point2d DataAugment::getCrossPoint( 185 | std::vector& vertexs) 186 | { 187 | // cv::Matx22d rMat = rotateMat(angle, radm); 188 | // std::vector r_vertext = 189 | // rotateVertex( vertexs, rMat ); 190 | 191 | cv::Point3d ln_ab = lineFunction(vertexs); 192 | return getMaxRectRegion(ln_ab); 193 | } 194 | 195 | cv::Point2d DataAugment::getSpecialCrossPoint( 196 | std::vector& vertexs ) 197 | { 198 | cv::Point3d line0_1 = lineFunction(vertexs[0], vertexs[1]); 199 | cv::Point3d line1_2 = lineFunction(vertexs[1], vertexs[2]); 200 | 201 | double a1 = line0_1.x; 202 | double b1 = line0_1.y; 203 | double c1 = line0_1.z; 204 | 205 | double a2 = line1_2.x; 206 | double b2 = line1_2.y; 207 | double c2 = line1_2.z; 208 | 209 | double x = -(a1*c2 + a2*c1) / (a2*b1 + a1*b2); 210 | double y = -(b1*x+c1) / a1; 211 | 212 | return cv::Point2d( x, y ); 213 | } 214 | 215 | cv::Matx22d DataAugment::rotateMat( 216 | double radian, 217 | RadMeasure rm/* = AU_RAD*/ ) 218 | { 219 | double alpha = radian; 220 | 221 | if ( rm == AU_ANG ) 222 | alpha *= CV_PI / 180.0; 223 | 224 | return cv::Matx22d( 225 | std::cos(alpha), std::sin(alpha), 226 | -std::sin(alpha), std::cos(alpha) ); 227 | } 228 | 229 | std::vector DataAugment::rotateVertex( 230 | std::vector& vertexs, 231 | cv::Matx22d& rMat ) 232 | { 233 | std::vector rt = vertexs; 234 | const int v_size = vertexs.size(); 235 | 236 | for ( int i=0; i& vertexs) 262 | { 263 | cv::Point2d pa = vertexs[0]; 264 | cv::Point2d pb = vertexs[1]; 265 | 266 | if ( !isColBigger ) 267 | { 268 | pb = vertexs[3]; 269 | } 270 | 271 | double delta_x = pa.x - pb.x; 272 | double delta_y = pa.y - pb.y; 273 | cv::Point3d line(delta_x, -delta_y, -pb.y*delta_x + pb.x*delta_y); 274 | 275 | double m_line = std::sqrt(line.x*line.x + line.y*line.y); 276 | line *= 1.0/m_line; 277 | 278 | if ( line.x < 0.0 ) 279 | line = -line; 280 | 281 | return line; 282 | } 283 | 284 | 285 | cv::Point3d DataAugment::lineFunction( 286 | cv::Point2d v1, 287 | cv::Point2d v2 ) 288 | { 289 | cv::Point2d pa = v1; 290 | cv::Point2d pb = v2; 291 | 292 | double delta_x = pa.x - pb.x; 293 | double delta_y = pa.y - pb.y; 294 | cv::Point3d line(delta_x, -delta_y, -pb.y*delta_x + pb.x*delta_y); 295 | 296 | double m_line = std::sqrt(line.x*line.x + line.y*line.y); 297 | line *= 1.0/m_line; 298 | 299 | if ( line.x < 0.0 ) 300 | line = -line; 301 | 302 | return line; 303 | } 304 | 305 | cv::Point2d DataAugment::getMaxRectRegion( 306 | cv::Point3d& line) 307 | { 308 | if ( line.x != 0.0 && line.y != 0.0 ) 309 | { 310 | // double k = - line.y / line.x; // k = -b/a 311 | // double s = - line.z / line.x; // s = -c/a 312 | double a = line.x; 313 | double b = line.y; 314 | double c = line.z; 315 | 316 | if ( !isColBigger ) 317 | { 318 | b *= -1.0; 319 | } 320 | return cv::Point2d( -c/(2*b) , -c/(2*a) ); 321 | } 322 | else 323 | return cv::Point2d(0.0, 0.0); 324 | } 325 | 326 | cv::Size DataAugment::getImageRange( 327 | std::vector& vertexs ) 328 | { 329 | const int v_size = vertexs.size(); 330 | cv::Point2d pMin(0.0, 0.0), pMax(0.0, 0.0); 331 | for ( int i=0; i vertexs[i].x ? pMax.x : vertexs[i].x; 336 | pMax.y = pMax.y > vertexs[i].y ? pMax.y : vertexs[i].y; 337 | } 338 | 339 | return cv::Size( 340 | static_cast(pMax.x - pMin.x + 1.0), 341 | static_cast(pMax.y - pMin.y + 1.0)); 342 | } 343 | --------------------------------------------------------------------------------