python JPG图片手动圈画掩膜Mask轮廓ROI

首先尝试实现一张JPG图片的勾画
注:opencv2键鼠操作可参考本博主的文章:python-opencv控制鼠标操作

import numpy as np
import cv2 as cv
from matplotlib import pylab as plt

def drawing(event, x, y, flags, param):
    global img #声明img为全局变量以在drawing函数内部使用img
    if event == 0 and flags == 1: #发现鼠标移动且左键按下
        print(x,y,"\n") #x y为此时鼠标坐标
        cv.circle(img, (x, y), 1, (71,0, 255), -1) #img图像为底 在(x,y)处画一个大小为1的点,其颜色为(71,0,255)(RGB)
        cv.imshow('img1', img) #显示新的img图像与窗口
       
        

img = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00001.jpg')#读取jpg文件
cv.namedWindow("img1")#命名窗口为img1
cv.moveWindow('img1',100,100)#将名为img1的窗口移动到屏幕100,100的位置
cv.setMouseCallback('img1', drawing)#将名为img1的窗口与IO操作函数drawing关联
cv.imshow('img1', img)#显示名为img1的窗口,窗口内图片为刚读取的JPG图像:img
cv.waitKey(0)#等待操作
cv.destroyAllWindows()#关闭窗口

运行可以得到如下图窗口 按下鼠标左键并移动即可画图
注意:若移动过快则会画出许多点,所以请勿勾画过快
在这里插入图片描述
目前我们只实现了一张图像的勾画,怎样才能实现多张图像的快速勾画呢?
比如使用鼠标滚轮快速切换图像
听起来很简单,但也是需要做不少修改的,需要我们理解opencv2窗口、图像、IO函数的机制,以五张图像为例,代码如下(为方便大家理解,未采用循环操作,请大家在自己代码中自行更改):

import numpy as np
import cv2 as cv
from matplotlib import pylab as plt

now=0 #从第一张图片开始 意为当前为第几张图片
im=[] #用来存储所有图片
imgs='image'+str(now) #当前窗口名 根据now进行修改以实现窗口切换


def drawing(event, x, y, flags, param):

    global imgs
    global now
    global im
    
    if event == 0 and flags == 1: #检测到鼠标移动且左键按下
        print(x,y,"\n")
        cv.circle(im[now], (x, y), 1, (71,0, 255), -1)
        cv.imshow(imgs, im[now])
    elif event==10: #检测到滚轮滑动
        cv.destroyAllWindows() #先关闭当前窗口
        if(flags<0): #如果是下滑
            now+=1 #图片切换到下一张
            if(now>(num-1)):now=0 #若超过最大图片数则置为第一张循环开始
            imgs='image'+str(now) #窗口名也随之进行修改
            cv.namedWindow(imgs) #建立新的窗口
            cv.moveWindow(imgs,100,100) #移动新的窗口
            cv.setMouseCallback(imgs, drawing) #为新的窗口关联IO函数
            cv.imshow(imgs, im[now]) #显示新的窗口 
        else: #如果是上滑
            now-=1 #切换到上一张
            if(now<0):now=num-1 #若当前为第一张 则切换为最后一张进行循环
            imgs='image'+str(now) #以下同上
            cv.namedWindow(imgs)
            cv.moveWindow(imgs,100,100)
            cv.setMouseCallback(imgs, drawing)  
            cv.imshow(imgs, im[now])
       
        
num = 5 #共五张图片
#依次读取五张图片
img = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00001.jpg')
img2 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00002.jpg')
img3 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00003.jpg')
img4 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00004.jpg')
img5 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00005.jpg')
#将图片数据保存在im列表中
im.append(img)
im.append(img2)
im.append(img3)
im.append(img4)
im.append(img5)

#↓↓↓↓↓↓↓↓↓先建立第一张图片的窗口↓↓↓↓↓↓↓↓↓
cv.namedWindow(imgs) 
cv.moveWindow(imgs,100,100)
cv.setMouseCallback(imgs, drawing)
cv.imshow(imgs, im[now])
    
cv.waitKey(0)
cv.destroyAllWindows()

由此一来,便实现了滚轮快速切换勾画图像的需求。

现在只实现了画图而已,MASK掩膜其实是勾画内部全部为1,外部全部为0的图像,与原JPG文件并无关系,所以我们需要记录勾画的坐标然后找到内部的点并将其置为1,其余置为0

先尝试记录下每张图像勾画的坐标

import numpy as np
import cv2 as cv
from matplotlib import pylab as plt

num = 5
now=0
im=[]
XY=[] #XY用来存储五张图片各自勾画的坐标
imgs='image'+str(now)
for i in range(num):
    XY.append([])



def drawing(event, x, y, flags, param):
    global imgs #窗口名
    global now #当前图片
    global im #所有图片
    if event == 0 and flags == 1: #鼠标移动 and 左键按下
    	#注:为了加快运算速度,勾画时不会存储本图片已勾画过的点
        if([x,y] not in XY[now]): #若当前点未记录则添加
            XY[now].append([x,y])
        if([x+1,y+1] not in XY[now]):#将当前点与附近的点都添加进勾画坐标以防止出现间隙
            XY[now].append([x+1,y+1])
        if([x-1,y-1] not in XY[now]):
            XY[now].append([x-1,y-1])
        if([x+1,y-1] not in XY[now]):
            XY[now].append([x+1,y-1])
        if([x-1,y+1] not in XY[now]):
            XY[now].append([x-1,y+1])
        if([x+1,y] not in XY[now]):
            XY[now].append([x+1,y])
        if([x-1,y] not in XY[now]):
            XY[now].append([x-1,y])
        if([x,y+1] not in XY[now]):
            XY[now].append([x,y+1])
        if([x,y-1] not in XY[now]):
            XY[now].append([x,y-1])
        cv.circle(im[now], (x, y), 1, (71,0, 255), -1)
        cv.imshow(imgs, im[now])
    elif event==10: #滚轮
        cv.destroyAllWindows()
        if(flags<0): #向下滚
            now+=1
            if(now>num-1):now=0
            imgs='image'+str(now)
            cv.namedWindow(imgs)
            cv.moveWindow(imgs,100,100)
            cv.setMouseCallback(imgs, drawing)  
            cv.imshow(imgs, im[now])
        else: #向下滚
            now-=1
            if(now<0):now=num-1
            imgs='image'+str(now)
            cv.namedWindow(imgs)
            cv.moveWindow(imgs,100,100)
            cv.setMouseCallback(imgs, drawing)  
            cv.imshow(imgs, im[now])
       
        

img = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00001.jpg')
img2 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00002.jpg')
img3 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00003.jpg')
img4 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00004.jpg')
img5 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00005.jpg')
im.append(img)
im.append(img2)
im.append(img3)
im.append(img4)
im.append(img5)
cv.namedWindow(imgs)
cv.moveWindow(imgs,100,100)
cv.setMouseCallback(imgs, drawing)
cv.imshow(imgs, im[now])
    
cv.waitKey(0)
cv.destroyAllWindows()

XY数据如下图所示 其中图片0 1 2被勾画 3 4未勾画
python JPG图片手动圈画掩膜Mask轮廓ROI_第1张图片
现在我们便成功得到了勾画坐标,进行一些处理便可以生成MASK掩膜文件
①找到勾画内部的坐标点
②将圈画及内部置为1,外部置为0
③优化速度

找到内部坐标点最稳定的方法就是检测当前点的上下左右是否含有勾画点,缺点是速度比较慢,但可以通过一些优化大大加快其速度
请注意: 代码中图像存储行列大小与此处分辨率应做调换 详见代码及注释python JPG图片手动圈画掩膜Mask轮廓ROI_第2张图片

完整代码如下:

import numpy as np
import cv2 as cv
from matplotlib import pylab as plt

num = 5
now=0
im=[]
XY=[]
imgs='image'+str(now)
for i in range(num):
    XY.append([])


#检测左侧是否有勾画点
def left(ii,jj,l):
    for i in l:
        if(i[0]==ii and i[1]<jj):
            return True
    return False
#检测右侧是否有勾画点
def right(ii,jj,l):
    for i in l:
        if(i[0]==ii and i[1]>jj):
            return True
    return False
#检测上侧是否有勾画点
def up(ii,jj,l):
    for i in l:
        if(i[1]==jj and i[0]<ii):
            return True
    return False
#检测下侧是否有勾画点
def down(ii,jj,l):
    for i in l:
        if(i[1]==jj and i[0]>ii):
            return True
    return False 

def drawing(event, x, y, flags, param):
    global imgs
    global now
    global im
    if event == 0 and flags == 1: #鼠标移动 and 左键按下
        if([x,y] not in XY[now]):
            XY[now].append([x,y])
        if([x+1,y+1] not in XY[now]):
            XY[now].append([x+1,y+1])
        if([x-1,y-1] not in XY[now]):
            XY[now].append([x-1,y-1])
        if([x+1,y-1] not in XY[now]):
            XY[now].append([x+1,y-1])
        if([x-1,y+1] not in XY[now]):
            XY[now].append([x-1,y+1])
        if([x+1,y] not in XY[now]):
            XY[now].append([x+1,y])
        if([x-1,y] not in XY[now]):
            XY[now].append([x-1,y])
        if([x,y+1] not in XY[now]):
            XY[now].append([x,y+1])
        if([x,y-1] not in XY[now]):
            XY[now].append([x,y-1])
        cv.circle(im[now], (x, y), 1, (71,0, 255), -1)
        cv.imshow(imgs, im[now])
    elif event==10: #滚轮
        cv.destroyAllWindows()
        if(flags<0): #向下滚
            now+=1
            if(now>num-1):now=0
            imgs='image'+str(now)
            cv.namedWindow(imgs)
            cv.moveWindow(imgs,100,100)
            cv.setMouseCallback(imgs, drawing)  
            cv.imshow(imgs, im[now])
        else: #向下滚
            now-=1
            if(now<0):now=num-1
            imgs='image'+str(now)
            cv.namedWindow(imgs)
            cv.moveWindow(imgs,100,100)
            cv.setMouseCallback(imgs, drawing)  
            cv.imshow(imgs, im[now])
       
        

img = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00001.jpg')
img2 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00002.jpg')
img3 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00003.jpg')
img4 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00004.jpg')
img5 = cv.imread(r'C:\Users\Miao\Desktop\T1jpg\IMG-0003-00005.jpg')
im.append(img)
im.append(img2)
im.append(img3)
im.append(img4)
im.append(img5)
cv.namedWindow(imgs)
cv.moveWindow(imgs,100,100)
cv.setMouseCallback(imgs, drawing)
cv.imshow(imgs, im[now])
    
cv.waitKey(0)
cv.destroyAllWindows()

img_size = [260, 320, 3] #根据自己的JPG图像修改前两个数据大小 与分辨率调换行列


for l in XY: #依次处理每张图片的勾画
    if(l):#若本图有勾画信息
        img2 = np.zeros(img_size, np.uint16)#先生成同型的全0数据
        for m in l:           
            img2[m[1]][m[0]]=[1,1,1]#若想可视化,请修改为[255,255,255],但掩膜文件应全为1,方便后续处理
        iii=0
        jjj=0
        start=0 #当前行是否有勾画 
        for i in img2:
            jjj=0
            if(right(iii,jjj,l) or left(iii,jjj,l) or up(iii,jjj,l) or down(iii,jjj,l)):start=1 #若当前行有勾画则处理本行
            if(start):
                for j in i:
                    if(right(iii,jjj,l) and left(iii,jjj,l) and up(iii,jjj,l) and down(iii,jjj,l)):
                        img2[jjj][iii]=[1,1,1] #注:若想可视化请修改为[255,255,255],但制作掩膜需要使用[1,1,1]
                    jjj+=1
            start=0
            iii+=1
        #若需要可视化 请先将代码中[1,1,1]修改为[255,255,255]    
        #plt.figure(figsize=(26, 32))
        #ax = plt.subplot(3,1,1)
        #ax.imshow(img2)
        #ax.axis("off")

    

若将代码中[1,1,1]修改为[255,255,255]并取消最后四行的注释则可进行可视化
python JPG图片手动圈画掩膜Mask轮廓ROI_第3张图片

你可能感兴趣的:(python,opencv,计算机视觉)