原文链接:http://www.cnblogs.com/emouse/archive/2013/03/31/2991333.html

作者写作一系列:http://www.cnblogs.com/emouse/category/449213.html

Qt图像的缩放显示


实现图像缩放的方法很多,在 OpenCV&Qt学习之一——打开图片文件并显示 的例程中,label控件是通过

ui->imagelabel->resize(ui->imagelabel->pixmap()->size());

来实现适应图像显示的,但是由于窗口固定,在打开的图像小于控件大小时就会缩在左上角显示,在打开图像过大时则显示不全。因此这个例程中首先实现图像适合窗口的缩放显示。

由于是基于OpenCV和Qt的图像处理,因此图像的缩放处理在OpenCV和Qt都可以完成,我这里就把OpenCV用作图像的原始处理,Qt用作显示处理,因此缩放显示由Qt完成。

Qt中QImage提供了用于缩放的基本函数,而且功能非常强大,使用Qt自带的帮助可以检索到相关信息。

函数原型:

QImage QImage::scaled ( const QSize & size, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation ) const

这是直接获取大小,还有另一种形式:

QImage QImage::scaled ( int width, int height, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation ) const

函数说明以及参数在文档中已经说的非常清楚了,文档摘录如下:

Returns a copy of the image scaled to a rectangle defined by the givensize according to the givenaspectRatioMode andtransformMode.

  1. If aspectRatioMode is Qt::IgnoreAspectRatio, the image is scaled tosize.
  2. If aspectRatioMode is Qt::KeepAspectRatio, the image is scaled to a rectangle as large as possible insidesize, preserving the aspect ratio.
  3. If aspectRatioMode is Qt::KeepAspectRatioByExpanding, the image is scaled to a rectangle as small as possible outsidesize, preserving the aspect ratio.

官方文档中已经说的比较清楚了,代码实现也比较简单,代码如下:

{QImage imgScaled ;imgScaled = img.scaled(ui->imagelabel->size(),Qt::KeepAspectRatio);
//  imgScaled = img.QImage::scaled(ui->imagelabel->width(),ui->imagelabel->height(),Qt::KeepAspectRatio);ui->imagelabel->setPixmap(QPixmap::fromImage(imgScaled));
}

显示效果如下:

QImage的一点疑问与理解


在查找资料时参考了这篇  Qt中图像的显示与基本操作 博客,但是存在一些疑点,博客中相关代码如下:

QImage* imgScaled = new QImage;
*imgScaled=img->scaled(width,height,Qt::KeepAspectRatio);
ui->label->setPixmap(QPixmap::fromImage(*imgScaled));

对于以上代码通过和我之前的代码做简单对比,发现有几点不一样的地方:

  1. 图像的定义方式,这里的定义方式为QImage* imgScale = new QImage
  2. scaled函数的调用方式,一个是imgScaled = img.scaled后者为*imgScaled=img->scaled,我最开始也是将.写为->一直没找出错误,提示base operand of '->' has non-pointer type 'QImage'

继续查找Qt的帮助手册,发现QImage的构造函数还真是多:

Public Functions

QImage ()

QImage ( const QSize &size, Formatformat )

QImage ( int width, intheight, Format format )

QImage ( uchar *data, intwidth, intheight, Formatformat )

QImage ( const uchar *data, intwidth, intheight, Formatformat )

QImage ( uchar *data, intwidth, intheight, intbytesPerLine, Formatformat )

QImage ( const uchar *data, intwidth, intheight, intbytesPerLine, Formatformat )

QImage ( const char * const[]xpm )

QImage ( const QString &fileName, const char *format = 0 )

QImage ( const char *fileName, const char *format = 0 )

QImage ( const QImage &image )

~QImage ()

QImage提供了适用于不同场合的构造方式,在手册中对他们也有具体的应用,但是我仍然没找到QImage image;和QImage* image = new QImage这两种究竟对应的是哪两种,有什么区别和不同。 在上一篇博文 OpenCV&Qt学习之二——QImage的进一步认识  中提到了对于图像数据的一点认识,其中提到QImage是对现有数据的一种重新整合,是一种格式,但是数据还是指向原来的。从这里来看还需要根据构造方式具体区别,并不完全正确。

凌乱查了查资料,网上的资料就那几个,互相转来转去的,而且多数比较老,仍然没有帮助我想通关于这里面数据结构的一些疑问,Qt 和 OpenCV对C和指针的要求还是比较高的,长时间从单片机类的程序过来那点功底还真不够,具体的C应用都忘光了。这个问题只能暂时搁置,在后面的学习中慢慢理解。

基于OpenCV的图像初步处理


以下两个例程根据书籍 OpenCV 2 Computer Vision Application Programming Cookbook中的相关例程整理,这是一本比较新也比较基础的入门书籍。

salt-and-pepper noise

关于图像数据的基础知识参见这段介绍:

Fundamentally, an image is a matrix of numerical values. This is why OpenCV 2 manipulates them using the cv::Mat data structure. Each element of the matrix represents one pixel. For a gray-level image (a "black-and-white" image), pixels are unsigned 8-bit values where 0 corresponds to black and corresponds 255 to white. For a color image, three such values per pixel are required to represent the usual three primary color channels {Red, Green, Blue}. A matrix element is therefore made, in this case, of a triplet of values.

这儿以想图像中添加saltand-pepper noise为例,来说明如何访问图像矩阵中的独立元素。saltand-pepper noise就是图片中一些像素点,随机的被黑色或者白色的像素点所替代,因此添加saltand-pepper noise也比较简单,只需要随机的产生行和列,将这些行列值对应的像素值更改即可,当然通过上面的介绍,需要更改RGB3个通道。程序如下:

void Widget::salt(cv::Mat &image,int n)
{int i,j;for (int k=0; k<n; k++){i= qrand()%image.cols;j= qrand()%image.rows;if (image.channels() == 1) { // gray-level imageimage.at<uchar>(j,i)= 255;} else if (image.channels() == 3) { // color imageimage.at<cv::Vec3b>(j,i)[0]= 255;image.at<cv::Vec3b>(j,i)[1]= 255;image.at<cv::Vec3b>(j,i)[2]= 255;}}
}

对Win 7系统中的自带图像考拉进行处理后的效果如下图所示(程序是Ubuntu 12.04下的):

减少色彩位数

在很多处理中需要对图片中的所有像素进行遍历操作,采用什么方式进行这个操作是需要思考的问题,关于这个问题的论述可以参考下面一段简介:

Color images are composed of 3-channel pixels. Each of these channels corresponds to the intensity value of one of the three primary colors (red, green, blue). Since each of these values is an 8-bit unsigned char, the total number of colors is 256x256x256, which is more than 16 million colors. Consequently, to reduce the complexity of an analysis, it is sometimes useful to reduce the number of colors in an image. One simple way to achieve this goal is to simply subdivide the RGB space into cubes of equal sizes. For example, if you reduce the number of colors in each dimension by 8, then you would obtain a total of 32x32x32 colors. Each color in the original image is then assigned a new color value in the color-reduced image that corresponds to the value in the center of the cube to which it belongs.

这个例子就是通过操作每一个像素点来减少色彩的位数,基本内容在以上的英文引文中已经有了介绍,代码的实现也比较直接。在彩色图像中,3个通道的数据是依次排列的,每一行的像素三个通道的值依次排列,cv::Mat中的通道排列顺序为BGR,那么一个图像需要的地址块空间为uchar 宽×高×3.但是需要注意的是,有些处理器针对行数为4或8的图像处理更有效率,因此为了提高效率就会填充一些额外的像素,这些额外的像素不被显示和保存,值是忽略的。

实现这个功能的代码如下:

// using .ptr and []
void Widget::colorReduce0(cv::Mat &image, int div)
{int nl= image.rows; // number of linesint nc= image.cols * image.channels(); // total number of elements per linefor (int j=0; j<nl; j++){uchar* data= image.ptr<uchar>(j);for (int i=0; i<nc; i++){// process each pixel ---------------------data[i]= data[i]/div*div+div/2;// end of pixel processing ----------------} // end of line}
}

data[i]= data[i]/div*div+div/2; 通过整除的方式,就像素位数进行减少,这里没明白的是为啥后面还要加上div/2。

效果如下:

程序源代码:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);}Widget::~Widget()
{delete ui;
}void Widget::on_openButton_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,tr("Open Image"),".",tr("Image Files (*.png *.jpg *.bmp)"));qDebug()<<"filenames:"<<fileName;image = cv::imread(fileName.toAscii().data());ui->imgfilelabel->setText(fileName);//here use 2 ways to make a copy
//    image.copyTo(originalimg);          //make a copyoriginalimg = image.clone();        //clone the imgqimg = Widget::Mat2QImage(image);display(qimg);                      //display by the labelif(image.data){ui->saltButton->setEnabled(true);ui->originalButton->setEnabled(true);ui->reduceButton->setEnabled(true);}
}QImage Widget::Mat2QImage(const cv::Mat &mat)
{QImage img;if(mat.channels()==3){//cvt Mat BGR 2 QImage RGBcvtColor(mat,rgb,CV_BGR2RGB);img =QImage((const unsigned char*)(rgb.data),rgb.cols,rgb.rows,rgb.cols*rgb.channels(),QImage::Format_RGB888);}else{img =QImage((const unsigned char*)(mat.data),mat.cols,mat.rows,mat.cols*mat.channels(),QImage::Format_RGB888);}return img;
}void Widget::display(QImage img)
{QImage imgScaled;imgScaled = img.scaled(ui->imagelabel->size(),Qt::KeepAspectRatio);
//  imgScaled = img.QImage::scaled(ui->imagelabel->width(),ui->imagelabel->height(),Qt::KeepAspectRatio);ui->imagelabel->setPixmap(QPixmap::fromImage(imgScaled));
}void Widget::on_originalButton_clicked()
{qimg = Widget::Mat2QImage(originalimg);display(qimg);
}void Widget::on_saltButton_clicked()
{salt(image,3000);qimg = Widget::Mat2QImage(image);display(qimg);
}
void Widget::on_reduceButton_clicked()
{colorReduce0(image,64);qimg = Widget::Mat2QImage(image);display(qimg);
}
void Widget::salt(cv::Mat &image, int n)
{int i,j;for (int k=0; k<n; k++){i= qrand()%image.cols;j= qrand()%image.rows;if (image.channels() == 1){ // gray-level imageimage.at<uchar>(j,i)= 255;}else if (image.channels() == 3){ // color imageimage.at<cv::Vec3b>(j,i)[0]= 255;image.at<cv::Vec3b>(j,i)[1]= 255;image.at<cv::Vec3b>(j,i)[2]= 255;}}
}// using .ptr and []
void Widget::colorReduce0(cv::Mat &image, int div)
{int nl= image.rows; // number of linesint nc= image.cols * image.channels(); // total number of elements per linefor (int j=0; j<nl; j++){uchar* data= image.ptr<uchar>(j);for (int i=0; i<nc; i++){// process each pixel ---------------------data[i]= data[i]/div*div+div/2;// end of pixel processing ----------------} // end of line}
}复制代码复制代码#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QImage>
#include <QFileDialog>
#include <QTimer>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>using namespace cv;namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();private slots:void on_openButton_clicked();QImage Mat2QImage(const cv::Mat &mat);void display(QImage image);void salt(cv::Mat &image, int n);void on_saltButton_clicked();void on_reduceButton_clicked();void colorReduce0(cv::Mat &image, int div);void on_originalButton_clicked();private:Ui::Widget *ui;cv::Mat image;cv::Mat originalimg; //store the original imgQImage qimg;QImage imgScaled;cv::Mat rgb;
};#endif // WIDGET_H复制代码书中还给了其他十余种操作的方法:
复制代码#include <iostream>#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>// using .ptr and []
void colorReduce0(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint nc= image.cols * image.channels(); // total number of elements per linefor (int j=0; j<nl; j++) {uchar* data= image.ptr<uchar>(j);for (int i=0; i<nc; i++) {// process each pixel ---------------------data[i]= data[i]/div*div + div/2;// end of pixel processing ----------------} // end of line                   }
}// using .ptr and * ++
void colorReduce1(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint nc= image.cols * image.channels(); // total number of elements per linefor (int j=0; j<nl; j++) {uchar* data= image.ptr<uchar>(j);for (int i=0; i<nc; i++) {// process each pixel ---------------------*data++= *data/div*div + div/2;// end of pixel processing ----------------} // end of line                   }
}// using .ptr and * ++ and modulo
void colorReduce2(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint nc= image.cols * image.channels(); // total number of elements per linefor (int j=0; j<nl; j++) {uchar* data= image.ptr<uchar>(j);for (int i=0; i<nc; i++) {// process each pixel ---------------------int v= *data;*data++= v - v%div + div/2;// end of pixel processing ----------------} // end of line                   }
}// using .ptr and * ++ and bitwise
void colorReduce3(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint nc= image.cols * image.channels(); // total number of elements per lineint n= static_cast<int>(log(static_cast<double>(div))/log(2.0));// mask used to round the pixel valueuchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0for (int j=0; j<nl; j++) {uchar* data= image.ptr<uchar>(j);for (int i=0; i<nc; i++) {// process each pixel ---------------------*data++= *data&mask + div/2;// end of pixel processing ----------------} // end of line                   }
}// direct pointer arithmetic
void colorReduce4(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint nc= image.cols * image.channels(); // total number of elements per lineint n= static_cast<int>(log(static_cast<double>(div))/log(2.0));int step= image.step; // effective width// mask used to round the pixel valueuchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0// get the pointer to the image bufferuchar *data= image.data;for (int j=0; j<nl; j++) {for (int i=0; i<nc; i++) {// process each pixel ---------------------*(data+i)= *data&mask + div/2;// end of pixel processing ----------------} // end of line                   data+= step;  // next line}
}// using .ptr and * ++ and bitwise with image.cols * image.channels()
void colorReduce5(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint n= static_cast<int>(log(static_cast<double>(div))/log(2.0));// mask used to round the pixel valueuchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0for (int j=0; j<nl; j++) {uchar* data= image.ptr<uchar>(j);for (int i=0; i<image.cols * image.channels(); i++) {// process each pixel ---------------------*data++= *data&mask + div/2;// end of pixel processing ----------------} // end of line                   }
}// using .ptr and * ++ and bitwise (continuous)
void colorReduce6(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint nc= image.cols * image.channels(); // total number of elements per lineif (image.isContinuous())  {// then no padded pixelsnc= nc*nl; nl= 1;  // it is now a 1D array}int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));// mask used to round the pixel valueuchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0for (int j=0; j<nl; j++) {uchar* data= image.ptr<uchar>(j);for (int i=0; i<nc; i++) {// process each pixel ---------------------*data++= *data&mask + div/2;// end of pixel processing ----------------} // end of line                   }
}// using .ptr and * ++ and bitwise (continuous+channels)
void colorReduce7(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint nc= image.cols ; // number of columnsif (image.isContinuous())  {// then no padded pixelsnc= nc*nl; nl= 1;  // it is now a 1D array}int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));// mask used to round the pixel valueuchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0for (int j=0; j<nl; j++) {uchar* data= image.ptr<uchar>(j);for (int i=0; i<nc; i++) {// process each pixel ---------------------*data++= *data&mask + div/2;*data++= *data&mask + div/2;*data++= *data&mask + div/2;// end of pixel processing ----------------} // end of line                   }
}// using Mat_ iterator
void colorReduce8(cv::Mat &image, int div=64) {// get iteratorscv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();for ( ; it!= itend; ++it) {// process each pixel ---------------------(*it)[0]= (*it)[0]/div*div + div/2;(*it)[1]= (*it)[1]/div*div + div/2;(*it)[2]= (*it)[2]/div*div + div/2;// end of pixel processing ----------------}
}// using Mat_ iterator and bitwise
void colorReduce9(cv::Mat &image, int div=64) {// div must be a power of 2int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));// mask used to round the pixel valueuchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0// get iteratorscv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();// scan all pixelsfor ( ; it!= itend; ++it) {// process each pixel ---------------------(*it)[0]= (*it)[0]&mask + div/2;(*it)[1]= (*it)[1]&mask + div/2;(*it)[2]= (*it)[2]&mask + div/2;// end of pixel processing ----------------}
}// using MatIterator_
void colorReduce10(cv::Mat &image, int div=64) {// get iteratorscv::Mat_<cv::Vec3b> cimage= image;cv::Mat_<cv::Vec3b>::iterator it=cimage.begin();cv::Mat_<cv::Vec3b>::iterator itend=cimage.end();for ( ; it!= itend; it++) { // process each pixel ---------------------(*it)[0]= (*it)[0]/div*div + div/2;(*it)[1]= (*it)[1]/div*div + div/2;(*it)[2]= (*it)[2]/div*div + div/2;// end of pixel processing ----------------}
}void colorReduce11(cv::Mat &image, int div=64) {int nl= image.rows; // number of linesint nc= image.cols; // number of columnsfor (int j=0; j<nl; j++) {for (int i=0; i<nc; i++) {// process each pixel ---------------------image.at<cv::Vec3b>(j,i)[0]=     image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;image.at<cv::Vec3b>(j,i)[1]=     image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;image.at<cv::Vec3b>(j,i)[2]=     image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;// end of pixel processing ----------------} // end of line                   }
}// with input/ouput images
void colorReduce12(const cv::Mat &image, // input image cv::Mat &result,      // output imageint div=64) {int nl= image.rows; // number of linesint nc= image.cols ; // number of columns// allocate output image if necessaryresult.create(image.rows,image.cols,image.type());// created images have no padded pixelsnc= nc*nl; nl= 1;  // it is now a 1D arrayint n= static_cast<int>(log(static_cast<double>(div))/log(2.0));// mask used to round the pixel valueuchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0for (int j=0; j<nl; j++) {uchar* data= result.ptr<uchar>(j);const uchar* idata= image.ptr<uchar>(j);for (int i=0; i<nc; i++) {// process each pixel ---------------------*data++= (*idata++)&mask + div/2;*data++= (*idata++)&mask + div/2;*data++= (*idata++)&mask + div/2;// end of pixel processing ----------------} // end of line                   }
}// using overloaded operators
void colorReduce13(cv::Mat &image, int div=64) {int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));// mask used to round the pixel valueuchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0// perform color reductionimage=(image&cv::Scalar(mask,mask,mask))+cv::Scalar(div/2,div/2,div/2);
}

★emouse 思·睿博客文章★原创文章转载请注明:http://emouse.cnblogs.com

Qt:OpenCV—Q图像处理基本操作(Code)相关推荐

  1. OpenCV Mat 图像处理基本操作

    1. 图片加载.灰度图. 显示和保存 cv::Mat img = cv::imread("01.jpg"); //cv::Mat img = cv::imread("01 ...

  2. (3两个例子)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练

    从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练 1综述 http://www.cnblogs.com/jsxyhelu/p/7907241.html 2环境架设 http://www ...

  3. windows平台下基于QT和OpenCV搭建图像处理平台

        在之前的博客中,已经分别比较详细地阐述了"windows平台下基于VS和OpenCV"以及"Linux平台下基于QT和OpenCV"搭建图像处理框架,并 ...

  4. (2环境架设)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练

    从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练 1综述 http://www.cnblogs.com/jsxyhelu/p/7907241.html 2环境架设 http://www ...

  5. OpenCV图像处理--Qt+OpenCV环境搭建

    LinuxQt+Opencv 环境搭建参照 LinuxQT+第三方库配置 WindowsQt+OpenCV环境搭建 一.下载opencv环境 opencv环境下载 2.Qt+OpenCV环境测试 新建 ...

  6. QT+opencv学习笔记(5)——霍夫直线检测、圆检测及椭圆检测

    开发环境为:win10+QT5.8+opencv3.2 Hough变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛.最基本的Hough变换是从黑白图像中检测直线,还可以经过改进检测圆.椭 ...

  7. win10下搭建qt+opencv环境

    文章目录 1 qt 2 cmake 3 opencv 4 demo 5 Q&A 5.1 qt安装vcredist_xx报错 原本使用pyqt开发界面程序,但是pyinstall打包后的程序实在 ...

  8. OpenCV实战(1)——OpenCV与图像处理基础

    OpenCV实战(1)--OpenCV与图像处理基础 0. 前言 1. OpenCV 基础 1.1 安装 OpenCV 1.2 OpenCV 主要模块 1.3 使用 Qt 进行 OpenCV 开发 2 ...

  9. OpenCV学习笔记(二十一)——绘图函数core OpenCV学习笔记(二十二)——粒子滤波跟踪方法 OpenCV学习笔记(二十三)——OpenCV的GUI之凤凰涅槃Qt OpenCV学习笔记(二十

    OpenCV学习笔记(二十一)--绘图函数core 在图像中,我们经常想要在图像中做一些标识记号,这就需要绘图函数.OpenCV虽然没有太优秀的GUI,但在绘图方面还是做得很完整的.这里就介绍一下相关 ...

最新文章

  1. 函数指针与指针函数的使用与小结
  2. 程序员面试题精选100题(63)-数组中三个只出现一次的数字[算法]
  3. YumRepo Error: All mirror URLs are not using问题解决
  4. 【题解】弃疗Nim (2019,5.23)
  5. html跳动的小球,canvas绘制跳动的小球
  6. 项目还有哪些优化的地方
  7. js隐藏和显示div
  8. 搭建邮件系统,采用sendmail+dovecot+openwebmail架构
  9. tomcat之servlet容器
  10. vue + 生成 下载 成 二维码
  11. Win10 卡在 微软账号循环登录界面解决方案
  12. 如何将视频中的音频提取出来?
  13. 一分钟看懂Python中的 // 和 / 和 % 的用法区别
  14. 首届“网刃杯”网络安全大赛部分WP
  15. json解析到map
  16. 千万级Feed流系统——阿里数据库技术解读
  17. layui富文本编辑器(layedit)的使用
  18. Django2.0官方文档--概览
  19. 利用python快速开发一个上位机软件
  20. 【技术篇】文件的md5值

热门文章

  1. navicat下载安装和激活一分钟完成
  2. ***F漏洞分析与利用
  3. [大数据之Yarn]——资源调度浅学
  4. hadoop自定义key,value
  5. Cocos数据篇[3.4](4) ——plist文件操作
  6. (技能篇)双机热备之Oracle切换故障处理
  7. nginx配置 yii2 URL重写规则 SSI配置使shtml
  8. Linux 配置yum本地安装源
  9. 从1.5k到18k, 一个程序员的5年成长之路(分享)
  10. mybatis中的智能标签之二