这周的CV基础练习是简单的图形绘制:比如说矩形、三角形和圆心什么的。会发现其实矩形和圆形的实现思路都很直白,矩形只需要确认两个对角坐标就可以了,圆心只需要确认圆心和半径,接着就是简单的遍历各个像素点判断。但是,三角形的绘制把数学渣的我难住了,然后去查了一下资料,受到了知乎某位大神的启发:
如下截图:
于是有了以下思路:
一.实心三角形绘制
①算出三条直线L1、L2、L3的直线方程,即K1、K2、K3(斜率)和B1、B2、B3(截距)的值
②设三个顶点为P1、P2、P3,某一个任意位置的像素点P,P1P2的直线为L1.
③要判断P点是否在L1的“+”一边,则把P、P3分别带入直线方程,算出Y-(KX+B)的值,同时大于0或者同时小于0,则P落在“+”,否则在“-”
④判断P点是否在三角形内,则需要判断P是否同时落在三条直线的“+”区域,所以要做三次判断
二. 三角形边框绘制
①求出三条直线方程
②求出三角形范围内的X轴最大最小值,Y轴最大最小值
③在minX~maxX, minY~maxY的范围内遍历,只要P点落在直线方程上,则可以判断为真,赋上指定的绘制颜色
code实现:
1 //一个三角形,需要依靠三个顶点来确立,同样还需要颜色以及空心与否等条件 2 void drawTri(CImg<unsigned char>& inputImage, vector<int>& position, vector<int>& color, bool solid ) { 3 unsigned int width = inputImage.width(); 4 unsigned int height = inputImage.height(); 5 unsigned int depth = inputImage.depth(); 6 unsigned int spectrum = inputImage.spectrum(); 7 8 ////算法优化,尽量地减少遍历范围 9 int minX = (position[0] > position[2] ? position[2] : position[0]) > position[4] ? position[4] : (position[0] > position[2] ? position[2] : position[0]); 10 int maxX = (position[0] > position[2] ? position[0] : position[2]) < position[4] ? position[4] : (position[0] > position[2] ? position[2] : position[0]); 11 int minY = (position[1] > position[3] ? position[3] : position[1]) > position[5] ? position[5] : (position[1] > position[3] ? position[3] : position[1]); 12 int maxY = (position[1] > position[3] ? position[1] : position[3]) < position[5] ? position[5] : (position[1] > position[3] ? position[3] : position[1]); 13 14 //三条直线方程 15 double k1, b1, k2, b2, k3, b3; 16 k1 = (position[3] - position[1]) / (position[2] - position[0] + 0.0); 17 b1 = position[1] - k1 * position[0]; 18 k2 = (position[5] - position[3]) / (position[4] - position[2] + 0.0); 19 b2 = position[3] - k2 * position[2]; 20 k3 = (position[5] - position[1]) / (position[4] - position[0] + 0.0); 21 b3 = position[1] - k3 * position[0]; 22 23 //遍历范围增大,防止边界无法检测 +2, 此处不需要担心超出边界,因为在最后赋值的时候会再判断一次 24 for (int x = minX - 2; x <= maxX + 2; x ++) { 25 for (int y = minY - 2; y <= maxY + 2; y ++) { 26 if (y >= 0 && y < height && x >= 0 && x < width) { 27 //按照三角形绘制算法,我们需要判断某一点落在正确的一边就需要依赖三角形中的第三个点 28 //P1P2形成直线L1,判断(X, Y)是否落在正确的一边,就把P3和该点一起带入直线方程,同时大于0,或者同时小于0则为真(即有效) 29 //判断某一点是否对于三条直线方程都符合条件(都为真,则该点落在三角形内) 30 int ans1 = static_cast<int>(position[5] - (position[4] * k1 + b1)); 31 int ans2 = static_cast<int>(y - (x * k1 + b1)); 32 int ans3 = static_cast<int>(position[1] - (position[0] * k2 + b2)); 33 int ans4 = static_cast<int>(y - (x * k2 + b2)); 34 int ans5 = static_cast<int>(position[3] - (position[2] * k3 + b3)); 35 int ans6 = static_cast<int>(y - (x * k3 + b3)); 36 bool line1 = false, line2 = false, line3 = false; 37 if ((ans1 > 0 && ans2 >= -1) || (ans1 < 0 && ans2 <= 1))line1 = true; 38 if ((ans3 > 0 && ans4 >= -1) || (ans3 < 0 && ans4 <= 1))line2 = true; 39 if ((ans5 > 0 && ans6 >= -1) || (ans5 < 0 && ans6 <= 1))line3 = true; 40 if (line1 && line2 && line3) { 41 for (int z = 0; z < spectrum; z ++){ 42 if (solid) { 43 inputImage.atXYZC(x, y, 1, z) = static_cast<unsigned char>(color[z]); 44 } 45 else { 46 if (ans2 == 0 || ans4 == 0 ||ans6 == 0) { 47 inputImage.atXYZC(x, y, 1, z) = static_cast<unsigned char>(color[z]); 48 } 49 } 50 } 51 } 52 53 } 54 } 55 } 56 };
效果如下图:
(填充)
(非填充)