Skip to content

Commit

Permalink
[Doc]: add document about how python read images
Browse files Browse the repository at this point in the history
  • Loading branch information
HarleysZhang committed Dec 14, 2022
1 parent 31c8b02 commit 90de815
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 19 deletions.
189 changes: 189 additions & 0 deletions 2-programming_language/python3/Python 读取图像方式总结.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
- [读取并显示图像](#读取并显示图像)
- [opencv3库](#opencv3库)
- [scikit-image库](#scikit-image库)
- [PIL库](#pil库)
- [读取图像结果分析](#读取图像结果分析)
- [打印图像信息](#打印图像信息)
- [skimage获取图像信息](#skimage获取图像信息)
- [PIL获取图像信息](#pil获取图像信息)
- [读取并显示图像方法总结](#读取并显示图像方法总结)
- [PIL库读取图像](#pil库读取图像)
- [Opencv3读取图像](#opencv3读取图像)
- [scikit-image库读取图像](#scikit-image库读取图像)
- [参考资料](#参考资料)

学习数字图像处理,第一步就是读取图像。这里我总结下如何使用 opencv3,scikit-image, PIL 图像处理库读取图片并显示。

## 读取并显示图像
### opencv3库
opencv 读取图像,返回的是矩阵数据,RGB 图像的 shape 是 (height, weight, channel),dtype 是 uint8。

示例代码如下:

```python
import cv2
# 读入一副彩色图像
img_cv2 = cv2.imread('test.jpg',cv2.IMREAD_COLOR)
# 打印图像尺寸,形状,图像元素数据类型
print(type(img_cv2))
print(img_cv2.shape) # (height, width, channel)
print(img_cv2.dtype) # uint8
# matplotlib绘制显示图像
plt.figure(1)
plt.imshow(img_PIL)
plt.show()
# cv2绘制显示图像
# cv2.imshow()
# cv2.namedWindow('image', cv2.WINDOW_NORMAL)
# cv2.imshow('image',img_cv2)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
```
### scikit-image库
示例代码如下:

```python
from skimage import io
img_skimage = io.imread('test.jpg')
# 打印图像尺寸
print(img_skimage.shape) #(height, width, channel)
# 绘制显示图像
io.imshow(img_skimage)
# import matplotlib.pyplot as plt
# plt.imshow(img_skimage)
```
注意:io.imshow(img\_skimage),这一行代码的实质是利用matplotlib包对图片进行绘制,绘制成功后,返回一个matplotlib类型的数据。也就是说scikit-image库对图像的绘制实际上是调用了matplotlib库imshow显示函数。

cv2和skimage读取图像,图像的尺寸可以通过其shape属性来获取,shape返回的是一个tuple元组,第一个元素表示图像的高度,第二个表示图像的宽度,第三个表示像素的通道数。

### PIL库
示例代码如下:

```python
# PIL库读取绘制显示图像
# plt 用于显示图片
from PIL import Image
import matplotlib.pyplot as plt

import numpy as np
img_PIL = Image.open('test.jpg')
img_PIL = np.array(img_PIL)
# 打印图像类型,尺寸和总像素个数
print(type(img_PIL)) # <class 'numpy.ndarray'>
print(img_PIL.shape) # (height, width, channel), (1200, 1793, 3)
print(img_PIL.size) # 6454800 = 1200*1793*3
# 绘制显示图像
plt.figure(1)
plt.imshow(img_PIL)
plt.show()
```
### 读取图像结果分析
分别用Opnecv3和sckit-image读取图像,并用matplotlib库显示。示例代码如下:

```python
import cv2
from skimage import io
import matplotlib.pyplot as plt
img_cv2 = cv2.imread('test.jpg',cv2.IMREAD_COLOR)
img_skimage = io.imread('test.jpg')
# matplotlib显示cv2库读取的图像
plt.figure('imread picture',figsize=(25,25))
plt.subplot(121)
plt.title('cv2 imread picture')
plt.imshow(img_cv2)
# matplotlib显示skimage库读取的图像
plt.subplot(122)
plt.title('skimage imread picture')
plt.imshow(img_skimage)
# 打印图像尺寸,总像素个数,和图像元素数据类型
print(img_cv2.shape)
print(img_cv2.size)
print(img_cv2.dtype)
```
![image](../../data/images/python_read_images/zk5YgqAS7448RIT1Bmq40vrCPHLp3pKrCS-_ilrL6Yw.png)

通过以上输出结果对比图,我们会发现,matplotlib绘制显示的cv2库读取的图像与原图有所差别,这是因为opencv3库读取图像的通道时BGR,而正常图像读取的通道都是RGB,matplotlib库显示图像也是按照RGB顺序通道来的,解释完毕。

一点疑惑,我通过查询库函数可知plt.show()第一个参数为要显示的对象(array\_like),字面意思理解为类似数组的对象,但是很明显,PIL库返回的不是’numpy.ndarray’对象,而是’PIL.JpegImagePlugin.JpegImageFile’对象,那为什么plt.show()函数还是能显示Image.open()函数读取图像返回的结果呢?

程序如下图所示:

![image](../../data/images/python_read_images/WYCTaeuwXi9ICkRMdEn3K0jvGsddfmr415rW0Wl_UCk.png)

## 打印图像信息
图像常用信息有图像尺寸,像素个数,通道数等。

### skimage获取图像信息
**注意:scikit-image 库读取和缩放图像速度要慢 opencv 库 近 4 倍。**

```Plain Text
from skimage import io, data
# create coffee image, return (300, 451, 3) uint8 ndarray
img = data.coffee()
io.imshow(img) # 显示图片
print(type(img)) # 显示类型
print(img.dtype) # 显示图像元素数据类型
print(img.shape) # 显示尺寸
print(img.shape[0]) # 图片高度
print(img.shape[1]) # 图片宽度
print(img.shape[2]) # 图片通道数
print(img.size) # 显示总像素个数=shape[0]*shape[1]*shape[2]
print(img.max()) # 最大像素值
print(img.min()) # 最小像素值
print(img.mean()) # 像素平均值
print(img[0][0]) # 图像第一行第一列的像素值
```
输出结果如下图:

![image](../../data/images/python_read_images/En5A4Xt4naL49ocumGSh2pHXKBMxqoud1KVL7blZAmw.png)

### PIL获取图像信息
```Plain Text
# 获取PIL image图片信息
im = Image.open('test.jpg')
print (type(im))
print (im.size) #图片的尺寸
print (im.mode) #图片的模式
print (im.format) #图片的格式
print (im.getpixel((0,0)))#得到像素:
# img读出来的图片获得某点像素用getpixel((w,h))可以直接返回这个点三个通道的像素值
```
输出结果如下:

![image](../../data/images/python_read_images/cqyXYva-pIgE7adHMXrskm-r2h6ivTP5CQeoNWPiaH8.png)

plt.show函数定义如下:

> Signature: plt.imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, origin=None, extent=None, shape=None, filternorm=1, filterrad=4.0, imlim=None, resample=None, url=None, hold=None, data=None, \*\*kwargs)
Docstring:
Display an image on the axes.

> Parameters
———-
X : array\_like, shape (n, m) or (n, m, 3) or (n, m, 4). Display the image in XX to current axes. XX may be an array or a PIL image. If XX is an array, it can have the following shapes and types:

> – MxN — values to be mapped (float or int)
– MxNx3 — RGB (float or uint8)
– MxNx4 — RGBA (float or uint8)

> The value for each component of MxNx3 and MxNx4 float arrays should be in the range 0.0 to 1.0. MxN arrays are mapped to colors based on the ∥∥∥∥ (mapping scalar to scalar) and the cmapcmap (mapping the normed scalar to a color).
## 读取并显示图像方法总结
### PIL库读取图像
> PIL.Image.open + numpy
scipy.misc.imread
scipy.ndimage.imread
这些方法都是通过调用PIL.Image.open 读取图像的信息;
**PIL.Image.open 不直接返回numpy对象**,可以用numpy提供的函数进行转换,参考Image和Ndarray互相转换;
scipy.ndimage.imread直接返回numpy.ndarray对象,通道顺序为RGB,通道值得默认范围为0-255。

### Opencv3读取图像
> cv2.imread: 使用opencv读取图像,直接返回numpy.ndarray 对象,通道顺序为BGR ,注意是**BGR**,通道值默认范围0-255。
### scikit-image库读取图像
> skimage.io.imread: 直接返回numpy.ndarray 对象,通道顺序为RGB,通道值默认范围0-255。
### 参考资料
* [https://blog.csdn.net/renelian1572/article/details/78761278](https://blog.csdn.net/renelian1572/article/details/78761278)
* [https://pillow.readthedocs.io/en/5.3.x/index.html](https://pillow.readthedocs.io/en/5.3.x/index.html)
* [http://scikit-image.org/docs/stable/user\_guide.html](http://scikit-image.org/docs/stable/user_guide.html)
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import pandas as pd
obj = pd.Series([1,4,7,8,9])
obj
```
![image](../../data/images/pandas/mehHOUu698p5GFHptdAl1TS1PZuqzwvUpJKGLHfuqEY.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173636576-1096205196.png)

Series 的字符串表现形式为:索引在左边,值在右边。由于我们没有为数据指定索引,于是会自动创建一个 0 到 N-1( N 为数据的长度)的整数型索引。也可以通过Series 的 values 和 index 属性获取其数组表示形式和索引对象,代码示例:

Expand Down Expand Up @@ -87,7 +87,7 @@ dtype:  int64

> Index(\[‘a’, ‘b’, ‘c’, ‘d’, ‘e’\], dtype=’object’)
![image](../../data/images/pandas/zjIofANE2EH8FbL8t5R2ivTG3ieIx3DrA1ufwfTJKG4.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173636983-250507382.png)

与普通 NumPy 数组相比,你可以通过索引的方式选取 Series 中的单个或一组值,代码示例:

Expand All @@ -96,7 +96,7 @@ obj2[['a', 'b', 'c']]
obj2['a']=2
obj2[['a', 'b', 'c']]
```
![image](../../data/images/pandas/pnppCnVHQV3YWboi44iaDHOgOzdAUpnnYFV8EwAnodU.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173637255-1678987627.png)

\[‘a’,’b’,’c\]是索引列表,即使它包含的是字符串而不是整数。

Expand All @@ -106,7 +106,7 @@ obj2[['a', 'b', 'c']]
obj2*2
np.exp(obj2)
```
![image](../../data/images/pandas/dHcDtD_Y6NZGCUIb_zVvp_5lJLBZaTzZTm823dlaqmg.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173637559-387232800.png)

还可以将 Series 看成是一个定长的有序字典,因为它是索引值到数据值的一个映射。它可以用在许多原本需要字典参数的函数中,代码示例:

Expand All @@ -127,7 +127,7 @@ obj3
> dtype: int64
![image](../../data/images/pandas/4GjLOFJv3__gjLkru7fU5DlPrU68R6mmI0Y6daaFPuc.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173637848-475545746.png)

### DataFrame数据结构
DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共用同一个索引)。DataFrame 中的数据是以一个或多个二维块存放的(而不是列表、字典或别的一维数据结构)。
Expand All @@ -145,7 +145,7 @@ frame
```
结果 DataFrame 会自动加上索引(跟 Series 一样),且全部列会被有序排列,输出如下:

![image](../../data/images/pandas/yw-a9Kc_vSLq0X6TLKUBbT9dve1ihpi437Izvu_L8oM.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173638152-581042833.png)

对于特别大的 DataFrame,head 方法会选取前五行:

Expand All @@ -157,7 +157,7 @@ frame.head()
```Plain Text
pd.DataFrame(data,columns=['state','year','pop'])
```
![image](../../data/images/pandas/6suW8Y3Om3nd9x6b0-GdWDCssQVsltgFion2NLZv-vM.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173638433-1095385810.png)

如果传入的列在数据中找不到,就会在结果中产生缺失值,代码示例:

Expand All @@ -166,7 +166,7 @@ frame2 = pd.DataFrame(data,columns=['state','year','pop','debt'],
                                    index=['one','two','three','four','five','six'])
frame2
```
![image](../../data/images/pandas/GUFyyIupGf-1EizI7KuJBtuogx9O83eWMPZGMHWgNV0.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173638742-1099455853.png)

获取 DataFrame 的 columns 和 index,代码示例:

Expand All @@ -180,23 +180,23 @@ frame2.index
> Index(\[‘one’, ‘two’, ‘three’, ‘four’, ‘five’, ‘six’\], dtype=’object’)
![image](../../data/images/pandas/vybZ_9BZL19pm3PW54JMxn_OtrnSB_ibDYHVYiH3l98.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173639028-477563344.png)

通过类似字典标记的方式或属性的方式,可以将 DataFrame 的列获取为一个 Series,代码示例:

```Plain Text
frame2['state']
frame2.state
```
![image](../../data/images/pandas/LVh77V4fUh10o4mU5AloGJg_spSi3H1cHvrGhxg3whY.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173639329-1980211561.png)

列可以通过赋值的方式进行修改,赋值方式类似 Series。例如,我们可以给那个空的 “debt” 列赋上一个标量值或一组值(数组或列表形式),代码示例:

```Plain Text
frame2.debt = np.arange(6.)
frame2
```
![image](../../data/images/pandas/b5g2YOTWsxoPnIZxyeSOkoV3P_8nxJpXSsf2ZuLO7co.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173639623-669995028.png)

注意:将列表或数组赋值给某个列时,其长度必须跟DataFrame的长度相匹配。

Expand All @@ -215,7 +215,7 @@ frame2
frame2['eastern'] = frame2.state=='Ohio'
frame2
```
![image](../../data/images/pandas/bVlf3zi_oCdjSzgdHHfeya1rYQUMWgLPWmYUsGMXi30.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173639941-1152724147.png)

DataFrame 另一种常见的数据形式是嵌套字典,如果嵌套字典传给 DataFrame,pandas 就会被解释为:外层字典的键作为列,内层键则作为行索引,代码示例:

Expand All @@ -228,11 +228,11 @@ pop = {
frame3 = pd.DataFrame(pop,columns=['Nvidia','Intel'])
frame3
```
![image](../../data/images/pandas/5NqSbp4XB_LEPBOnbqRv27Ou3Of3RNL1-6zib6v9l7I.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173640184-113328660.png)

**表5-1列出了DataFrame构造函数所能接受的各种数据**

![image](../../data/images/pandas/_7XUyaBIMZVAO41Ih8BY10W21yMODIQz9SNWmFekhWQ.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173640533-32023963.png)

### 索引对象
pandas 的索引对象负责管理轴标签和其他元数据(比如轴名称等)。构建 Series 或 DataFrame 时,所用到的任何数组或其他序列的标签都会被转换成一个 Index,代码示例:
Expand All @@ -245,7 +245,7 @@ index = obj.index
#index
index[:-1]
```
![image](../../data/images/pandas/eN7Nbjg4bMr0CpiB0C44WN_TidMdM_1_kemHxwk3XIM.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173640827-1806772635.png)

注意:Index 对象是不可变的,因此用户不能对其进行修改。

Expand All @@ -259,7 +259,7 @@ obj2 = pd.Series([1.5, -2.5, 0], index=labels)
obj2
#print(obj2.index is labels)
```
![image](../../data/images/pandas/HdxGMf8q3VQvdnWZelz2xtTBOQYju7PxKFsJO-Azm74.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173641079-2052115414.png)

> 注意:虽然用户不需要经常使用 Index 的功能,但是因为一些操作会生成包含被索引化的数据,理解它们的工作原理是很重要的。
Expand All @@ -271,7 +271,7 @@ dup_labels
```
每个索引都有一些方法和属性,它们可用于设置逻辑并回答有关该索引所包含的数据的常见问题。表5-2列出了这些函数。

![image](../../data/images/pandas/NYmjzWqZnomg7PIbJQx3KYgg3--Xb0MpCpjBKSyK4Mc.png)
![image](https://img2023.cnblogs.com/blog/2989634/202212/2989634-20221214173641454-1217752448.png)

## pandas 选择数据
```Plain Text
Expand Down Expand Up @@ -379,5 +379,4 @@ print(df[df.A>8])
本文主要记录了 Series 和 DataFrame 作为 pandas 库的基本结构的一些特性,如何创建 pandas 对象、指定 columns 和 index 创建 Series 和 DataFrame 对象、赋值操作、属性获取、索引对象等,这章介绍操作 Series 和 DataFrame 中的数据的基本手段。

## 参考资料
* 《利用python进行数据分析》

* 《利用python进行数据分析》
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 90de815

Please sign in to comment.