Last active
March 19, 2017 06:15
-
-
Save zhanghuimeng/c680197d7a2cd250597c5b4683f2b0c3 to your computer and use it in GitHub Desktop.
Using OpenCV 3 to practice draw lines and circles in a Raster Graphics manner. Antialias included.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <vector> | |
#include <iostream> | |
#include <opencv2/opencv.hpp> | |
#include <cmath> | |
using namespace std; | |
using namespace cv; | |
const int width = 1500, height = 1500; | |
const Vec3b BLACK(0, 0, 0), WHITE(255, 255, 255), BLUE(255, 0, 0), | |
MAGENTA(255, 0, 255), RED(0, 0, 255), GREEN(0, 255, 0), | |
YELLOW(0, 255, 255), CYAN(255, 255, 0); | |
// 存储图片文件 | |
Mat image(width, height, CV_8UC3, Scalar(255, 255, 255)); | |
// 权重 | |
const double WEIGHT[3][3] = { { 1 / 16.0, 2 / 16.0, 1 / 16.0 },{ 2 / 16.0, 4 / 16.0, 2 / 16.0 }, | |
{ 1 / 16.0, 2 / 16.0, 1 / 16.0 } }; | |
// 在图片上绘制一个像素 | |
void drawpixel(int x, int y, const Vec3b& color) | |
{ | |
if (x >= 0 && x < width && y >= 0 && y < height) | |
{ | |
Vec3b& intensity = image.at<Vec3b>(x, y); | |
for (int i = 0; i < 3; i++) | |
{ | |
intensity[i] = color.val[i]; | |
} | |
} | |
} | |
// 处理垂直线段的退化情况 | |
void DegenerateLine(int x0, int y0, int y1, const Vec3b& color) | |
{ | |
if (y0 > y1) swap(y0, y1); | |
for (int i = y0; i <= y1; i++) | |
drawpixel(x0, i, color); | |
} | |
// 数值微分法 | |
void DDALine(int x0, int y0, int x1, int y1, const Vec3b& color) | |
{ | |
if (x0 == x1) | |
{ | |
DegenerateLine(x0, y0, y1, color); | |
return; | |
} | |
if (x0 > x1) | |
swap(x0, x1), swap(y0, y1); | |
double dx = x1 - x0, dy = y1 - y0, k = dy / dx; | |
// 取决于斜率 | |
// 为什么需要区分两种斜率情况呢? | |
// 要不然线就断了 | |
// |k| <= 1时 | |
if (abs(k) <= 1.00) | |
{ | |
double y = y0; | |
for (int x = x0; x <= x1; x++) | |
{ | |
drawpixel(x, int(y + 0.5), color); | |
y = y + k; | |
} | |
} | |
else | |
{ | |
k = 1 / k; | |
double x = x0; | |
for (int y = y0; y <= y1; y++) | |
{ | |
drawpixel(int(x + 0.5), y, color); | |
x = x + k; | |
} | |
} | |
} | |
// 一次画出相对圆心对称的八个像素 | |
void CirclePoints(int x0, int y0, int x, int y, const Vec3b& color) | |
{ | |
drawpixel(x, y, color); | |
drawpixel(2 * x0 - x, 2 * y0 - y, color); | |
drawpixel(x, 2 * y0 - y, color); | |
drawpixel(2 * x0 - x, y, color); | |
drawpixel(y - y0 + x0, x - x0 + y0, color); | |
drawpixel(y0 - y + x0, x0 - x + y0, color); | |
drawpixel(y0 - y + x0, x - x0 + y0, color); | |
drawpixel(y - y0 + x0, x0 - x + y0, color); | |
} | |
// 中点画圆法 | |
void MidPointCircle(int x0, int y0, int r, const Vec3b& color) | |
{ | |
int x, y; | |
double d; | |
x = x0, y = y0 + r, d = 1.25 - r; | |
CirclePoints(x0, y0, x, y, color); | |
while (x - x0 <= y - y0) | |
{ | |
// d = (x+2-x0)*(x+2-x0) + (y-0.5-y0)*(y-0.5-y0) - r*r; | |
if (d < 0) | |
d += 2 * x + 3 - 2 * x0; | |
// d = (x+2-x0)*(x+2-x0) + (y-1.5-y0)*(y-1.5-y0) - r*r; | |
else | |
{ | |
d += 2 * x - 2 * y + 5 - 2 * x0 + 2 * y0; | |
y--; | |
} | |
x++; | |
CirclePoints(x0, y0, x, y, color); | |
} | |
} | |
// 判断点是否在圆内 | |
bool IsInCircle(int x0, int y0, int r, double width, int x, int y) | |
{ | |
double dist = sqrt((x - x0)*(x - x0) + (y - y0)*(y - y0)); | |
if (dist >= r) return dist - r < width / 2; | |
else return r - dist < width * 0.7; | |
} | |
// 颜色插值 | |
inline Vec3b ColorInterpolation(const Vec3b& color, double portion) | |
{ | |
double x = color[0] * portion + 1 - portion; | |
double y = color[1] * portion + 1 - portion; | |
double z = color[2] * portion + 1 - portion; | |
return Vec3b((int)(x * 255), y * 255, z * 255); | |
} | |
// 中点画圆法 + 加权区域采样 | |
void ASWeighedMidPointCircle(int x0, int y0, int r, const Vec3b& color) | |
{ | |
int x, y; | |
double d; | |
x = x0, y = y0 + r, d = 1.25 - r; | |
while (x - x0 <= y - y0) | |
{ | |
// 考虑当前像素的上下各2个,进行判断 | |
for (int i = -2; i <= 2; i++) | |
{ | |
int PixX = x; | |
int PixY = y + i; | |
// 对于当前像素考虑 | |
// 分成九份 | |
double portion = 0.0; | |
for (int l = 0; l < 3; l++) | |
{ | |
for (int m = 0; m < 3; m++) | |
{ | |
double dx = PixX + (1 - l) / 3.0; | |
double dy = PixY + (1 - m) / 3.0; | |
// 切分之后得到像素中心dx, dy | |
if (IsInCircle(x0, y0, r, 1.0, dx, dy)) | |
portion += WEIGHT[l][m]; | |
} | |
} | |
if (portion > 0.0) | |
{ | |
CirclePoints(x0, y0, PixX, PixY, ColorInterpolation(color, portion)); | |
} | |
} | |
CirclePoints(x0, y0, x, y, color); | |
if (d < 0) | |
// 计算公式:d = (x+2-x0)*(x+2-x0) + (y-0.5-y0)*(y-0.5-y0) - r*r; | |
d += 2 * x + 3 - 2 * x0; | |
else | |
{ | |
// 计算公式:d = (x+2-x0)*(x+2-x0) + (y-1.5-y0)*(y-1.5-y0) - r*r; | |
d += 2 * x - 2 * y + 5 - 2 * x0 + 2 * y0; | |
y--; | |
} | |
x++; | |
} | |
} | |
// 叉乘 | |
inline double Cross(double x0, double y0, double x1, double y1, double x2, double y2) | |
{ | |
return (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0); | |
} | |
// 叉乘 | |
// P0P1 × P0P2 | |
inline double Cross(const Vec2d& p0, const Vec2d& p1, const Vec2d& p2) | |
{ | |
return Cross(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]); | |
} | |
// 判断一个点在长方形内 | |
bool IsInRect(const Vec2d& p1, const Vec2d& p2, const Vec2d& p3, const Vec2d& p4, const Vec2d& p) | |
{ | |
if (Cross(p, p1, p2) * Cross(p, p4, p3) <= 0 | |
&& Cross(p, p4, p1) * Cross(p, p3, p2) <= 0) | |
return true; | |
return false; | |
} | |
// 把像素切分成9份 | |
inline void CutPixelCenter(int x, int y, int size, vector<Vec2d>& centerList) | |
{ | |
centerList.clear(); | |
double d = 1.0 / size; | |
int mid = size / 2; | |
for (int i = 0; i < size; i++) | |
{ | |
for (int j = 0; j < size; j++) | |
{ | |
double dx = x + (mid - i) * d; | |
double dy = y + (mid - j) * d; | |
centerList.push_back(Vec2d(dx, dy)); | |
} | |
} | |
} | |
// 数值微分法 + 离散区域采样 | |
// 把一个像素平均分成9份 | |
void ASDDALine(int x0, int y0, int x1, int y1, double width, const Vec3b& color) | |
{ | |
if (x0 == x1) // 退化情况,显然不需要反走样 | |
{ | |
DegenerateLine(x0, y0, y1, color); | |
return; | |
} | |
if (x0 > x1) swap(x0, x1), swap(y0, y1); | |
double dx = x1 - x0, dy = y1 - y0, k = dy / dx; | |
// 计算线段对应的矩形的四个顶点 | |
double dw = abs(width / 2.0), dh = (abs(sqrt(k*k + 1)) * (x1 - x0) ) /2.0; | |
Vec2d center, p[4]; | |
center[0] = (x0 + x1) / 2.0; | |
center[1] = (y0 + y1) / 2.0; | |
p[0][0] = p[3][0] = dh; | |
p[1][0] = p[2][0] = -dh; | |
p[0][1] = p[1][1] = dw; | |
p[2][1] = p[3][1] = -dw; | |
// 矩阵乘法,旋转 | |
double theta = atan(k), s = sin(theta), c = cos(theta); | |
for (int i = 0; i < 4; i++) | |
{ | |
double x = p[i][0] * c - p[i][1] * s; | |
double y = p[i][0] * s + p[i][1] * c; | |
p[i][0] = x + center[0]; | |
p[i][1] = y + center[1]; | |
} | |
vector<Vec2d> centerList; | |
// 考虑当前像素的上下各2个,进行判断 | |
// 取决于斜率 | |
// |k| <= 1时 | |
if (abs(k) <= 1.00) | |
{ | |
double y = y0; | |
for (int x = x0; x <= x1; x++) | |
{ | |
for (int i = -2; i <= 2; i++) | |
{ | |
CutPixelCenter(x, int(y + 0.5) + i, 3, centerList); | |
int counts = 0; | |
for (int j = 0; j < centerList.size(); j++) | |
{ | |
if (IsInRect(p[0], p[1], p[2], p[3], centerList[j])) | |
++counts; | |
} | |
double portion = counts / 9.0; | |
if (counts > 0) | |
{ | |
drawpixel(x, int(y + 0.5) + i, ColorInterpolation(color, portion)); | |
} | |
} | |
drawpixel(x, int(y + 0.5), color); | |
y = y + k; | |
} | |
} | |
else | |
{ | |
k = 1 / k; | |
double x = x0; | |
for (int y = y0; y <= y1; y++) | |
{ | |
for (int i = -2; i <= 2; i++) | |
{ | |
CutPixelCenter(int(x + 0.5) + i, y, 3, centerList); | |
int counts = 0; | |
for (int j = 0; j < centerList.size(); j++) | |
{ | |
if (IsInRect(p[0], p[1], p[2], p[3], centerList[j])) | |
++counts; | |
} | |
double portion = counts / 9.0; | |
if (counts > 0) | |
{ | |
drawpixel(int(x + 0.5) + i, y, ColorInterpolation(color, portion)); | |
} | |
} | |
drawpixel(int(x + 0.5), y, color); | |
x = x + k; | |
} | |
} | |
} | |
// 数值微分法 + 加权区域采样 | |
void ASWeighedDDALine(int x0, int y0, int x1, int y1, double width, const Vec3b& color) | |
{ | |
if (x0 == x1) // 退化情况,显然不需要反走样 | |
{ | |
DegenerateLine(x0, y0, y1, color); | |
return; | |
} | |
if (x0 > x1) swap(x0, x1), swap(y0, y1); | |
double dx = x1 - x0, dy = y1 - y0, k = dy / dx; | |
// 计算线段四个顶点 | |
double dw = abs(width / 2.0), dh = abs(sqrt(k*k + 1) * (x1 - x0) / 2.0); | |
Vec2d center, p[4]; | |
center[0] = (x0 + x1) / 2.0; | |
center[1] = (y0 + y1) / 2.0; | |
p[0][0] = p[3][0] = dh; | |
p[1][0] = p[2][0] = -dh; | |
p[0][1] = p[1][1] = dw; | |
p[2][1] = p[3][1] = -dw; | |
// 矩阵乘法,旋转 | |
double theta = atan(k), s = sin(theta), c = cos(theta); | |
for (int i = 0; i < 4; i++) | |
{ | |
double x = p[i][0] * c - p[i][1] * s; | |
double y = p[i][0] * s + p[i][1] * c; | |
p[i][0] = x + center[0]; | |
p[i][1] = y + center[1]; | |
} | |
vector<Vec2d> centerList; | |
// 考虑当前像素的上下各2个,进行判断 | |
// 取决于斜率 | |
// |k| <= 1时 | |
if (abs(k) <= 1.00) | |
{ | |
double y = y0; | |
for (int x = x0; x <= x1; x++) | |
{ | |
for (int i = -2; i <= 2; i++) | |
{ | |
double d = 1.0 / 3.0; | |
int mid = 3 / 2; | |
double portion = 0.0; | |
for (int j = 0; j < 3; j++) | |
{ | |
for (int k = 0; k < 3; k++) | |
{ | |
double dx = x + (mid - j) * d; | |
double dy = y + i + (mid - k) * d; | |
if (IsInRect(p[0], p[1], p[2], p[3], Vec2d(dx, dy))) | |
portion += WEIGHT[j][k]; | |
} | |
} | |
if (portion > 0.0) | |
{ | |
drawpixel(x, int(y + 0.5) + i, ColorInterpolation(color, portion)); | |
} | |
} | |
drawpixel(x, int(y + 0.5), color); | |
y = y + k; | |
} | |
} | |
else | |
{ | |
k = 1 / k; | |
double x = x0; | |
for (int y = y0; y <= y1; y++) | |
{ | |
for (int i = -2; i <= 2; i++) | |
{ | |
double d = 1.0 / 3.0; | |
int mid = 3 / 2; | |
double portion = 0.0; | |
for (int j = 0; j < 3; j++) | |
{ | |
for (int k = 0; k < 3; k++) | |
{ | |
double dx = x + (mid - j) * d; | |
double dy = y + i + (mid - k) * d; | |
if (IsInRect(p[0], p[1], p[2], p[3], Vec2d(dx, dy))) | |
portion += WEIGHT[j][k]; | |
} | |
} | |
if (portion > 0.0) | |
{ | |
drawpixel(int(x + 0.5), y + i, ColorInterpolation(color, portion)); | |
} | |
} | |
drawpixel(int(x + 0.5), y, color); | |
x = x + k; | |
} | |
} | |
} | |
int main() | |
{ | |
//DDALine(20, 20, 500, 130, BLUE); | |
//DDALine(200, 100, 210, 400, BLACK); | |
//MidPointCircle(450, 470, 70, GREEN); | |
//MidPointCircle(600, 600, 120, MAGENTA); | |
ASWeighedMidPointCircle(450, 470, 150, BLACK); | |
ASWeighedMidPointCircle(200, 200, 10, BLACK); | |
// ASDDALine(20, 20, 500, 130, 1, BLACK); | |
// ASDDALine(200, 100, 210, 400, 1, BLACK); | |
// ASWeighedDDALine(400, 700, 600, 800, 1, BLACK); | |
// ASWeighedDDALine(400, 600, 600, 800, 1, BLACK); | |
imshow("test", image); | |
waitKey(0); | |
imwrite("test.bmp", image); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment