第九章 文件和数据的操作

文章介绍了如何使用Python的OS库进行文件操作,包括检查文件是否存在、拼接文件路径、创建文件夹以及删除文件。此外,详细阐述了如何创建和划分训练集与测试集,以及如何读取和显示Numpy存储的图像数据。文章还提到了处理中文显示的问题,并提供了将大量图片数据划分为训练集和测试集的代码示例。
摘要由CSDN通过智能技术生成

1.文件操作

1. 介绍
我们已经学会了如何把图片导入到程序中,但是做机器学习的时候,很多数据其实不是图片类型,这时候我们就需要学习如何把任意的文件导入到当前的程序中了。一般python都会自带OS库,用这个库,我们可以完成几乎所有的数据操作。导入OS库的方法和之前一样:

import os

2.os.path接口
os.path接口是很常用的一个接口,它主要与文件路径地址相关,常用于检测文件是否存在以及拼接文件地址,下面我来一一介绍一下它的内置函数的功能。
(1)os.path.exists('文件路径')该函数可以检测该路径下的文件是否存在,如果存在返回True,不存在返回False,还是那之前我家猫的那个图片文件来作为例子:

"""
@FileName:OS_use.py
@Description:os的使用例子
@Author:段鹏浩
@Time:2023/3/13 22:33
"""
import os

# mycat.jpg存在,但是cat.jpg不存在
print(os.path.exists('E:/pictures/mycat.jpg'))
print(os.path.exists('E:/pictures/cat.jpg'))

输出结果为:

True
False

这里建议每次要导入文件前,先查看是否存在,否则会引发系统崩溃等问题
(2)os.path.join(路径名,文件名)连接路径名和文件名。通过该函数,我们先定义好要保存的位置,或者要查询的位置,写个循环,就可以循环的保存文件或者是查看文件,因为现在还没有学怎么保存文件,等后面实战的时候说明。先写个拼接的例子给大家看看(其实用python内置的字符串拼接 路径+文件名也可):

"""
@FileName:OS_use.py
@Description:os的使用例子
@Author:段鹏浩
@Time:2023/3/13 22:33
"""
import os

path = "E:/pictures/"
name = "cat.jpg"

p_and_n = os.path.join(path, name)  # join函数
# 和字符串拼接是等价的
p_an_n = path + name
print(f"join函数生成的:{p_and_n}\n")
print(f"字符串拼接成的:{p_an_n}")

输出为:

join函数生成的:E:/pictures/cat.jpg

字符串拼接成的:E:/pictures/cat.jpg

当然,os.path.join有着更高级的用法:
1 ◯ \textcircled{1} 1若各个路径之间不存在 “ \ ”, 则其会自动为各个路径之间增加连接符 “ \ ”。
例如:

import os
dir = os.path.join('home','pc','data')
print(dir)

输出:

home\pc\data

我们可以看到,在windows系统下,它拼接出的路径是符合window系统的,不是“/”,而是"",这是编译器会警告,但是我们只能选择无视编译器,因为你直接运行windows的路径,不出现\n什么的特殊符号,也不会报错
有没有不警告的呢,还真有,replace转换一下就行,代码如下:

import os

dir = os.path.join('home', 'pc', 'data')

dir = dir.replace('\\', '/')	# 这个代码用/来替换全部的\(虽然是\\,但是\前面的一个\会被编译器无视,否则编译器还是会认为出错)
print(dir)

输出就正常了:

home/pc/data

2 ◯ \textcircled{2} 2存在以“ / ”开始的子路径,则从最后一个以“ / ”开头的子路径开始拼接,之前的子路径全部丢弃。
例如:

import os

dir = os.path.join('home', '/pc', 'data')
dir = dir.replace('\\', '/')
print(dir)
dir = os.path.join('/home', 'pc', '/data')
dir = dir.replace('\\', '/')
print(dir)

输出:

/pc/data
/data

3 ◯ \textcircled{3} 3存在以“. / ”开始的子路径,join会无视里面的"/",如果同时存在以“ / ”开头的子路径,则还是以“ / ”开头的子路径为依据,从最后一个以“ / ”开头的子路径开始拼接,之前的子路径全部丢弃,但是如果在./后面没有“/”,那不影响全部元素的拼接。
例如:

import os
dir = os.path.join('./home','/pc','data')	# 这里在./后面有/
dir = dir.replace('\\', '/')
print(dir)
dir = os.path.join('home','pc','./data')	# 这里./在最后面
dir = dir.replace('\\', '/')
print(dir)

输出结果为:

/pc/data
home/pc/./data

(3)path的其他函数并不是那么常用,仅仅了解一下就行:

函数名称功能
os.path.abspath(文件变量)返回文件的绝对路径
os.path.basename(路径名称)返回路径名称的最后部分(最后一个"/"后面的)
os.path.isfile()检查路径是否指向文件
os.path.isdir()检查路径是否指向文件夹目录

3.删除文件
删除文件是很简单的,只需要一行代码就可以搞定:

os.remove(要删除文件的路径)

但是要注意的是,如果文件不存在,系统会报错,所以需要在删除前用path.exists()函数确定一下文件是否存在。

4. 文件夹的创建
对于训练集,测试集,和验证集,我们需要创建三个文件夹来进行区别。所以创建文件夹是很有必要的,创建文件夹的方法很容易:

os.makedirs('路径/文件夹名称')	# 在特定路径创建
os.makedirs('名称')	# 在当前路径创建

如果指定的路径不存在,则系统会层层的创建下去。
举例:

import os

os.makedirs('E:/test/love/you')
os.makedirs('cats/cat')

输出的效果:
在这里插入图片描述
在这里插入图片描述

5. 文件的遍历和排序:
我们的数据集可能是数十万张的图片,为了遍历这些输入,我们需要获得一个文件中的全部文件名称,所以就需要文件遍历代码:

os.listdir(路径)

该函数会返回这个路径中的全部文件名称以及其格式。
下面的例子是输出我当前的文件夹:

import os

path = 'E:/pytorchProject1'
lists = os.listdir(path)

print(lists)
['.idea', 'cat', 'hello.py', 'MyNet.py', 'OS_use.py', 'traIn.py', 'use_cv.py', 'use_numpy.py']

可以看到,不仅是文件名称,文件夹名称程序也可以一并输出。这样输出的文件名称排序其实是按照哈希表的顺序来排列的,所以有时并不按照我们想要的方式排列。这个时候需要进行排序:
(1)排列数字:要排序的数组.sort()代码,这是Python的内置函数,它可以把数字从小到大进行排列
例如:

a = [1, 4, 6, 8, 3, 2]
print(a)
a.sort()
print(a)

输出为:

[1, 4, 6, 8, 3, 2]
[1, 2, 3, 4, 6, 8]

缺点是只能排序int类型的,否则会报错,显然不适合文件名
(2)字符串和数字排序:这个就没有内置函数帮忙了,只能自己写,我现在写一个先按照数字排序,如果不是数字则按照字母排序的(其实在Windows系统下,listdir()返回的值就是这么排序的,只有linux下会乱排,建议排序前先看一眼是否已经排好)。
先附上判断字符串属于什么的方法:

函数功能
isdigit()自然数
isalpha ()字母
isspace()空格
isdecimal()十进制数字
islower()小写字母
isupper()大写字母
istitle()单词首字母大写
isalnum()字母或数字

代码如下:

"""
@FileName:Sort.py
@Description:按照文件名开头的字母和者数字排序
@Author:段鹏浩
@Time:2023/3/14 12:33
"""
import os

path = "cat"
names = os.listdir(path)  # 获取全部的文件名

number = []  # 创建一个空数组,用来存那些数字开头的文件
alpha = []  # 创建一个空数组存放字符串开头的文件
el = []  # 用其他字符开头的文件

# 先遍历一遍,把各个文件名进行分离
for i in names:
    head = i[0]  # 取出头部
    if head.isdigit():
        number.append(i)  # 如果是数字类型,就放入数字列表
    elif head.isalpha():
        alpha.append(i)  # 如果是字母,就放入字母列表
    else:
        el.append(i)  # 放入其他列表

# 简单的冒泡排序:
i = len(number)
while i > 1:
    # print(i)
    b = 0
    c = 1
    while 1:
        # print(c)
        if c == i:
            break
        else:
            if int(number[b][0]) > int(number[c][0]):
                tem = number[b]
                number[b] = number[c]
                number[c] = tem
            b = b + 1
            c = c + 1
    i -= 1

i = len(alpha)
while i > 1:
    # print(i)
    b = 0
    c = 1
    while 1:
        # print(c)
        if c == i:
            break
        else:
            A = alpha[b][0]
            B = alpha[c][0]
            # 大小写判断,如果是大写则转换小写再比较
            if A.isupper():
                A = A.lower()
            if B.isupper():
                B = B.lower()
            if A > B:
                tem = alpha[b]
                alpha[b] = alpha[c]
                alpha[c] = tem
            b = b + 1
            c = c + 1
    i -= 1

# 最后把三个拼接起来:
names = number + alpha +el
print(names)

2.数据操作

1.构建训练集和验证集
(1)我们之前讲过,需要把数据集划分为训练集和验证集,且比例是7:3。我们还是用图片为例子,假设我们的数据集文件夹名称是data,那么我们首先要看看里面有多少张图片,并且记为n。

data = os.listdir('前面的路径\data')
n = len(data)

(2)因为图片的大小是不一样的,所以我们要统一一下图片的大小,然后把训练集和测试集分开,所以我们需要搞一个数据结构来在内存里面保存这些处理好的图片。因为图片是矩阵,那么我们就可以拿一个矩阵容器,就是一个空的多维矩阵来保存每一张修改后的图片。所以我们需要用到之前的Numpy来创建这个多维数组。但是这里矩阵是不能像列表那样依次append()进去的,只能先设置好一个全零或者全1的矩阵,然后一个个替换,所以我们需要先搞两个这样的多维矩阵,一个保存全部的训练集,一个保存全部的数据集。但是np中全零和全1的矩阵函数,zeros和ones都只能创建二维矩阵,不能创建多维矩阵,所以需要搞一个可以平铺所有图片的超大二维矩阵,然后用np.shape来变换它,把它多次折叠成一个和我们的训练集和验证集对应的多维矩阵。
假设我们需要的图片是 x × y x\times y x×y,那么我们可以写出如下创建容器的代码:

# 训练集的容器
train_set = np.zeros(int(0.7*n)*1*x*y)	# 这里我们假设我们的图片是灰度图,那么就只有n*1*x*y个像素点,如果是3维的则*3
train_set = np.reshape(train_set, (int(0.7*n),1,x,y))	# 注意这里的变换尺寸要和上面一致,如果是三维彩图就改成3

# 用一样的方法去创建测试集
test_set = np.zeros(int(0.3*n)*1*x*y)
test_set = np.reshape(test_set,(int(0.3*n), 1, x, y))

用int()是为了向下取整,防止图片数量出现小数,同时,小数出现在矩阵创建中也会出错。
(3)接下来,我们只需要把图片放入我们创建好的容器里面就好啦,搞训练集:

# 首先,我们需要在最后看一下有多少张图片完成转换,所以设置一个计数器
m = 0
# 之后就可以循环int(0.7*n)次,把图片一一转换然后放入
for i in range(int(0.7 * n)):
    path = os.path.join('E:/pictures/val', data[i])  # 拼接出文件路径
    if os.path.exists(path):
        img = cv.imread(path)  # 文件如果存在就开始读取
        img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)  # 把图片转换为灰度图
        img = cv.resize(img, (x, y))  # 变换图片尺寸
        train_set[i, 0, :, :] = img[:, :]  # 因为每一组只有一张图片,所以维度这里是1,但是从0开始,所以给用0切片切出的平面赋值
        m += 1  # 记录一下成功几个
        print("\r" + f"一共有:{int(0.7 * n)}张图片,现在完成:{m}张图片", end="", flush=True)  # 动态更新情况
      else:
      	print(f"路径:{path} 不存在")

最后这里的print里面,“\r"是回车符,表示该行清除全部输出,然后开始输出,end=”"表示输出以后留在这行(这样才会被新的清楚),flush=True是实时刷新的意思,不然python会等for循环结束以后才输出,如果图片很长的话,我们是看不到动态更新的情况的。
(4)接下来参考上面的做测试集:

# 测试集的容器
m2 = 0	# 一样的标记
for i in range(int(n*0.3)):
	path = os.path.join("路径名称",data[n-i-1])	# 这里就从后取起来,如果要从前取那就切片
	if os.path.exists(path):
		img = cv.imread(path)
		img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
		img = cv.resize(img, (x, y))
		test_set[i, 0, :, :] = img[:, :]
		m2 += 1
		print("\r"+f"一共有{int(0.3*n)}张图片,现在处理了{m2}张图片", end = "", flush = True)
	else:
		print(f"路径:{path} 不存在")
	

(5)最后记录一下成功多少,并且把训练集和测试集保存起来

print(f"训练集共{m}张图片,测试集共{m2}张图片")
np.save('train_set.npy', train_set)
np.save('test_set.npy', test_set)

(6)实际的代码,在用的时候记得把我的路径换成你自己的路径:

"""
@FileName:train_test.py
@Description:划分训练集和测试集
@Author:段鹏浩
@Time:2023/3/11 21:33
"""
import os
import numpy as np
import cv2 as cv

data = os.listdir('E:/pictures/val')
n = len(data)
x = 500
y = 500

# 训练集的容器
train_set = np.zeros(int(0.7 * n) * 1 * x * y)  # 这里我们假设我们的图片是灰度图,那么就只有n*1*x*y个像素点,如果是3维的则*3
train_set = np.reshape(train_set, (int(0.7 * n), 1, x, y))  # 注意这里的变换尺寸要和上面一致,如果是三维彩图就改成3

# 用一样的方法去创建测试集
test_set = np.zeros(int(0.3*n)*1*x*y)
test_set = np.reshape(test_set,(int(0.3*n), 1, x, y))

# 首先,我们需要在最后看一下有多少张图片完成转换,所以设置一个计数器
m = 0
# 之后就可以循环int(0.7*n)次,把图片一一转换然后放入
for i in range(int(0.7 * n)):
    path = os.path.join('E:/pictures/val', data[i])  # 拼接出文件路径
    if os.path.exists(path):
        img = cv.imread(path)  # 文件如果存在就开始读取
        img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)  # 把图片转换为灰度图
        img = cv.resize(img, (x, y))  # 变换图片尺寸
        train_set[i, 0, :, :] = img[:, :]  # 因为每一组只有一张图片,所以维度这里是1,但是从0开始,所以给用0切片切出的平面赋值
        m += 1  # 记录一下成功几个
        print("\r" + f"一共有:{int(0.7 * n)}张图片,现在完成:{m}张图片", end="", flush=True)  # 动态更新情况


print("\n训练集划分结束\n")

# 测试集的容器
m2 = 0  # 一样的标记
for i in range(int(n * 0.3)):
    path = os.path.join("E:/pictures/val", data[n - i - 1])  # 这里就从后取起来,如果要从前取那就切片,因为从0开始所以要减一
    if os.path.exists(path):
        img = cv.imread(path)
        img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        img = cv.resize(img, (x, y))
        test_set[i, 0, :, :] = img[:, :]
        m2 += 1
        print("\r" + f"一共有{int(0.3 * n)}张图片,现在处理了{m2}张图片", end="", flush=True)
    else:
        print(f"路径:{path} 不存在")

print("\n测试集划分结束\n")

print(f"训练集共{m}张图片,测试集共{m2}张图片")
np.save('train_set.npy', train_set)
np.save('test_set.npy', test_set)

输出:

一共有:333张图片,现在完成:333张图片
训练集划分结束

一共有142张图片,现在处理了142张图片
测试集划分结束

训练集共333张图片,测试集共142张图片

在这里插入图片描述
2. 把npy文件读取为图片:
这里很简单,比如我们读取并显示上面的训练集的第100张图片,我们只需要四步就行:
(1)读取整个npy文件:

train_set = np.load("train_set.npy")

(2)定位到对应的图片,记得索引是从0开始,所以第一百张100其实是99:

img = train_set[99, 0, :, :]

(3)这里很关键,npy的数据类型(float)和cv读取的数据类型(uint8)是不一样的,所以需要进行类型转换,转换方法也很简单,用np.astype就可以转换矩阵中全部数据的类型

img = img.astype(np.uint8)

插一句,uint8是无符号八位整型。
(4)显示即可:

cv.imshow("第一百张图", img)
cv.waitKey(0)

下面是完整代码:

"""
@FileName:use_numpy.py
@Description:怎么用numpy的学习
@Author:段鹏浩
@Time:2023/3/11 21:33
"""
import os
import numpy as np
import cv2 as cv

train_set = np.load("train_set.npy")
img = train_set[99, 0, :, :]
img = img.astype(np.uint8)

# 把原图也显示一下:
data = os.listdir('E:/pictures/val')
path = os.path.join('E:/pictures/val', data[99])
if os.path.exists(path):
    img0 = cv.imread(path)
    img0 = cv.resize(img0, (500, 500))
    cv.imshow(u"原图", img0)  # 加u把中文转为unicode码,防止出错


cv.imshow(u"第一百张图", img)
cv.waitKey(0)

输出如下(这是个车厘子,别害怕):
在这里插入图片描述
3. opencv显示中文:
可惜的是这里中文没有显示成功。
这里就需要进行中文解码了:

def zh_cn(string):
    return string.encode('gb2312').decode(errors='ignore')

完整代码如下

"""
@FileName:use_numpy.py
@Description:怎么用numpy的学习
@Author:段鹏浩
@Time:2023/3/11 21:33
"""
import os
import numpy as np
import cv2 as cv


def zh_cn(string):
    return string.encode('gb2312').decode(errors='ignore')


train_set = np.load("train_set.npy")
img = train_set[99, 0, :, :]
img = img.astype(np.uint8)

# 把原图也显示一下:
data = os.listdir('E:/pictures/val')
path = os.path.join('E:/pictures/val', data[99])
if os.path.exists(path):
    img0 = cv.imread(path)
    img0 = cv.resize(img0, (500, 500))
    cv.imshow(zh_cn("原图"), img0)  # 加u把中文转为unicode码,防止出错

cv.imshow(zh_cn("现在的图"), img)
cv.waitKey(0)

这个函数可以把输入的中文字符串转换为对应编码,缺点是有大部分的中文直接不能用或者不显示,如果非要显示中文的话,在另起一个conda环境,下载python2,在里面用cv.imshow(u"中文",img)是可以正常显示的。
附上显示结果:
在这里插入图片描述
第二张显然没有正常显示。

4.把数据集的图片一张张的划分:
其实,肯定有人觉得,如果图片很多,矩阵太大内存放不下怎么办,那就一张张的分,代码和之前很像,只不过不需要进行矩阵的提取创建了,因为代码简单,就不一点点讲解了:

"""
@FileName:test_and_train.py
@Description:单张的划分训练集和测试集
@Author:段鹏浩
@Time:2023/3/15 17:15
"""
import cv2 as cv
import os

# 首先还是一样的读取全部的数据,并且计算其大小
data = os.listdir('E:/pictures/val')
n = len(data)
n1 = int(0.7 * n)  # 训练集的量
n2 = int(0.3 * n)  # 测试集的量

# 创建两个新的文件夹
os.makedirs('E:/pictures/train')
os.makedirs('E:/pictures/test')

m1 = 0
for i in range(n1):
    path = os.path.join('E:/pictures/val', data[i])
    if os.path.exists(path):
        img = cv.imread(path)
        img = cv.resize(img, (300, 300))    # 现在就不用转灰度图了,但是可以把图片格式和名称转一下
        name = 'fruit' + str(i) + ".jpg"     # 水果1,水果2这样排列,并且统一保存为jpg格式
        path2 = os.path.join('E:/pictures/train', name)
        cv.imwrite(path2, img)
        m1 += 1
        print('\r'+f"一共有{n1}张训练集图片,目前成功划分{m1}张", end="", flush=True)
    else:
        print(f"路径:{path} 不存在")

print("\n")     # 结束换行

m2 = 0
for i in range(n2):
    path = os.path.join('E:/pictures/val', data[n-i-1])
    if os.path.exists(path):
        img = cv.imread(path)
        img = cv.resize(img, (300, 300))    # 现在就不用转灰度图了,但是可以把图片格式和名称转一下
        name = 'fruit' + str(i) + ".jpg"     # 水果1,水果2这样排列,并且统一保存为jpg格式
        path2 = os.path.join('E:/pictures/test', name)
        cv.imwrite(path2, img)
        m2 += 1
        print('\r'+f"一共有{n2}张测试集图片,目前成功划分{m2}张", end="", flush=True)
    else:
        print(f"路径:{path} 不存在")
print("\n")

输出结果:

一共有333张训练集图片,目前成功划分333张

一共有142张测试集图片,目前成功划分142张

在这里插入图片描述
需要显示的话,直接来拿图片就行,也不需要转换格式。

3.总结:

本章我们学习了怎么进行文件的创建,确定文件存在与否,以及文件读取,文件的遍历和排序。还学习了两种划分数据集的为训练集和测试集的方法。下一章,我们将学习如何使用Matpotlib来画图(画图有利于我们进行数据分析)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值