import numpy as np
import matplotlib.pyplot as plt
import cv2
img = cv2.imread('cat.jpg')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
h, w = img.shape[0], img.shape[1]
fig1 = plt.figure('原始图像')
plt.imshow(img)
plt.axis('off')
plt.title('Original image')
print( '\t\t\t 原始图像形状:\n', '\t\t\t',img.shape )
这里有三个要点:图像的存储问题,图像的裁剪方法,长宽不能整除的问题。下面逐一介绍
由于事先不知道m和n的具体值,因此也就不知道图像的分块数目具体是多少个。必须想个办法来存储这 m × n m \times n m×n个图像分块,创建 m × n m \times n m×n个变量来存储也是不可取的,因为这样会让程序变得很复杂。
本人想到一个很简单的方法:增加维度的方法。创建一个五维的张量来存放各个划分后的子图像块,其中前面两维表示该图像块在原图像的位置(如第i行第j列的分块),后面三个维度表示每个子图像块的具体内容(R、G、B的值)。
直接利用numpy的切片功能将每个网格的左上角和右下角的坐标值所确定的图像块的像素直接写入五维张量即可。这里只需要注意网格顶点坐标的确认,在处理的时候横坐标和纵坐标对应的维度不要搞反。
上一步还存在一个很大的bug:如果图像的长宽不能分别被m和n整除就会带来网格点非整数的问题,还会带来部分图像分块的大小与其他图像分块不一致的问题。举个例子,比如我的图像尺寸是 3 × 3 3 \times 3 3×3,现在要把这个图像分成 1 × 2 1 \times 2 1×2个图像块,那么第2列像素就要一分为二才能做到真正的均匀划分,这显然是不可能的。
我们做个简单的处理,直接对网格顶点的坐标进行四舍五入取整,这样一来,第2个像素就被划分到左边的分块了,但是这样会带来一个新的问题,即每个图像块的大小不一致。还沿用前面的例子,尺寸为 3 × 3 3 \times 3 3×3会被划分为 3 × 2 3 \times 2 3×2和 3 × 1 3 \times 1 3×1两个图像块,这会导致图像分块的不均匀,也会导致前面的五维张量不合理。一般来说,我们要分块的图像都是很大的,而划分的块数一般不会特别多,所以舍入误差带来的几个像素的差异完全可以忽略,针对这种情况,一个比较好的思路就是在五维张量的填充中只对已知位置的像素进行填充,而把其他位置的像素值仍然设置为0。
完整程序为:
def divide_method1(img,m,n):#分割成m行n列
h, w = img.shape[0],img.shape[1]
gx=np.round(gx).astype(np.int)
gy=np.round(gy).astype(np.int)
divide_image = np.zeros([m-1, n-1, int(h*1.0/(m-1)+0.5), int(w*1.0/(n-1)+0.5),3], np.uint8)#这是一个五维的张量,前面两维表示分块后图像的位置(第m行,第n列),后面三维表示每个分块后的图像信息
for i in range(m-1):
for j in range(n-1):
divide_image[i,j,0:gy[i+1][j+1]-gy[i][j], 0:gx[i+1][j+1]-gx[i][j],:]= img[
gy[i][j]:gy[i+1][j+1], gx[i][j]:gx[i+1][j+1],:]#这样写比a[i,j,...]=要麻烦,但是可以避免网格分块的时候,有些图像块的比其他图像块大一点或者小一点的情况引起程序出错
return divide_image
这个方法很简单,将图像缩放一下,让其满足整除关系即可。
def divide_method2(img,m,n):#分割成m行n列
h, w = img.shape[0],img.shape[1]
grid_h=int(h*1.0/(m-1)+0.5)#每个网格的高
grid_w=int(w*1.0/(n-1)+0.5)#每个网格的宽
#满足整除关系时的高、宽
h=grid_h*(m-1)
w=grid_w*(n-1)
#图像缩放
img_re=cv2.resize(img,(w,h),cv2.INTER_LINEAR)# 也可以用img_re=skimage.transform.resize(img, (h,w)).astype(np.uint8)
#plt.imshow(img_re)
gx, gy = np.meshgrid(np.linspace(0, w, n),np.linspace(0, h, m))
gx=gx.astype(np.int)
gy=gy.astype(np.int)
divide_image = np.zeros([m-1, n-1, grid_h, grid_w,3], np.uint8)#这是一个五维的张量,前面两维表示分块后图像的位置(第m行,第n列),后面三维表示每个分块后的图像信息
for i in range(m-1):
for j in range(n-1):
divide_image[i,j,...]=img_re[
gy[i][j]:gy[i+1][j+1], gx[i][j]:gx[i+1][j+1],:]
return divide_image
采用numpy的切片方法可以简单进行处理:
def create_image_block(self, image,row_number,col_number):
block_row = np.array_split(image, row_number, axis = 0)#垂直方向切割,得到很多横向长条
print(image.shape)
img_blocks = []
for block in block_row:
block_col = np.array_split(block, col_number, axis = 1)#水平方向切割,得到很多图像块
img_blocks += [block_col]
#print(img_blocks[-1][-1].shape)
cv2.imshow("block image",img_blocks[2][1])#第3行第2列图像块
def display_blocks(divide_image):#
m,n=divide_image.shape[0],divide_image.shape[1]
for i in range(m):
for j in range(n):
plt.subplot(m,n,i*n+j+1)
plt.imshow(divide_image[i,j,:])
plt.axis('off')
plt.title('block:'+str(i*n+j+1))
方法一:四舍五入法
m=3
n=4
divide_image1=divide_method1(img,m+1,n+1)#该函数中m+1和n+1表示网格点个数,m和n分别表示分块的块数
fig2 = plt.figure('分块后的子图像:四舍五入法')
display_blocks(divide_image1)
divide_image2=divide_method2(img,m+1,n+1)#该函数中m+1和n+1表示网格点个数,m和n分别表示分块的块数
fig3 = plt.figure('分块后的子图像:图像缩放法')
display_blocks(divide_image2)
主要目的是将分块后的图像拼接起来,还原成一幅完整的大图像。有两个方法:
1.使用opencv。其实opencv自带的有图像拼接的函数,hconcat函数:用于两个Mat矩阵或者图像的水平拼接;vconcat函数:用于两个Mat矩阵或者图像的垂直拼接。
2.自己动手写。图像拼接是图像分块的逆过程,首先创建一个空的还原后的图像,然后将对于位置填充上对应的像素即可。
由于事先不知道具体分成多少块,使用opencv拼接图像是很麻烦的。为了简单,我们还是选择第2种方法,自己动手丰衣足食!
def image_concat(divide_image):
m,n,grid_h, grid_w=[divide_image.shape[0],divide_image.shape[1],#每行,每列的图像块数
divide_image.shape[2],divide_image.shape[3]]#每个图像块的尺寸
restore_image = np.zeros([m*grid_h, n*grid_w, 3], np.uint8)
restore_image[0:grid_h,0:]
for i in range(m):
for j in range(n):
restore_image[i*grid_h:(i+1)*grid_h,j*grid_w:(j+1)*grid_w]=divide_image[i,j,:]
return restore_image
下面分别显示‘四舍五入法’和‘图像缩放法’得到的分块图像的还原结果。
fig4 = plt.figure('分块图像的还原')
restore_image1=image_concat(divide_image1)#四舍五入法分块还原
restore_image2=image_concat(divide_image2)#图像缩放法分块还原
plt.subplot(1,2,1)
plt.imshow(restore_image1)
plt.axis('off')
plt.title('Rounding')
plt.subplot(1,2,2)
plt.imshow(restore_image2)
plt.axis('off')
plt.title('Scaling')
print('\t\t\t还原后的图像尺寸')
print('\t‘四舍五入法’:', restore_image1.shape,'\t''‘图像缩放法’:', restore_image2.shape)
plt.show()
[1]图像的水平方向或者垂直方向拼接https://blog.csdn.net/mangobar/article/details/79656417