文章目录

  • 图像旋转的原理
  • 图像旋转的实现
    • 最近邻插值
    • 双线性插值
    • 双线性的优化

图像旋转的原理

图像旋转的原理其实很简单,为了简化公式的推导,这里我们假设绕原点 ( 0 , 0 ) (0,0) (0,0)旋转。

本文规定逆时针旋转为正,当然你也可以规定顺时针为正(此时,正角度就表示顺时针旋转)
很容易得到三个方程:
t a n ( θ + α ) = y ′ x ′ ( 1 ) tan(\theta + \alpha)={y' \over x'} \quad \quad \quad (1) tan(θ+α)=x′y′​(1)
t a n α = y x ( 2 ) tan\alpha={y \over x} \quad \quad \quad (2) tanα=xy​(2)
x 2 + y 2 = x ′ 2 + y ′ 2 ( 3 ) x^2+y^2={x'}^2+{y'}^2 \quad \quad \quad (3) x2+y2=x′2+y′2(3)
由和角公式可知
t a n ( θ + α ) = t a n θ + t a n α 1 − t a n θ t a n α tan(\theta + \alpha)={{tan\theta+tan\alpha} \over {1-tan\theta tan\alpha}} tan(θ+α)=1−tanθtanαtanθ+tanα​
代入(1)和(2)可得
y ′ = x ′ ( y + x t a n θ ) x − y t a n θ ( 4 ) y'={{x'(y+xtan\theta)} \over {x-ytan\theta}} \quad \quad \quad (4) y′=x−ytanθx′(y+xtanθ)​(4)
(4)代入(3)得到:
x ′ 2 = ( x c o s θ − y s i n θ ) 2 ( 5 ) x'^2=(xcos\theta-ysin\theta)^2 \quad \quad \quad (5) x′2=(xcosθ−ysinθ)2(5)
(5)代入(3)
y ′ 2 = ( x s i n θ + y c o s θ ) 2 y'^2=(xsin\theta+ycos\theta)^2 y′2=(xsinθ+ycosθ)2
所以
{ x ′ = x c o s θ + y ( − s i n θ ) y ′ = x s i n θ + y c o s θ ( 6 ) \begin{cases} x '=xcos\theta +y(-sin\theta) \\ y'=xsin\theta + ycos \theta \end{cases} \quad \quad \quad (6) {x′=xcosθ+y(−sinθ)y′=xsinθ+ycosθ​(6)
我们知道仿射变换矩阵可以表示为:
A = [ c o s θ − s i n θ s i n θ c o s θ ] (7) A=\left[ \begin{matrix} cos\theta & -sin\theta \\ sin\theta & cos \theta \\ \end{matrix} \right] \tag{7} A=[cosθsinθ​−sinθcosθ​](7)
公式7与公式6在形式上是一致的(这里没有加入平移和缩放)
注意公式6中 x , y , x ′ , y ′ x,y,x',y' x,y,x′,y′实际表示 x − 0 , y − 0 , x ′ − 0 , y ′ − 0 x-0,y-0,x'-0,y'-0 x−0,y−0,x′−0,y′−0即与旋转中心横坐标以及纵坐标之间的距离,现在我们将旋转中心变为更具有一般性的 ( x 0 , y 0 ) (x_0,y_0) (x0​,y0​),得旋转公式为:
{ x ′ = ( x − x 0 ) c o s θ + ( y − y 0 ) ( − s i n θ ) + x 0 y ′ = ( x − x 0 ) s i n θ + ( y − y 0 ) c o s θ + y 0 ( 8 ) \begin{cases} x '=(x-x_0)cos\theta +(y-y_0)(-sin\theta)+x_0 \\ y'=(x-x_0)sin\theta + (y-y_0)cos \theta+y_0 \end{cases} \quad \quad \quad (8) {x′=(x−x0​)cosθ+(y−y0​)(−sinθ)+x0​y′=(x−x0​)sinθ+(y−y0​)cosθ+y0​​(8)
对于图像来说,规定顺时针旋转角度为正,则旋转公式与上述公式一致,由于任意角度的旋转后会出现像素坐标为负的情况,如果不将旋转后的图像坐标平移,会缺失部分图像,所以,对于旋转后的图像,我们通常会加入一个平移
{ x ′ = ( x − x 0 ) c o s θ + ( y − y 0 ) ( − s i n θ ) + x 0 + Δ x y ′ = ( x − x 0 ) s i n θ + ( y − y 0 ) c o s θ + y 0 + Δ y ( 9 ) \begin{cases} x '=(x-x_0)cos\theta +(y-y_0)(-sin\theta)+x_0+\Delta x \\ y'=(x-x_0)sin\theta + (y-y_0)cos \theta+y_0+\Delta y \end{cases} \quad \quad \quad (9) {x′=(x−x0​)cosθ+(y−y0​)(−sinθ)+x0​+Δxy′=(x−x0​)sinθ+(y−y0​)cosθ+y0​+Δy​(9)

图像中使用后向映射,使用 x ′ , y ′ x',y' x′,y′表示 x , y x,y x,y,则上述公式变为
{ x = ( ( x ′ − x 0 − Δ x ) c o s θ + ( y ′ − y 0 − Δ y ) s i n θ ) / s c a l e + x 0 y = ( ( x ′ − x 0 − Δ x ) ( − s i n θ ) + ( y ′ − y 0 − Δ y ) c o s θ ) / s c a l e + y 0 ( 10 ) \begin{cases} x=((x'-x_0-\Delta x)cos\theta +(y'-y_0-\Delta y)sin\theta)/scale+x_0 \\ y=((x'-x_0-\Delta x)(-sin\theta) + (y'-y_0-\Delta y)cos \theta)/scale+y_0 \end{cases} \quad \quad \quad (10) {x=((x′−x0​−Δx)cosθ+(y′−y0​−Δy)sinθ)/scale+x0​y=((x′−x0​−Δx)(−sinθ)+(y′−y0​−Δy)cosθ)/scale+y0​​(10)
这里加入了缩放,其中:scale>1表示原图放大 <1表示原图缩小
有了上面的公式,下面就可以写出相应的代码了

注:为什么公式推导的时候选用右手系?因为右手系与图像坐标系推导出来的公式在形式上是统一的,而右手系又是我们熟悉的坐标系。


图像旋转的实现

最近邻插值

先给出一个结构最简洁的版本,采用最近邻插值

// 宏定义
#define DEGREE2RADIAN(x) (x*CV_PI/180)//角度转弧度
#define RADIAN2DEGREE(x) (x*180/CV_PI)//弧度转角度
#define  SHIFT  10
#define  DESCALE(x,n)  (((x)+(1 << ((n)-1))) >> (n))
/*  center:原图像的旋转中心dstSize:旋转后图像的大小theta:旋转角度,单位弧度,顺时针为正scale:缩放,scale>1表示放大  <1表示缩小
*/
void Rotate_Nearest(const Mat &srcImage, Mat &dstImage, Point center, Size dstSize, double theta, double scale)
{CV_Assert(srcImage.depth() == CV_8U);dstImage.create(dstSize, srcImage.type());int x0 = center.x;int y0 = center.y;theta = DEGREE2RADIAN(theta);// dx,dy就是dst与src图像中心的距离 int dx = dstImage.cols/2 - srcImage.cols/2;int dy = dstImage.rows/2 - srcImage.rows/2;int numberOfChannels = srcImage.channels();int widthOfDst = dstImage.cols;int heightOfDst = dstImage.rows;for (int y = 0; y <= heightOfDst - 1; ++y){for (int x = 0; x <= widthOfDst - 1; ++x){float srcX = ((x - x0 - dx)*cos(theta) + (y - y0 - dy)*sin(theta))/scale + x0;float srcY = ((x0 + dx - x)*sin(theta) + (y - y0 - dy)*cos(theta))/scale + y0;// get the nearest coordinate of srcint x1 = (int)srcX;int y1 = (int)srcY;if (numberOfChannels == 1){if ((x1 >= 0 && x1 <= srcImage.cols - 1) && (y1 >= 0 && y1 <= srcImage.rows - 1)){dstImage.at<uchar>(y, x) = srcImage.at<uchar>(y1, x1);}else{// 越界赋值0dstImage.at<uchar>(y, x) = 0;}}else{if ((x1 >= 0 && x1 <= srcImage.cols - 1) && (y1 >= 0 && y1 <= srcImage.rows - 1)){dstImage.at<cv::Vec3b>(y, x) = srcImage.at<cv::Vec3b>(y1, x1);}else{dstImage.at<cv::Vec3b>(y, x) = cv::Vec3b(0,0,0);}}}}}

测试使用的原始图像大小为1000 x 580 (三通道彩色图)
测试代码:
Rotate_Nearest(srcImage, dstImage, Point(srcImage.cols / 2, srcImage.rows / 2), Size(2500, 2500), 30.0, 2);
效果:
原图:

结果:

无缩放测试:
Rotate_Nearest(srcImage, dstImage, Point(srcImage.cols / 2, srcImage.rows / 2), Size(2500, 2500), 30.0, 1);
结果:

双线性插值

下面我们对他进行优化,首先我们将最近邻插值改用更为通用的双线性插值,代码如下

void Rotate_Bilinear(const Mat &srcImage, Mat &dstImage, Point center, Size dstSize, double theta, double scale)
{CV_Assert(srcImage.depth() == CV_8U);dstImage.create(dstSize, srcImage.type());dstImage.setTo(Scalar(0, 0, 0));int x0 = center.x;int y0 = center.y;theta = DEGREE2RADIAN(theta);// Mat extendedImage;copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_CONSTANT,Scalar(0,0,0)); // 使用0填充边界// dx,dy就是dst与src图像中心的距离 int dx = dstImage.cols / 2 - srcImage.cols / 2;int dy = dstImage.rows / 2 - srcImage.rows / 2;int numberOfChannels = srcImage.channels();int widthOfDst = dstImage.cols;int heightOfDst = dstImage.rows;for (int y = 0; y <= heightOfDst - 1; ++y){for (int x = 0; x <= widthOfDst - 1; ++x){// 按照原来的方式计算原图坐标float srcX = ((x - x0 - dx)*cos(theta) + (y - y0 - dy)*sin(theta)) / scale + x0;float srcY = ((x0 + dx - x)*sin(theta) + (y - y0 - dy)*cos(theta)) / scale + y0;// 加1,得到在extendedImage中的坐标srcX++; srcY++;// get the nearest coordinate of srcint x1 = (int)(srcX); int y1 = (int)(srcY);// 浮点转化为整数int dx1 = (srcX - x1)*(1<< SHIFT);int dy1 = (srcY - y1)*(1<< SHIFT);if (numberOfChannels == 1){// !!!注意这里的范围,在extendedImage中,原图的范围就是1~cols - 2了if ((x1 >= 1 && x1 <= extendedImage.cols - 2) && (y1 >= 1 && y1 <= extendedImage.rows - 2)){    //双线性插值//周围4个点//a就是最近邻像素//a   b//  p//c   duchar a = extendedImage.at<uchar>(y1, x1);uchar b = extendedImage.at<uchar>(y1, x1 + 1);uchar c = extendedImage.at<uchar>(y1 + 1, x1);uchar d = extendedImage.at<uchar>(y1 + 1, x1 + 1);//int p = (a*((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b*dx1*((1 << SHIFT) - dy1) + c*((1 << SHIFT) - dx1)*dy1 + d*dx1*dy1)/(1<<(2* SHIFT));int p = a*((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b*dx1*((1 << SHIFT) - dy1) + c*((1 << SHIFT) - dx1)*dy1 + d*dx1*dy1;p = DESCALE(p, 2*SHIFT);dstImage.at<uchar>(y, x) = p;}else{// 越界赋值0dstImage.at<uchar>(y, x) = 0;}}else{if ((x1 >= 1 && x1 <= extendedImage.cols - 2) && (y1 >= 1 && y1 <= extendedImage.rows - 2)){//双线性插值//周围4个点//a就是最近邻像素//a   b//  p//c   dVec3b a = extendedImage.at<Vec3b>(y1, x1);Vec3b b = extendedImage.at<Vec3b>(y1, x1 + 1);Vec3b c = extendedImage.at<Vec3b>(y1 + 1, x1);Vec3b d = extendedImage.at<Vec3b>(y1 + 1, x1 + 1);/*int p1 = (a[0] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[0] * dx1*((1 << SHIFT) - dy1) + c[0] * ((1 << SHIFT) - dx1)*dy1 + d[0] * dx1*dy1)/(1<<(2*SHIFT));int p2 = (a[1] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[1] * dx1*((1 << SHIFT) - dy1) + c[1] * ((1 << SHIFT) - dx1)*dy1 + d[1] * dx1*dy1)/ (1 << (2 * SHIFT));int p3 = (a[2] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[2] * dx1*((1 << SHIFT) - dy1) + c[2] * ((1 << SHIFT) - dx1)*dy1 + d[2] * dx1*dy1)/ (1 << (2 * SHIFT));*/int p1 = a[0]*((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[0]*dx1*((1 << SHIFT) - dy1) + c[0]*((1 << SHIFT) - dx1)*dy1 + d[0]*dx1*dy1;p1 = DESCALE(p1, 2 * SHIFT);int p2 = a[1]*((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[1]*dx1*((1 << SHIFT) - dy1) + c[1]*((1 << SHIFT) - dx1)*dy1 + d[1] *dx1*dy1;p2 = DESCALE(p2, 2 * SHIFT);int p3 = a[2]*((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[2]*dx1*((1 << SHIFT) - dy1) + c[2]*((1 << SHIFT) - dx1)*dy1 + d[2] *dx1*dy1;p3 = DESCALE(p3, 2 * SHIFT);dstImage.at<cv::Vec3b>(y, x) = Vec3b(p1,p2,p3);}else{dstImage.at<cv::Vec3b>(y, x) = cv::Vec3b(0, 0, 0);}}}}
}

测试方法与上述相同,测试代码如下:
Rotate_Bilinear(srcImage, dstImage, Point(srcImage.cols / 2, srcImage.rows / 2), Size(2500, 2500),30.0, 2);
基本旋转效果都是一样的,下面我们看局部放大图

这是最近邻的眼部区域

双线性在保持图像细节方面要好于最近邻,而且不容易产生锯齿

双线性的优化

上面的双线性插值速度比较慢,我们对其进行优化,主要优化有:
1.将循环内部不变量提取出来
由于sin和cos的计算是比较慢的函数,所以可以将他们提前计算好,而不是每次循环都要计算
如:
double sinTheta = sin(theta);
double cosTheta = cos(theta);

2.改变循环体内循环变量自增的方式
采用加法自增的方式,而不是每次都要计算乘法,详见代码

注:浮点数转化为整数的优化,上面已经涉及,这里不再说明

优化后的代码如下:

void Rotate_Bilinear2(const Mat &srcImage, Mat &dstImage, Point center, Size dstSize, double theta, double scale)
{CV_Assert(srcImage.depth() == CV_8U);dstImage.create(dstSize, srcImage.type());dstImage.setTo(Scalar(0, 0, 0));int x0 = center.x;int y0 = center.y;theta = DEGREE2RADIAN(theta);// Mat extendedImage;copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(0, 0, 0)); // 使用0填充边界// dx,dy就是dst与src图像中心的距离 int dx = dstImage.cols / 2 - srcImage.cols / 2;int dy = dstImage.rows / 2 - srcImage.rows / 2;int numberOfChannels = srcImage.channels();int widthOfDst = dstImage.cols;int heightOfDst = dstImage.rows;// 优化部分/// 将循环内的不变量提取出来double sinTheta = sin(theta);double cosTheta = cos(theta);scale = 1.0 / scale;// 改变了循环内部增量的方式double temp1= (0 - y0 - dy)*sinTheta;double temp2 = (0 - y0 - dy)*cosTheta;double dtemp1 = sinTheta;double dtemp2 = cosTheta;for (int y = 0; y <= heightOfDst - 1; ++y,temp1+=dtemp1,temp2+=dtemp2){// 改变了循环内部增量的方式double temp3= ((0 - x0 - dx)*cosTheta + temp1)*scale + x0;double temp4= (-(0 - x0 - dx)*sinTheta + temp2)*scale + y0;double dtemp3 = (cosTheta)*scale;double dtemp4= (-sinTheta)*scale;for (int x = 0; x <= widthOfDst - 1; ++x,temp3+=dtemp3,temp4+=dtemp4){// 计算原图坐标double srcX = temp3;double srcY = temp4;// 加1,得到在extendedImage中的坐标srcX++;srcY++;// get the nearest coordinate of srcint x1 = (int)(srcX);int y1 = (int)(srcY);// 浮点转化为整数int dx1 = (srcX - x1)*(1 << SHIFT);int dy1 = (srcY - y1)*(1 << SHIFT);if (numberOfChannels == 1){// !!!注意这里的范围,在extendedImage中,原图的范围就是1~cols - 2了if ((x1 >= 1 && x1 <= extendedImage.cols - 2) && (y1 >= 1 && y1 <= extendedImage.rows - 2)){//双线性插值//周围4个点//a就是最近邻像素//a   b//  p//c   duchar a = extendedImage.at<uchar>(y1, x1);uchar b = extendedImage.at<uchar>(y1, x1 + 1);uchar c = extendedImage.at<uchar>(y1 + 1, x1);uchar d = extendedImage.at<uchar>(y1 + 1, x1 + 1);//int p = (a*((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b*dx1*((1 << SHIFT) - dy1) + c*((1 << SHIFT) - dx1)*dy1 + d*dx1*dy1)/(1<<(2* SHIFT));int p = a*((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b*dx1*((1 << SHIFT) - dy1) + c*((1 << SHIFT) - dx1)*dy1 + d*dx1*dy1;p = DESCALE(p, 2 * SHIFT);dstImage.at<uchar>(y, x) = p;}else{// 越界赋值0dstImage.at<uchar>(y, x) = 0;}}else{if ((x1 >= 1 && x1 <= extendedImage.cols - 2) && (y1 >= 1 && y1 <= extendedImage.rows - 2)){//双线性插值//周围4个点//a就是最近邻像素//a   b//  p//c   dVec3b a = extendedImage.at<Vec3b>(y1, x1);Vec3b b = extendedImage.at<Vec3b>(y1, x1 + 1);Vec3b c = extendedImage.at<Vec3b>(y1 + 1, x1);Vec3b d = extendedImage.at<Vec3b>(y1 + 1, x1 + 1);/*int p1 = (a[0] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[0] * dx1*((1 << SHIFT) - dy1) + c[0] * ((1 << SHIFT) - dx1)*dy1 + d[0] * dx1*dy1)/(1<<(2*SHIFT));int p2 = (a[1] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[1] * dx1*((1 << SHIFT) - dy1) + c[1] * ((1 << SHIFT) - dx1)*dy1 + d[1] * dx1*dy1)/ (1 << (2 * SHIFT));int p3 = (a[2] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[2] * dx1*((1 << SHIFT) - dy1) + c[2] * ((1 << SHIFT) - dx1)*dy1 + d[2] * dx1*dy1)/ (1 << (2 * SHIFT));*/int p1 = a[0] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[0] * dx1*((1 << SHIFT) - dy1) + c[0] * ((1 << SHIFT) - dx1)*dy1 + d[0] * dx1*dy1;p1 = DESCALE(p1, 2 * SHIFT);int p2 = a[1] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[1] * dx1*((1 << SHIFT) - dy1) + c[1] * ((1 << SHIFT) - dx1)*dy1 + d[1] * dx1*dy1;p2 = DESCALE(p2, 2 * SHIFT);int p3 = a[2] * ((1 << SHIFT) - dx1)*((1 << SHIFT) - dy1) + b[2] * dx1*((1 << SHIFT) - dy1) + c[2] * ((1 << SHIFT) - dx1)*dy1 + d[2] * dx1*dy1;p3 = DESCALE(p3, 2 * SHIFT);dstImage.at<cv::Vec3b>(y, x) = Vec3b(p1, p2, p3);}else{dstImage.at<cv::Vec3b>(y, x) = cv::Vec3b(0, 0, 0);}}}}
}

下面我们测试一下他们的速度
测试环境:Intel core i5-6200U,12G,放大2倍

测试方法 Rotate_Nearest Rotate_Bilinear Rotate_Bilinear2
速度(单位ms) 58.2 144.9 78.7

可以看出,优化后的代码速度提升还是很明显的。

双线性代码还可以进一步的优化,有兴趣的朋友可以自己实现。

完整工程见github项目:QQImageProcess_OpenCV
其中图像旋转优化的实现在 Src/ImageProcess/GeometryTransformation.h中的Rotate_系列函数中。


2016-9-11 15:12:24
Last Updated:2017-3-11 23:22:16


非常感谢您的阅读,如果您觉得这篇文章对您有帮助,欢迎扫码进行赞赏。

图像旋转的原理,实现与优化相关推荐

  1. 图像旋转的原理与实现

    图像旋转的原理与实现 图像旋转的原理与实现 一般图像的旋转是以图像的中心为原点,旋转一定的角度,也就是将图像上的所有像素都旋转一个相同的角度.旋转后图像的的大小一般会改变,即可以把转出显示区域的图像截 ...

  2. 经验 | OpenCV图像旋转的原理与技巧

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自|OpenCV学堂 01 引言 初学图像处理,很多人遇到的 ...

  3. OpenCV图像旋转的原理与技巧

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 转自|OpenCV学堂 01 引言 初学图像处理,很多人遇到的第一 ...

  4. 小白学习图像处理3——图像旋转原理

    文章目录 一.图像旋转的原理 二.使用matlab实现 1.思路 2.实现代码 三.优化 1.思路 2.代码实现 3.使用双线性插值 四.matlab函数实现图像旋转 1.imrotate函数 2.i ...

  5. 基于双线性插值的图像旋转原理及MATLAB实现(非自带函数)

    目录 1.图像旋转的原理 1.1.旋转矩阵 1.2.双线性插值 1.3.像素点匹配 2.实现效果与说明 1.图像旋转的原理 1.1.旋转矩阵 旋转一幅图像(假设这幅图像大小是矩形的),当然应该从像素点 ...

  6. 【数字图像处理】MATLAB实现图像旋转

    前言 上节课学习了实现图像旋转的原理,下课后用matlab实现了一下图像旋转的功能,这里做个记录. 图像旋转原理 图像旋转的本质利用的是向量的旋转. 矩阵乘法的实质是进行线性变换,因此对一个向量进行旋 ...

  7. 学习OpenCV3——图像旋转算法实现

    图像旋转是非常常见的图像变换,通常应用于图像矫正,在OpenCV可以使用密集仿射变换函数cv::warpAffine()实现图像旋转.为了理解图像旋转的原理,本文实现了一个图像旋转算法. 图像旋转是指 ...

  8. 【嵌入式C编程】keil图像旋转仿真

    老爷们求求点个赞,我完全是自己想的,没有参考祖传代码. 一.题目描述与分析 题目:导入一幅128X128的8bit灰度图像,请在ARM 处理器上编程,使图像顺时针旋转45度,并导出图像: 分析:主要可 ...

  9. 二维图像旋转的坐标公式推导

    二维图像旋转后的坐标公式推导: 综上,图像顺时针和逆时针旋转的矩阵分别为: 对于此处红色字体的理解部分,有误解,图像没有翻转. 这个坐标转换只是让图像每个位置在某一坐标系的表达,转换到另一个坐标系上的 ...

最新文章

  1. FPGA之道(37)Verilog中的编写注意事项
  2. python-字符串常用方法、文件简单读写
  3. Libvirsh 问题:GLib-WARNING **: gmem.c:483: custom memory allocation vtable not supported
  4. 爬楼梯—leetcode70
  5. 如何修改服务器mac地址,如何修改服务器mac地址
  6. 微型计算机各部件之间通过总线传递各种信息,2015年9月计算机一级考试基础及MSOffice应用选择真题...
  7. 随想录(常用的音视频、图像库)
  8. 病毒及***防御手册之十二
  9. UIKit框架-高级控件Swift版本: 5.UITextView方法/属性详解
  10. java共同方法_java-现有公共方法的NoSuchMethodError
  11. 真机开包!国产至强5600服务器35张图赏
  12. CCF201412试题
  13. 【转】hive简介安装 配置常见问题和例子
  14. 如何写一个批量下载PDF文件的程序hp48
  15. 正点原子STM32(基于标准库)
  16. ECSHOP二次开发教程__连接
  17. Java项目中 实体类(ENTITY,VO,DTO)理解
  18. mysql 获取天数_MySQl 计算本年的天数
  19. 一种快速求解最大团问题的算法
  20. 1154 Vertex Coloring

热门文章

  1. Nokia提供的手机游戏以及内容的市场和渠道资料
  2. linux安装Keepalived
  3. Linux复习3 实验 教材 Linux 教程 第5版 燕山大学
  4. 为啥你们公司的后浪根本“浪”不起来?
  5. 上市公司急聘:PostgreSQL DBA
  6. 企业如何添加内嵌式的帮助文档
  7. java 生成一维码并打印
  8. ipython notebook_ipython notebook使用教程
  9. SYD8801低功耗【深度睡眠模式】【浅度睡眠模式】【进入睡眠模式后要等待硬件进入睡眠】【内部上拉电阻对功耗的影响】【测试低功耗步骤】
  10. SAP 工作台请求和定制请求的区别