图像基本数据结构

要讲图片格式还先得从图像的基本数据结构说起。在计算机中, 图像是由一个个像素点组成,像素点就是颜色点,而颜色最简单的方式就是用RGB或RGBA表示, 如图所示

(图1)

(图2)

如果有A通道就表明这个图像可以有透明效果。

R,G,B每个分量一般是用一个字节(8位)来表示,所以图(1)中每个像素大小就是3*8=24位图, 而图(2)中每个像素大小是4*8=32位。

这里有三点需要说明:

一、图像y方向正立或倒立

图像是二维数据,数据在内存中只能一维存储,二维转一维有不同的对应方式。比较常见的只有两种方式: 按像素“行排列”从上往下或者从下往上。

如图所示的图像有9个像素点,如果从上往下排列成一维数据是(123456789), 如果是从下往上排列则为(789456123)。
只所以会有这种区别是因为,前一种是以计算机图形学的屏幕坐标系为参考(右上为原点,y轴向下 ),而另后一种是以标准的数学坐标系为参考(右下为原点,y轴向上)。这两个坐标系只是y值不一样,互相转换的公式为:

y2 = height-1-y1

y1,y2分别为像素在两个坐标系中的y坐标,height为图像的高度。

不过好像只有bmp图片格式以及windows下的GDI,GDI+是从下往上排列,其它比如DirectX,OpenGL,Cocoa(NSImage, UIImage),OpenCV等都是从上往下排列。

二、RGB排列顺序

不同图形库中每个像素点中RGBA的排序顺序可能不一样。上面说过像素一般会有RGB,或RGBA四个分量,那么在内存中RGB的排列就有6种情况,如下:

  • RGB
  • RBG
  • GRB
  • GBR
  • BGR
  • BRG

RGBA的排列有24种情况,这里就不全部列出来了。
不过一般只会有RGB,BGR, RGBA, RGBA, BGRA这几种排列据。 绝大多数图形库或环境是BGR/BGRA排列,cocoa中的NSImage或UIImage是RGBA排列。

三、像素32位对齐

如果是RGB24位图,会存在一个32位对齐的问题——
在x86体系下,cpu一次处理32整数倍的数据会更快,图像处理中经常会按行为单位来处理像素。24位图,宽度不是4的倍数时,其行字节数将不是32整数倍。这时可以采取在行尾添加冗余数据的方式,使其行字节数为32的倍数。
比如,如果图像宽为5像素,不做32位对齐的话,其行位数为24*5=120,120不是32的倍数。是32整数倍并且刚好比120大的数是128,也就只需要在其行尾添加1字节(8位)的冗余数据即可。(一个以空间换时间的例子)
有个公式可以轻松计算出32位对齐后每行应该占的字节数

byteNum = ((width * 24 + 31) & ~31)>>3;

注意结果是字节数,如果想知道位数,还得x8

图片格式的必要性

如果将图像原始格式直接存储到文件中将会非常大,比如一个5000*5000 24位图,所占文件大小为5000*5000*3字节=71.5MB, 其大小非常可观。
如果用zip或rar之类的通用算法来压缩像素数据,得到的压缩比例通常不会太高,因为这些压缩算法没有针对图像数据结构进行特殊处理。
于是就有了jpeg,png等格式,同样是图像压缩算法jpeg和png也有不同的适用场景,具体在下文再阐述。

所以可以总结如下: jpeg,png文件之于图像,就相当于zip,rar格式之于普通文件(用zip,rar格式对普通文件进行压缩)。

BMP格式

bmp格式没有压缩像素格式,存储在文件中时先有文件头、再图像头、后面就都是像素数据了,上下颠倒存储。
用windows自带的mspaint工具保存bmp格式时,可以发现有四种bmp可供选择:
单色: 一个像素只占一位,要么是0,要么是1,所以只能存储黑白信息
16色位图: 一个像素4位,有16种颜色可选
256色位图: 一个像素8位,有256种颜色可选
24位位图: 就是图(1)所示的位图,颜色可有2^24种可选,对于人眼来说完全足够了。

这里为了简单起见,只详细讨论最常见的24位图的bmp格式。

现在来看其文件头和图片格式头的结构:

                      

文件头信息

字段 大小(字节) 描述 bfType  2 一定为19778,其转化为十六进制为0x4d42,对应的字符串为BM bfSize  4 文件大小  bfReserved1 2  一般为0   bfReserved2 2  一般为0   bfOffBits  4  从文件开始处到像素数据的偏移,也就是这两个结构体大小之和

bmp图片结构头

字段 大小(字节)  描述   biSize  4 此结构体的大小   biWidth  4  图像的宽 biHeight   4 图像的高  biPlanes   2 图像的帧数,一般为1  biBitCount   2 一像素所占的位数,一般是24 biCompression   4 一般为0  biSizeImage   4 像素数据所占大小,即上面结构体中文件大小减去偏移(bfSize-bfOffBits)  biXPelsPerMeter   4  一般为0 biXPelsPerMeter   4   一般为0 biClrUsed   4   一般为0 biClrImportant   4   一般为0

本来在windows平台下wingdi.h文件中已经有这些结构的定义,不过为了不依赖与windows,实现为跨平台,本人将wingdi.h中的这两个结构“偷用”出来了。代码如下:

 1 //bmp文件头2 #pragma pack(push)3 #pragma pack(2)4     typedef struct tagBITMAPFILEHEADER {5         unsigned short bfType;      // 19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为197786         unsigned int bfSize;        // 文件大小7         unsigned short bfReserved1; // 08         unsigned short bfReserved2; // 09         unsigned int bfOffBits;     // 从文件头到像素数据的偏移,也就是这两个结构体的大小之和
10     } BITMAPFILEHEADER;
11 #pragma pack(pop)
12
13 //bmp图像头
14 typedef struct tagBITMAPINFOHEADER {
15     unsigned int biSize;        // 此结构体的大小
16     int biWidth;                // 图像的宽
17     int biHeight;               // 图像的高
18     unsigned short biPlanes;    // 1
19     unsigned short biBitCount;  // 24
20     unsigned int biCompression; // 0
21     unsigned int biSizeImage;   // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits
22     int biXPelsPerMeter;        // 0
23     int biYPelsPerMeter;        // 0
24     unsigned int biClrUsed;     // 0
25     unsigned int biClrImportant;// 0
26 } BITMAPINFOHEADER;

Bmp结构体

由于bmp格式比较简单,本人已实现了一份简单的c++代码,具有读取、保存bmp图片的功能,只支持24位的bmp格式。

代码在 http://git.oschina.net/xiangism/blogData 的“常见图片格式详解/ImageDemo/BmpDemo”文件夹中。

虽然这里只建立了vs2008项目,但代码在linux, mac平台下都可以编译通过。

需要说明的是为了统一处理,将bmp读取到LBitmap::m_pixel中时就将其转化为32位从上往下排列的图像格式了。并且会有y坐标的转化。
所以在读取的时候会有一个temp_line先存储文件中的24位数据,再转化为32位数据。在保存时也是先将32位数据转化到temp_line的24位数据上,然后再写入文件。(如果仅仅是处理bmp,那么这么多的一个A通道是冗余数据,但后面处理png图片时就会用到这个A通道)

如果用上面的代码来读取如图所示的图片(放大8倍后的显示图):

右上角像素为RGB(255, 128, 0)

1 ln::LBitmap bmp;
2 bmp.ReadBmp(L"one.bmp");
3 unsigned char *p = bmp.Pixel(0, 0);
4 printf("%d, %d, %d\n", p[0], p[1], p[2]); //显示左上角的像素值
5 bmp.WriteBmp(L"out.bmp");                 //保存到文件,可以测试是否能正确读取和保存bmp

运行的结果为: 0,128,255
可以看出像素分布为BGR

ps:

  • bmp格式也是可以压缩.
  • bmp格式也可以有颜色板。颜色板就是一个颜色的索引,上面说过bmp格式一个像素可以只有2个,16个或256个取值。就拿单色位图来说明,默认为0对应RGB(0,0,0) 1,对应RGB(255, 255, 255)
    如果颜色板这样定义: 
    0对应 RGB(255,0, 0)红
    1对应 RGB(0, 255, 0)绿
    这样黑白图就成了红绿图

JPEG格式

  1. jpeg是有损压缩格式, 将像素信息用jpeg保存成文件再读取出来,其中某些像素值会有少许变化。在保存时有个质量参数可在[0,100]之间选择,参数越大图片就越保真,但图片的体积也就越大。一般情况下选择70或80就足够了。
  2. jpeg没有透明信息。
  3. jpeg比较适合用来存储相机拍出来的照片,这类图像用jpeg压缩后的体积比较小。其使用的具体算法核心是离散余弦变换、Huffman编码、算术编码等技术,有兴趣的同学可以在网上找一大堆资料,本文就不详细介绍了。

接下来要介绍一个有关jpeg非常实用的技术——
jpeg格式支持不完全读取整张图片,即可以选择读取原图、1/2、1/4、1/8大小的图片
比如5000*5000的一张大图,可以只读取将其缩小成1/8后即625*625大小的图片。 这样比先完全读取5000*5000的图像,再用算法缩小成625*625大小不知快多少倍。
如果应用需求只需要一张小图时,这种读取方式就可以大显身手了。

在c代码中读取jpeg一般是使用libjpeg, 这个库提供了不完全读取图片的功能。

给ln::LBitmap添加有关jpeg的接口,如下ReadJpeg()第三个参数fraction可取值为1,2,4,8,分别对应1/1,1/2,1/4,1/8

```
在上面LBitmap的基本上加入下面5个函数:
// 不读取像素数据,只读取jpeg文件的大小, 用指针*width, *height做为传出参数, 返回值bool返回文件是否为jpeg格式
static bool ReadJpegSize(const wchar_t *path, int *width, int *height);

// 判断文件是否为jpeg格式
static bool IsJpegFile(const wchar_t *fileame);

// 读取jpeg,fraction可取值为1, 2, 4, 8
bool ReadJpeg(const wchar_t *filename, int fraction = 1);

// 按照width, height的大小读取合适的jpeg大小, 所得的图像大小不会超过width*height
bool ReadFitJpeg(const wchar_t *filename, int width, int height);

// 保存jpeg,quality范围是[0, 100]
bool WriteJpeg(const wchar_t *filename, int quality = 80

JpegAPI

具体的实现在JpegDemo
用上面的函数进行jpeg的读取和保存的测试

```
ln::LBitmap bmp;
bmp.ReadBmp(L"one.bmp");
unsigned char *p = bmp.Pixel(0, 0);
printf("%d, %d, %d\n", p[0], p[1], p[2]);
bmp.WriteJpeg(L"one.jpg", 90);
```

读取one.bmp图片,然后保存成jpeg格式,one.jpg放大后显示如下

发现左上角的颜色发生了变化,并且也影响到周围的像素,就算将上面WriteJpeg()第二个参数换成100,也还是这种效果,这是Jpeg格式无法避免的问题
但如果读取一张风景照,再保存成Jpeg,就几乎看不出有什么差别了。

android平台下实现jpeg预读

BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeFile(info.fullPath, opt); //这里仅仅只读取jpeg的大小
opt.inJustDecodeBounds = false;
if (opt.outWidth > opt.outHeight) {opt.inSampleSize = opt.outWidth / phSize;//hpSize是允许的图片宽高的最大值
} else {opt.inSampleSize = opt.outHeight / phSize;
}
Bitmap b = BitmapFactory.decodeFile(info.fullPath, opt); 

将BitmapFactory.Options的inJustDecodeBounds 设置为true后,就只会读取Jpeg的大小,而不会去解析像素数据。然后再设置inSampleSize后,就可以根据这个值来读取适当大小的图片,研究android的源码后可以发现底层也是调用的libjpeg库来实现。

ios,mac

本人还没有在ios/mac中发现如何预读jpeg的官方API。Apple对图形、图像、多媒体领域提供了丰富接口,如果这个功能真没实现就太令我惊讶了! 不过ObjectC完全兼容C,可以调用libjpeg库来实现这个功能。

.NET下仅读取jpeg的大小

下面是用c#仅仅读取jpeg宽高(没有解析像素数据), 直接用C#读取1/2,1/4,1/8还不知道如何实现

FileStream stream = new FileStream(path, FileMode.Open);
Image img = Image.FromStream(stream, false, false);  //关键是将第三个参数设置为false
Console.WriteLine("size: {0},{1}", img.Width, img.Height);

jpeg批量转化工具

用相机拍出来的原始jpeg图片是高保真质量, 所占文件体积非常大,本人写了一个批量转化的工具,可以将jpeg的质量都转化成80, 图像的宽高不变, 这时人眼几乎看不出有什么差别, 但其体积只有原来的1/3. 如果有大量的照片需要保存时, 节约的空间就很客观了。实现原理很简单, 就是读取jpeg文件, 然后再保存.
用c#实现的,代码量非常少,在此贴出全部源码

  1     class Program2     {3         static string src_path;4         static long small_size = 0;5 6         private static ImageCodecInfo GetCodecInfo(string mimeType)7         {8             ImageCodecInfo[] CodecInfo = ImageCodecInfo.GetImageEncoders();9 10             foreach (ImageCodecInfo ici in CodecInfo) {11                 if (ici.MimeType == mimeType)12                     return ici;13             }14             return null;15         }16 17         static void SaveImage(string path)18         {19             FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);20 21             if (stream.Length == 0) {22                 stream.Close();23                 return;24             }25 26             byte[] file_data = new byte[stream.Length];27             stream.Read(file_data, 0, (int)stream.Length);28             Stream mem = new MemoryStream(file_data);29 30             long old_size = stream.Length;31 32             try {33                 Image img = new Bitmap(mem);34                 stream.Close();35 36                 EncoderParameter p = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 80L);37                 EncoderParameters ps = new EncoderParameters(1);38 39                 ps.Param[0] = p;40 41                 img.Save(path, GetCodecInfo("image/jpeg"), ps);42 43                 FileStream f = new FileStream(path, FileMode.Open, FileAccess.Read);44                 long new_size = f.Length;45                 f.Close();46 47                 small_size += old_size - new_size;48 49             } catch (System.Exception ex) {50 51             } finally {52                 stream.Close();53             }54       55         }56 57         static void ConvertOneImage(string path, bool is_save)58         {59             if (is_save) {60                 string new_name = src_path + path.Substring(path.LastIndexOf('\\'));61                 File.Copy(path, new_name, true);62             }63 64             SaveImage(path);            65         }66 67         static void ShowSize(string path)68         {69             FileStream stream = new FileStream(path, FileMode.Open);70             Image img = Image.FromStream(stream, false, false);71             stream.Close();72             //Console.WriteLine("{0} {1}", img.Width, img.Height);73             if (img.Width == 0) {74                 Console.WriteLine("Error");75             }76         }77 78         static void BatchJpeg()79         {80             string path = Application.ExecutablePath;81             path = path.Substring(0, path.LastIndexOf('\\'));82 83             src_path = path + "\\" + "src";84             //Console.WriteLine(src_path);85 86             Console.WriteLine("批量转化jpeg图片,保证其图片质量的前提下减少其存储大小");87             Console.WriteLine("若想保存原图片,其按y(原图将放在src文件夹下), 否则按任意键开始处理");88 89             ConsoleKeyInfo key = Console.ReadKey();90             bool is_save = false;91 92             if (key.KeyChar == 'y' || key.KeyChar == 'Y') {93                 is_save = true;94 95                 Directory.CreateDirectory("src");96             }97 98             string[] files = Directory.GetFiles(path, "*.jpg");99
100             Stopwatch sw = new Stopwatch();
101             sw.Start();
102
103             for (int i = 0; i < files.Length; ++i) {
104                 ConvertOneImage(files[i], is_save);
105                 //string s = files[i].Substring(files[i].LastIndexOf('\\') + 1);
106                 //Console.WriteLine((i + 1).ToString() + "/" + files.Length.ToString() + " \t " + s);
107
108                 //ShowSize(files[i]);
109             }
110
111             sw.Stop();
112
113             Console.WriteLine("*********已结束,按任意键结束********");
114             double v = (small_size * 1.0 / (1024 * 1024));
115
116             Console.WriteLine("共减少 " + v.ToString("0.00##") + "M 的存储空间");
117             Console.WriteLine("耗时:" + sw.Elapsed.TotalSeconds.ToString("0.00##") + "秒");
118             Console.ReadKey();
119         }
120
121         static void Main(string[] args)
122         {
123             BatchJpeg();
124             //SaveImage("E:\\img - 副本.JPG");
125             //string path = "E:\\cpp_app\\LiteTools\\JpegBatch\\bin\\Release\\img - 副本.JPG";
126             //File.Delete(path);
127         }
128     }

JpegBatchConvert

Exif信息

另外jpeg文件一般有一个附属的exif信息,这个信息中有图像大小,拍摄时间,拍摄的相关参数,照片方向,图像缩略图等信息。

用相机拍出来的jpeg都会有这个信息。如果照片方向不是正立的话,在读取到像素取后,还得按exif所指明的方向将图像旋转下。mspaint程序就没有做这个处理,有些图片用picasa查看和用mspaint查看方向就不一样。当然为了简单起见,上面的LBitmap中也自动忽略了exif信息及其图像拍摄时的方向。

如果不用读取1/2,1/4,1/8的方法,也可以从exif中来读取缩略图,但这个缩略图一般很小。

说到exif,不得不说一款用perl实现的命令行工具:exiftool。几乎所有的多媒体文件(图像、音乐、视频)都可以用这个工具来查看其有关信息,当然如果不是jpeg文件就是指广义上的"exif"。在git中有已经编译好可执行文件exiftool.exe。使用方法是将这个文件放到系统路径下,然后在想查看的文件路径下执行 exiftool filename

在实现BatchJpeg工具时如果仅仅用上面实现的LBitmap来读取,保存, 将会失去exif信息, 而相片的拍摄时间等信息又很重要, 所以还得用另一个库exiv2来读取写入exif。如果用c#, 用上面的代码exif信息会自动保留下来。默默地向c#致敬。

intelJpeg库

如果在win32环境下对jpeg IO速度有很高的要求,可以使用interlJpeg库,不开源,但提供有*.h,*.lib文件。这个库可以大大提高jpg读取、保存速度。

当时分别用c#和c实现了jpeg批量转化工具, 在处理大量图片时发现c#用时居然只有c的一半。太奇怪了,按理说,c的速度比c#应该快才对啊, 而实事是c慢了这么多。 最后发现问题就在libjpeg上,用了intetJpeg后速度就和c#差不多了(猜想.NET内部也是用intelJpeg来处理jpeg)。

PNG格式

  1. png是一种无损压缩格式, 压缩大概是用行程编码算法。
  2. png可以有透明效果。
  3. png比较适合适量图,几何图。 比如本文中出现的这些图都是用png保存,比用joeg保存体积要小。

再强调一下: jpeg比较适合存储色彩“杂乱”的拍摄图片,png比较适合存储几何特征强的图形类图片。

png可能有24位图和32位图之分。32位图就是带有alpha通道的图片。
将图片a绘制到另一幅图片b上,如果图片a没有alpha通道,那么就会完全将b图片的像素给替换掉。而如果有alpha通道,那么最后覆盖的结果值将是c = a*alpha + b*(1-alpha)
再对LBitmap添加png的支持。
添加接口如下:

static bool ReadPngSize(const wchar_t *path, int *width, int *height);
static bool IsPngFile(const wchar_t *filename);
bool ReadPng(const wchar_t *filename);
bool WritePng(const wchar_t *filename);

具体实现在PngDemo中。有调用libpng库,并且libpng库依赖zlib库(由此可以看出png算法有用到常规的压缩算法)。

GIF格式

上面提到的bmp,jpeg,png图片都只有一帧,而gif可以保存多帧图像,如图所示

libgif库可以用来读取gif图片。gif中有个参数可以控制图片变化的快慢。在程序中可以使用这个参数,也可以自己定义一个参数,这就是为什么gif图片,在不同程序中查看时其变化速度不一样。

webp

google开发的一种有损、透明图片格式,相当于jpeg和png的合体,google声称其可以把图片大小减少40%。

一个强大的格式库,CxImage

CxImage几乎可以读取任何图片格式

下面是其头文件中的宏定义:

#define CXIMAGE_SUPPORT_WINDOWS 1
#define CXIMAGE_SUPPORT_EXIF    1
#define CXIMAGE_SUPPORT_BMP 1
#define CXIMAGE_SUPPORT_GIF 1
#define CXIMAGE_SUPPORT_JPG 1
#define CXIMAGE_SUPPORT_PNG 1
#define CXIMAGE_SUPPORT_ICO 1
#define CXIMAGE_SUPPORT_TIF 1
#define CXIMAGE_SUPPORT_TGA 1
#define CXIMAGE_SUPPORT_PCX 1
#define CXIMAGE_SUPPORT_WBMP 1
#define CXIMAGE_SUPPORT_WMF 1
#define CXIMAGE_SUPPORT_JP2 1
#define CXIMAGE_SUPPORT_JPC 1
#define CXIMAGE_SUPPORT_PGX 1
#define CXIMAGE_SUPPORT_PNM 1
#define CXIMAGE_SUPPORT_RAS 1#define CXIMAGE_SUPPORT_MNG 1
#define CXIMAGE_SUPPORT_SKA 1
#define CXIMAGE_SUPPORT_RAW 1
#define CXIMAGE_SUPPORT_PSD 1

CxImage在针对特定格式时,也是调用了其它图片库(比如libjpeg, libpng, libtiff)。由于CxImage太过庞大,如果不想使用其全部代码,可以自己从中“偷取”特定图片格式的读取、保存代码。

原文出处 http://www.cnblogs.com/xiangism

各种图片编码格式详解(bmp,jpg,png)相关推荐

  1. 各种图片编码格式详解

    感谢xiangism 常见的图片格式有bmp, jpg(jpeg), png, gif, webp等. 图像基本数据结构 要讲图片格式还先得从图像的基本数据结构说起.在计算机中, 图像是由一个个像素点 ...

  2. python爬取图片-Python爬取网页中的图片(搜狗图片)详解

    前言 最近几天,研究了一下一直很好奇的爬虫算法.这里写一下最近几天的点点心得.下面进入正文: 你可能需要的工作环境: Python 3.6官网下载 本地下载 我们这里以sogou作为爬取的对象. 首先 ...

  3. imagepreview使用案例_微信小程序wx.previewImage预览图片实例详解

    一.小知识 二.例子 1.wxml 2.wxss .container { box-sizing:border-box; padding:20px; } .previewimg{ float:left ...

  4. java字符编码详解_Java中字符编码格式详解

    一.前言 在分析Comparable和Comparator的时候,分析到了String类的compareTo方法,String底层是用char[]数组来存放元素,在比较的时候是比较的两个字符串的字符, ...

  5. 会说话的狗狗本电脑版_一看就会用!Fotor 电脑版 图片后期处理详解系列教程 第一节...

    Fotor 电脑版 图片后期处理详解系列教程 第一节 这节课我们先来了解一下 Fotor 电脑版的界面功能,为了能用 Fotor 电脑版修图处理打基础. 如图示,启动 Fotor 以后出面界面,有编辑 ...

  6. android图片展示图片,Android图片展示详解(一)

    Android图片显示详解(一) 说明: 本讲义分为三部分,较为详细的介绍了Android平台下图片显示,加载等操作的处理原则与办法,以供大家共同学习,转载请注明出处 "From 移动微技& ...

  7. android怎么把动图变成视频格式,Android 录制手机屏幕视频生成GIF图片实例详解

    Android 录制手机屏幕视频生成GIF图片实例详解 无图无真相,在我们日常的网络交流中往往需要给交流对象提供直观的显示,而视频是一个很好的方式,但是视频需要播放器,还需要当做文件进行对点传输,并不 ...

  8. ffmpeg-jpeg图片格式详解

    jpg/jpeg是24位的图像文件格式,也是一种高效率的压缩格式 JPEG格式可以分为 1.标准JPEG:只有图片完全被加载和读取完毕之后,才能看到图片的全貌 2.渐进式JPEG:(标准的改进)首先呈 ...

  9. 【图像处理】jpeg图片格式详解

    jpeg图片格式详解 1. JPEG文件简介 JPEG的全称是JointPhotographicExpertsGroup(联合图像专家小组),它是一种常用的图像存储格式, jpg/jpeg是24位的图 ...

最新文章

  1. Android 页面进行镜像反转-面试
  2. linux mysql清除缓存_案例:通过shell脚本实现mysql数据备份与清理
  3. 《数据库SQL实战》获取所有非manager的员工emp_no
  4. Java生鲜电商平台-深入订单拆单架构与实战
  5. ZooKeeper編程02--多線程的分佈式鎖
  6. ASP如何限定中英文混合的文字输出字数?
  7. typedef的四个用途和两个陷阱
  8. idw matlab 程序_IDW 算法MATLAB 实现 -
  9. POJ 1001 Exponentiation
  10. IT兄弟连 JavaWeb教程 Servlet线程安全问题
  11. += 对于可变对象和不可变对象的区别
  12. java简单实现一个缓存_java简单实现缓存的三种方法
  13. urule客户端和服务器配置
  14. 跪了,腾讯数据工程师用Python可视化绘制的中国GDP数据地图,大佬就是不一样
  15. 解决tuple index out of range
  16. 让物体沿椭圆轨道运动
  17. 淘宝买零件组装一台手机,可以正常使用,想自己组装手机的可以看下
  18. 计算机查询网络连接,本地网络查询方法大全
  19. 中国15家外卖O2O大盘点-2014
  20. PH4-Java核心技术·卷1

热门文章

  1. 微信网页授权登录且实现access_token分布式管理
  2. 前端项目2048小游戏
  3. lgo怎么打开o文件_GO语言文件的创建与打开实例分析
  4. 程序员的算法趣题:Q29 合成电阻的黄金分割比(Java版)
  5. MySQL:开窗函数
  6. Linux 之父亮相,OpenCloudOS 社区开放日来了
  7. Visual Studio 2022大面积错误无法打开源文件
  8. android studio 远程调试,Unity3D学习笔记——Android远程真机调试(Unity Remote)
  9. 微信小程序:爱情保证书制作生成
  10. spyder pyecharts不显示_显示器需要定时校色吗?