公交快到站了,我赶紧写了个图像样本采集器-灵析社区

GG

小团队搞算法,需要多面手,等同当个厨子要从买菜到炒菜,一直到端给顾客,还要劝顾客多吃,以便好收拾盘子。

据说,我的前辈曾是大厂的大牛,但是来了之后没出成绩,反而先要求配两个助手。后来,经过很长时间,也没有什么成绩,大牛说是助手不行,要换更高级的助手。

我听到后,我说,我不是大牛,不用助手,我喜欢从琐碎的事情中寻找规律,并开发出工具来提高效率。

图像处理,有三驾马车:硬件、样本、算法。

其中,硬件舍得花钱买就行,算法基本上对成熟的模型进行微调即可。而这个样本,却需要实打实地进行收集和标注。

比如做语义分割,你觉得,哇,真了不起,计算机那么智能,怎么就把各类物体区分出来了。

你看下面这张图,传入一张小猫的自拍照,计算机就像是能读懂图像的语义一样,轻松地把小猫、草地、森林、天空,分割了出来。


其实,它之所以智能,是因为前期喂了它很多数据。这就和养孩子一样,她叫你一声“爸爸”,你感觉好神奇,其实你仔细想想,你喂了她多少奶粉,你叫了她多少句“爸爸”。

在计算机能识别物体之前,是用了成千上万张标注好的图片进行了训练。这些图片是一个点一个点地标记了哪些像素是猫,哪些像素是草地。

而在自动驾驶领域,标注更为变态,你需要标上你看到的一切,细致到行人、非机动车、机动车、建筑物、绿植、电线、路牌、不可跨越的障碍物(如护栏)、可跨越的障碍物(如马路牙子)等等。

所以,你看到现在很多人工智能的企业招聘一种职业,叫图像标注员(可兼职,一天200)。其实,这活不好干。

市面上有很多图像样本处理工具,有做框选的labelimg,也有做标注的labelme,甚至还有人说用Photoshop。

是的,我就用过PS,是在要识别一些目标上,比如行人。需要的素材就是包含行人的图片5000张,以及不包含行人的图片1000张,这些图片要求宽高尺寸都一样。

首先,用PS打开下面的图片,然后使用裁剪工具,框出固定大小或者比例的行人,然后保存。以此类推。

这样效率很低。做了50张,我就累了。框图不累,但是输入文件名点击保存按钮等各种操作很累。

于是,我就写了下面这个工具。


它会加载文件夹下的素材图片。你只需要在图片上拉一个框出来,它就会保存这个框对应的图片。而且,起初保存的是1开头的文件名,点击鼠标右键后,保存的是0开头的文件名。这样,就可以做到先框选行人,然后再框选背景,大大地提高了效率。

以下是实现的代码,100行代码,相信结合注释,你能轻松读懂。

#%%  导入相关包
import os
import tkinter as tk
from PIL import Image
from PIL import ImageTk
import time

# 鼠标左键按下事件
def left_mouse_down(event):
    global left_mouse_down_x, left_mouse_down_y
    # 记录按下的坐标,赋值给全局变量
    left_mouse_down_x = event.x
    left_mouse_down_y = event.y

# 鼠标左键抬起事件
def left_mouse_up(event):
    # 记录抬起时的坐标,鼠标左键抬起时x,y坐标
    left_mouse_up_x = event.x
    left_mouse_up_y = event.y

    # 通过抬起的点减去按下的点,比划矩形,计算出宽和高
    width = left_mouse_up_x - left_mouse_down_x
    height = left_mouse_up_y - left_mouse_down_y
    # 如果宽高太小,有可能是点击了一下,或者想放弃这次操作
    if width < 20 or height < 20:
        print("size is to small,不要了")
        return

    # 如果宽大于高,让高依照比例自动计算:谁幅度大听谁的
    if width > height:
        _height = int(width*h_scale/w_scale)
        # 强行定义鼠标抬起的位置
        left_mouse_up_y = left_mouse_down_y + _height
    else: # 宽不大于高,一样操作
        _width = int(height*w_scale/h_scale)
        left_mouse_up_x = left_mouse_down_x + _width

    # 保存文件
    f_name = "out/"+str(crop_pos)+"_"+str(int(time.time()))+".png"
    corp_image = image.crop((left_mouse_down_x, left_mouse_down_y, left_mouse_up_x, left_mouse_up_y))
    corp_image.save(f_name)

# 鼠标左键按下并移动
def moving_mouse(event):
    global sole_rectangle # 绘制的矩形
    # 鼠标按下的x,y
    global left_mouse_down_x, left_mouse_down_y
    moving_mouse_x = event.x
    moving_mouse_y = event.y
    # 通过移动的点减去起始按下的点,比划矩形,计算出宽和高
    width = moving_mouse_x - left_mouse_down_x
    height = moving_mouse_y - left_mouse_down_y
    if width > height:
        # 如果宽大于高,让高依照比例自动计算:谁幅度大听谁的
        _height = int(width*h_scale/w_scale)
        # 强行定义鼠标移动的位置
        moving_mouse_y = left_mouse_down_y + _height
    else:
        _width = int(height*w_scale/h_scale)
        moving_mouse_x = left_mouse_down_x + _width
    # 如果原来画过矩形,删除前一个矩形,绘制出新的
    if sole_rectangle is not None:
        canvas.delete(sole_rectangle) 
    sole_rectangle = canvas.create_rectangle(left_mouse_down_x, left_mouse_down_y, moving_mouse_x,moving_mouse_y, outline='red')

# 鼠标右键按下
def right_mouse_down(event):
    pass

# 鼠标右键抬起
def right_mouse_up(event):
    global crop_pos
    crop_pos = 0
    
#%% 执行代码
if __name__ == '__main__':
    
    # 鼠标左键按下时x,y坐标
    left_mouse_down_x, left_mouse_down_y = 0, 0
    sole_rectangle = None # 画出的矩形
    w_scale = 1 # 画出的宽高比例
    h_scale = 3

    target = "img"
    all_files=os.listdir(target)
    
    for f_name in all_files:

        crop_pos = 1
        img_path = target+"/"+f_name

        win = tk.Tk()
        frame = tk.Frame()
        frame.pack()

        button = tk.Button(frame, text = "下一张", command=win.destroy)
        button.pack()
        
        image = Image.open(img_path)
        image_x, image_y = image.size
        img = ImageTk.PhotoImage(image)
        canvas = tk.Canvas(frame, width=image_x, height=image_y, bg='white')
        i = canvas.create_image(0, 0, anchor='nw', image=img)
        canvas.pack()
        
        canvas.bind('<Button-1>', left_mouse_down) # 鼠标左键按下
        canvas.bind('<ButtonRelease-1>', left_mouse_up) # 鼠标左键释放
        canvas.bind('<Button-3>', right_mouse_down) # 鼠标右键按下
        canvas.bind('<ButtonRelease-3>', right_mouse_up) # 鼠标右键释放
        canvas.bind('<B1-Motion>', moving_mouse) # 鼠标左键按下并移动

        win.mainloop()

其逻辑有几点:

  1. 鼠标按下时记录起点(x1,y1)坐标,鼠标抬起时记录终点(x2,y2)坐标,对图片中起点和终点两个点形成的矩形进行裁剪保存。
  2. 文件名添加前缀crop_pos的数值,当点击右键时,把crop_pos的值改为0。这样就保存了0开头的文件名。

定义图像的宽高比,当鼠标拖动时,依照比例强行画出符合宽高比的矩形框,类似于PS中按着Shift+Alt等比例缩放。
还有一个小彩蛋,那就是当你下手选择图像之后,发现此位置无法裁出符合要求的图,想反悔的时候,把框缩小,此时会放弃这张图,不会保存。

阅读量:746

点赞量:0

收藏量:0