图像概念

 
- 像素
像素是指由图像的小方格组成的,这些小方块都有一个明确的位置和被分配的色彩数值,小方格颜色和位置就决定该图像所呈现出来的样子。
- 分辨率
图像分辨率指图像中存储的信息量,是每英寸图像内有多少个像素点,分辨率的单位为PPI(Pixels Per Inch),通常叫做像素每英寸。图像分辨率一般被用于ps中,用来改变图像的清晰度。
- 图片的尺寸x图片的分辨率=图片的像素
    - 这里的尺寸指的就是照相馆中说的几寸照片
    - 而AI图像高与宽则是存储图像的矩阵有几行几列
### 通俗理解
- 图像有形状(尺寸)和颜色
- 颜色是指RGB(红,绿,蓝),red,green,blue三色
-
- 每个颜色的取值范围为[0,255]
- AI的图像处理通常都是黑白色的
- 也就是说,AI更关注图像的形状,而不是色彩
-
- 图像由像素组成
- 一个像素就是一个值为[0,255]的一个数
    

 

    

 
这里指计算机中的图像,从视觉上看,它与现实相近,
比如电脑中看到大山,马路,楼房,车子等,与现实中看到的相近,
但我们知道,这些事物并没有真的进入电脑,
这时要从两个角度理解:
1. 由于电脑中全是数字/字节/二进制01,所图像在电脑的存储方式只能是数字,我们处理图像就是要去处理图像对应的数字
2. 看不见的数字是如何转换成视觉成像的问题,是显示器或者相机处理的,这个不是我们要关注的问题

处理图像就是要处理与图像对应的 数字矩阵

 

    

 
## 数据
1
是一个数,也叫标量,或者张量

[1]
是一个列表,确切讲是1维列表,也可以叫做向量
向量就是n个数值形成的列表

[[1],[2]]
2维列表,它的每1维都是一个1维列表,或者说每1维都是一个向量
也可以说是矩阵

## 数据shape
数据shape,指数据的形状,或者数据的维度
单个数字没有维度
```python
print("shape----------------------")
import numpy as np
from numpy.core.fromnumeric import shape
print(1,shape(3))             # (),没有
print(2,shape([3]))           # (1,),表示第一维的个数为1,第二维没有
print(3,shape([[1],[2]]))     # (2, 1)  第一维个数为2,第二维个数为1
print(4,shape([[1,1],[2,2]])) # (2, 2) 第一维个数为2,第二维个数为2

```

神经网络模型常用的数据shape,通常 不超过四维

 


 

### reshape
- 将原数组中的所有元素串起来,按新的维度进行组合
```python
import numpy as np
a = np.array(   [
        [
            [[1],[255]],
            [[0],[100]  ],
        ]
    ])
print("1",a)
print("2",a.shape)

b = a.reshape(2,2)
print("3",b)
```
```
1 [[[[  1]
    [255]]
    [[  0]
    [100]]]]

2 (1, 2, 2, 1)
3 [[  1 255]
    [  0 100]]
```

reshape不改变数组元素具体的值,也不改变元素的个数,改变了数组的形状,

数据进入模型后,会经过很多方法的处理,
不同的方法需要的数组的shape不一样,
所以有时需要改变数据的shape

  

新增1个维度-在最高维增加

 
- 元素个数不变的情况下,再加1维(维度数为1)

```python
import numpy as np
w = np.array([1,2,3])

print(w.shape)
"""
(3,)
行向量第2个维度为空
下面增加一个维度,新的维度为(3,1),表示3行1列
"""

w = w.reshape(3,1)

```

新增1个维度-矩阵

 
由于数据的元素不变化,reshape新的维度后,数据会重排列,
可以利用这一点,为数据增加一个维度,

新的维度要能刚好放下原来所有的数据才行,还需要考虑整除的问题,
如果这加上的一维的维度数不是1,比如是3,就直接报错了

- 下面矩阵最后一个维度是一个行向量
```python
import numpy as np
a = np.array(   [
        [
            [1, 2, 55],
            [0, 3, 10],
            [1, 2, 55],

        ]
    ])
print("1",a)
print("2",a.shape)
b = a.reshape(1,3,3,1)
print("3",b)
print("4",b.shape)
```
```
1 [[[ 1  2 55]
    [ 0  3 10]
    [ 1  2 55]]]
2 (1, 3, 3)
3 [[[[ 1]
    [ 2]
    [55]]

    [[ 0]
    [ 3]
    [10]]

    [[ 1]
    [ 2]
    [55]]]]
4 (1, 3, 3, 1)
```
    

 


图像shape与通道

 
### 单个图像shape
- 一张图像的矩阵表示
- 下面是一张图片,185是高,宽是358
- 以jpg格式存储,然后使用numpy读取

```python
from PIL import Image
import numpy as np
im = np.array(Image.open('/tmp/31.jpg'))

print(im.dtype)
# uint8

print(im.shape)
(185, 358, 3)
```
- RGB图像的shape为(高,宽,RGB颜色)
- 第3维,表示的是[红,黄,蓝]三色,
- 也叫:通道
-
- 高宽决定图片的尺寸

- png图片还有一个透明度的维度,它的通道数为4
- 所以通道表示的是高与宽形成的矩阵数量
-
- 每个通道的矩阵表示了相似的图片形状,不同的颜色

下面是png图片,每张图片有4组数据
```python
from PIL import Image
import numpy as np

im = np.array(Image.open('/tmp/1.png'))
print(im.shape)
# (61, 97, 4)
```

### 图像通道
- 下面是一张5行2列3通道的白色图片
- 白色对应的RGB是三个255,图像数据最后一维是一个行向量[255,255,255]
```python
from PIL import Image
import numpy as np

im = np.array(Image.open('/tmp/12.jpg'))
print(im.shape) # (5, 2, 3)
print(im)
"""
[[[255 255 255]
    [255 255 255]]

    [[255 255 255]
    [255 255 255]]

    [[255 255 255]
    [255 255 255]]

    [[255 255 255]
    [255 255 255]]

    [[255 255 255]
    [255 255 255]]]
"""
```
图像入模处理

图像处理之从PIL读取到模型输入

 
from PIL import Image
from torchvision.transforms import Compose
from torchvision.transforms import Grayscale
from torchvision.transforms import Resize
from torchvision.transforms import ToTensor
from torchvision.transforms import Normalize


img = Image.open(fp="/opt/tpf/aiwks/datasets/images/001/dianzi1.jpg")

# 图像数据预处理
transforms1 = Compose(transforms=[Resize(size=(224, 224)), 
                                    ToTensor()])
# Compose的参数要求是JpegImageFile
x = transforms1(img)
print(x.shape)  # torch.Size([3, 224, 224]),注意这里shape已经进行了转换
print(x[0,0][:5])  # tensor([0.0863, 0.0863, 0.0863, 0.0863, 0.0863]),还完成了归一化,将0-255的数值转换到0-1


# Normalize标准化处理,图像有3通道,所以需要3个维度进行归一化
transforms2 = Compose(transforms=
                        [Resize(size=(224, 224)), 
                    ToTensor(),
                    Normalize(mean=[0.5, 0.5, 0.5], 
                    std=[0.5, 0.5, 0.5])])
x = transforms2(img)
print(x.shape)  # torch.Size([3, 224, 224])


# 灰度处理,图像有1通道,所以需要1个维度进行归一化
transforms3 = Compose([
    Grayscale(),
    Resize(size=(32,32)),
    ToTensor(),
    Normalize(mean=[0.5], 
                    std=[0.5])
])

x = transforms3(img)
print("灰度处理:",x.shape)  # 灰度处理: torch.Size([1, 32, 32])

是否灰度处理看业务需求:
如果业务更关注形状,就灰度处理减少计算量 
如果不同的颜色对业务分类有帮助,就不进行灰度处理 

Normalize(mean=[0.5], std=[0.5]) 将数据拉到0与1的中间,并且转为正态分布

图像增强

按比例 随机 批量 生成 以x为中心的值

 
import numpy as np 

def shake(x):
    float_num = [0.1, 0.1,   0.3, 0.5,   0.95, 0.95, 0.99, 0.99, 0.99, 0.99]
    seed = float_num[np.random.randint(0, len(float_num))]
    x = 100
    x = x + np.random.randint(int(-x * seed), int(x * seed))
    return x
for i in range(5):
    x = shake(100)
    print(x)

输出:
107
105
126
85
34

按比例 随机 批量 生成 以图像为中心的其他图像

 
参考上面的思路,进行以下设计:

要生成的图像通常是正方形,所以求图像的高h,宽w 中的最大值 msize = max(h,w) 
以此值为中心,按上面的方法抖动一下,生成一批边

求出图像的中心坐标(cx,cy)按上面的方法抖动一下,得到一系列中心坐标 

新的中心+新的边长 对应 新的正方形 

存在的问题:
比如x=100,原图像最大边长110,但随机生成的值却有126这样的数据,
所以并不是所有生成的图像都要,要除大于某个值的数据

既然图像变大可能出问题,那么可以将原初的坐标框收缩一下,
这样再随机扩大后,可以减少要去除的框的个数 

图像偏移率

 
(原坐标 - 新坐标)/ 新的最大边长 = 偏移率 

所有点的 偏移率之和 就是 损失函数 

损失函数的目标是 让程序框到的图像 离 真实标签框的图像 的偏移率 越来越小

如此,程序框 就逐渐逼近 标签框 了

参考