"""
此程序用于把原始的图片和标签转换为截图后的图片和标签
设定固定的窗口大小
去截取物体图片
可以把一张图多截取几张图片,以增加数据量
已实现
"""
import os.path
import shutil
import cv2
import random
from multiprocessing import Process,Pool
class Crop_Img:
# 0 初始化
def __init__(self,txt_path="",img_path="",window_w=640,window_h=640):
self.txt_path=txt_path #待处理文本路径
self.img_path=img_path #待处理图片路径
self.window_w=window_w #滑窗宽
self.window_h=window_h #滑窗高
self.temp_list=[] #目标在原图中的位置列表[xmin,ymin,xmax,ymax,class]
self.slide_list=[] #滑窗在原图中的位置列表[xmin,ymin,xmax,ymax]
self.xyxy_list=[] #异物在滑窗中的位置列表[xmin,ymin,xmax,ymax]
self.xxyywwhh_list=[] #异物在滑窗中归一化后的位置列表[class,x,y,w,h]
# 1 判断保存文件是否存在
def path_isexist(self):
self.save_dir = os.path.dirname(self.img_path)
self.save_dir=os.path.dirname(self.save_dir)
self.save_img_dir = os.path.join(self.save_dir, "save_imgs") # 裁剪后保存图片的路径
self.save_txt_dir = os.path.join(self.save_dir, "save_labels") # 裁剪后保存图片的标签
if not os.path.exists(self.save_img_dir):
os.mkdir(self.save_img_dir)
if not os.path.exists(self.save_txt_dir):
os.mkdir(self.save_txt_dir)
# 2 把读取的xywh转换为xyxy然后返回
def get_xyhw2xyxy_from_txt(self):
"""
根据txt读取坐标信息,把归一化的xyhw坐标转换为xyxy坐标
"""
image = cv2.imread(self.img_path)
file = open(self.txt_path)
content = file.readlines()
for x in content:
x = x.replace("\n", "")
temp_coor = x.split(" ")
xx = float(temp_coor[1])
yy = float(temp_coor[2])
ww = float(temp_coor[3])
hh = float(temp_coor[4])
wt = int(image.shape[1])
ht = int(image.shape[0])
xmin = int(wt * xx - 0.5 * ww * wt)
xmax = int(wt * xx + 0.5 * ww * wt)
ymin = int(ht * yy - 0.5 * hh * ht)
ymax = int(ht * yy + 0.5 * hh * ht)
self.temp_list.append(xmin)
self.temp_list.append(ymin)
self.temp_list.append(xmax)
self.temp_list.append(ymax)
self.temp_list.append(temp_coor[0])
print("temp",self.temp_list)
return self.temp_list
# 3 随机生成截取区域,
def slide_window(self):
"""
根据目标在原始图片中的位置随机生成滑动窗口
可以多生成几个滑动窗口以增强数据集
"""
window_num=1 #想要生成多少个滑窗
image = cv2.imread(self.img_path)
img_w = int(image.shape[1])
img_h = int(image.shape[0])
for i in range(window_num):
while (True):
slide_list = [] # 临时存放滑窗坐标
while (True):
ys = random.randint(0, int(self.temp_list[1]))
if ys + self.window_h < img_h:
break
while (True):
xs = random.randint(0, int(self.temp_list[0]))
if xs + self.window_w < img_w:
break
if xs < self.temp_list[0] and xs + self.window_w > self.temp_list[2] and ys < self.temp_list[1] and ys + self.window_h > self.temp_list[3]:
slide_list.append(xs)
slide_list.append(ys)
slide_list.append(xs + self.window_w)
slide_list.append(ys + self.window_h)
self.slide_list.append(slide_list)
break
else:
continue
# 4 裁剪图片部分区域
def crop_img_from_originalimg(self,save_img_dir):
"""
把在原图中截取的图片保存在指定文件夹save_img_dir中
"""
i=1
for slide_list in self.slide_list:
image = cv2.imread(self.img_path)
a = int(slide_list[0])
b = int(slide_list[2])
c = int(slide_list[1])
d = int(slide_list[3])
cropImg = image[c:d, a:b] # 裁剪图像 注意image[ymin:ymax,xmin:xmax]
name = self.img_path.split("\\")[-1]
name=name.split(".")[0]
name=name + "_{}.jpg".format(i)
i += 1
dest = os.path.join(save_img_dir, name)
cv2.imwrite(dest, cropImg) # 写入图像路径
# 5 获取异物在滑窗内的xyxy和xywh
def get_realative_xyxy_xywh_in_slidewindow(self):
"""
返回物体在滑动窗口的左上右下坐标,和相对于截取后物体在滑窗中的相对坐标classesname,xx,yy,ww,hh
"""
for slide_list in self.slide_list:
xxyywwhh_list=[]
xyxy_list=[]
xmin = int(self.temp_list[0]) - int(slide_list[0])
ymin = int(self.temp_list[1]) - int(slide_list[1])
xmax = int(self.temp_list[2]) - int(slide_list[0])
ymax = int(self.temp_list[3]) - int(slide_list[1])
class_name = self.temp_list[4]
xx = float(float(xmin + xmax) / 2.0) / float(self.window_w)
yy = float(float(ymin + ymax) / 2.0) / float(self.window_h)
ww = float(xmax - xmin) / float(self.window_w)
hh = float(ymax - ymin) / float(self.window_h)
xxyywwhh_list.append(class_name)
xxyywwhh_list.append(xx)
xxyywwhh_list.append(yy)
xxyywwhh_list.append(ww)
xxyywwhh_list.append(hh)
self.xxyywwhh_list.append(xxyywwhh_list)
xyxy_list.append(xmin)
xyxy_list.append(ymin)
xyxy_list.append(xmax)
xyxy_list.append(ymax)
self.xyxy_list.append(xyxy_list)
print("xyxy_list",self.xyxy_list)
print("xxyywwhh_list",self.xxyywwhh_list)
# 6 把get_realative_xyxy_xywh_in_slidewindow这个函数获取的wywh坐标写入txt文件
def write_realative_txt(self,save_txt_dir):
"""
把目标在滑动窗口中的[class,x,y,w,h]写入txt文件中
"""
i=1
for xxyywwhh_list in self.xxyywwhh_list:
txt_name = self.txt_path.split("\\")[-1]
txt_name=txt_name.split(".")[0]
txt_name=txt_name+"_{}.txt".format(i)
i+=1
save_txt_path = os.path.join(save_txt_dir, txt_name)
with open(save_txt_path, "a", encoding="gbk") as f:
f.write(str(xxyywwhh_list[0]))
f.write(" ")
f.write(str(xxyywwhh_list[1]))
f.write(" ")
f.write(str(xxyywwhh_list[2]))
f.write(" ")
f.write(str(xxyywwhh_list[3]))
f.write(" ")
f.write(str(xxyywwhh_list[4]))
# 7 运行该函数即可
def main_get_crop_img_txt(self):
"""
获得最终结果运行此方法即可
:param save_img_dir:
:param save_txt_dir:
:return:
"""
self.path_isexist()
self.get_xyhw2xyxy_from_txt()
self.slide_window()
self.get_realative_xyxy_xywh_in_slidewindow()
self.crop_img_from_originalimg(self.save_img_dir)
self.write_realative_txt(self.save_txt_dir)
# 8 根据cxywh格式在原图中画框
def draw_rec(self, cor_list,save_flag=True):
"""
save_flag:是否把画的框保存在原图中
True:保存
False:仅显示
根据坐标在图片中画框
cor_list[xmin,ymin,xmax,ymax]
"""
image = cv2.imread(self.img_path)
draw_0 = cv2.rectangle(image, (int(cor_list[0]), int(cor_list[1])), (int(cor_list[2]), int(cor_list[3])),
(0, 0, 255), 2)
if save_flag:
cv2.imwrite(self.img_path, draw_0)
else:
cv2.imshow('deaw_0', draw_0)
cv2.waitKey(0) # 等待按键
#其他功能————————————————————————————————————————————————————————————————————
# 9 从文本里读路径并找出来然后放在指定文件夹
def get_contents_without_huiche_from_txt(self):
"""
传入一个文本
输出没有回车符的队列
forexample:
输入:
print(read_txt(r"D:\desk\A_3.txt"))
文本内容读进来是这样的
["asdasda\n",asdasdasd\n]
输出:
["asdasda",asdasdasd]
"""
file = open(self.txt_path)
content = file.readlines()
temp_list = []
for x in content:
x = x.replace("\n", "")
temp_list.append(x)
return temp_list
# 10 批量重命名
def batch_rename(self,path):
"""
:param path:
输入
——imgs
—————A
———————a.jpg
———————at.jpg
———————ad.jpg
———————aw.jpg
———————aq.jpg
———————ar.jpg
...
—————B
......
输出
——imgs
—————A
———————A_1.jpg
———————A_2.jpg
———————A_3.jpg
———————A_4.jpg
———————A_5.jpg
———————A_6.jpg
...
:return:
"""
for dir_name in os.listdir(path):
path_dir = os.path.join(path, dir_name)
if os.path.isdir(path_dir):
i = 0
for img_name in os.listdir(path_dir):
img_path = os.path.join(path_dir, img_name)
i += 1
name_suffix=img_name.split(".")[-1]
rename_path = os.path.join(path_dir, "{}_{}.{}".format(dir_name, i,name_suffix))
print(rename_path)
os.rename(img_path, rename_path)
print("Successflly Changed")
return
# 11 从txt列出的路径里找到对应的图片并加入到文件夹中去
def from_txt_get_images(self,save_img_dir, txt_path):
with open(txt_path, "r", encoding="gbk") as f:
content = f.readlines()
image_path_list = content[0].split("\\n")
image_path_list.remove("")
for x in image_path_list:
x = x.split("/")
a = x[0][:-6]
b = x[1]
img_path = a + "JEPGimages\\" + b
img_name = img_path.split("\\")[-1]
new_img_path = save_img_dir + "\\" + img_name
shutil.copy(img_path, new_img_path)
print(new_img_path)
# 12 统计图片的大中小目标个数
def get_num_big_mediun_small(self):
self.get_xyhw2xyxy_from_txt()
self.obj_num = {"small_num": 0, "medium_num": 0, "big_num": 0} #用来保存目标数量
w_object=self.temp_list[2]-self.temp_list[0]
h_object=self.temp_list[3]-self.temp_list[1]
piex_object=w_object*h_object
if piex_object <= 32 * 32 and piex_object >= 0:
self.obj_num["small_num"] += 1
if piex_object > 32 * 32 and piex_object <= 96 * 96:
self.obj_num["medium_num"] += 1
if piex_object > 96 * 96:
self.obj_num["big_num"] += 1
print(self.obj_num)
# 13 找出最合适切块儿的行与列,结合下面的cut_img(14)一起使用
def judge_best_column_row(self,img_size_list):
"""
:param img_size_list: 你想要的图像块儿的尺寸
:return:
"""
# 找出最合适的分割行和列
img = cv2.imread(self.img_path)
h, w = img.shape[:2]
best_row = 1
best_colu = 1
patch_h = h
patch_w = w
close_value_h = img_size_list[0]
close_value_w = img_size_list[1]
for row in range(2, 20):
temp_h = h / row
if abs(temp_h - close_value_h) < abs(patch_h - close_value_h):
patch_h = temp_h
best_row = row
for colu in range(2, 20):
temp_w = w / colu
if abs(temp_w - close_value_w) < abs(patch_w - close_value_w):
patch_w = temp_w
best_colu = colu
return best_row, best_colu
# 14 把图片切块儿
def cut_imgs(self):
row,column=self.judge_best_column_row([640,640])
# 需要分割的图片
org_img = cv2.imread(self.img_path)
# 判断文件夹是否为空?
path_save_dir = os.path.dirname(self.img_path)
path_save=os.path.join(path_save_dir,"cut_dir")
if not os.path.exists(path_save):
os.mkdir(path_save)
if len(os.listdir(path_save)) != 0:
shutil.rmtree(path_save)
os.mkdir(path_save)
height, width = org_img.shape[:2]
print('height %d widht %d' % (height, width))
row_step = (int)(height / row)
column_step = (int)(width / column)
print('row step %d col step %d' % (row_step, column_step))
print('height %d widht %d' % (row_step * row, column_step * column))
img = org_img[0:row_step * row, 0:column_step * column]
# 把图片写入文件夹
for i in range(row):
for j in range(column):
pic_name = path_save + '\\' + str(i) + "_" + str(j) + ".jpg"
tmp_img = img[(i * row_step):(i * row_step + row_step),
(j * column_step):(j * column_step) + column_step]
cv2.imwrite(pic_name, tmp_img)
#批量在原图中截目标
def get_crop_img_and_txt(img_dir,txt_dir):
for img_name in os.listdir(img_dir):
if img_name.endswith("jpg"):
img_path=os.path.join(img_dir,img_name)
txt_name=img_name.split(".")[0]+".txt"
txt_path=os.path.join(txt_dir,txt_name)
crop_img = Crop_Img(txt_path,img_path,2000,2000)
crop_img.main_get_crop_img_txt()
#批量在图片中画框
def draw_rec_in_img(save_img_dir,save_txt_dir):
for img_name in os.listdir(save_img_dir):
if img_name.endswith("jpg"):
img_path=os.path.join(save_img_dir,img_name)
txt_name=img_name.split(".")[0]+".txt"
txt_path=os.path.join(save_txt_dir,txt_name)
c=Crop_Img(txt_path,img_path)
temp=c.get_xyhw2xyxy_from_txt()
c.draw_rec(temp,True)
if __name__ == '__main__':
weichuli = ["A","B","B-","C","C-","D","D-","D--","E","E-","F","F-","G","G-","H","H-","I","J","K","K-", "L", "L-"]
print(len(weichuli))
for x in weichuli:
img_dir = r"F:\xuedi\New_img\images\{}".format(x) # 未裁剪的图片
txt_dir = r"F:\xuedi\New_img\labels-hebing\{}".format(x) # 未裁剪的标签
get_crop_img_and_txt(img_dir, txt_dir)
# draw_rec_in_img(save_img_dir,save_txt_dir)
# c=Crop_Img(img_path=img_path)
# c.cut_imgs()
当时处理机场异物数据集时写的代码
把分辨率为3000*4000的图片
截图为1000*1000的图片
以及生成对应的标签文件