Skip to content

Instantly share code, notes, and snippets.

@zhanghuimeng
Last active March 19, 2017 06:15
Show Gist options
  • Save zhanghuimeng/c680197d7a2cd250597c5b4683f2b0c3 to your computer and use it in GitHub Desktop.
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.
#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