如何在Python中删除图像上的绘制线条?

5
我使用Python(opencv包)在现有图像上使用鼠标点击绘制了几条线(每个鼠标点击都是一个连接的点),您可以将其视为允许用户在图像上选择某些内容。
我该如何允许用户通过右键单击来删除图像上的最后一个点?这是我的当前代码:
import numpy
import cv2

points = []

def draw_point(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        cv2.circle(image, (x,y), 1, (255,0,0),-1)
        points.append((x,y))
        pts = numpy.array(points, numpy.int32)
        cv2.polylines(image,[pts],False,(255,0,0))
    elif event == cv2.EVENT_RBUTTONDOWN:
        # HOW TO DELETE?
        del points[-1]
        pts = numpy.array(points, numpy.int32)
        cv2.polylines(image,[pts],True,(255,0,0))



image = cv2.imread('simple_tattoo.jpg', cv2.IMREAD_UNCHANGED)

cv2.namedWindow('example', cv2.WINDOW_AUTOSIZE)
cv2.setMouseCallback('example', draw_point)


while(1):
   cv2.imshow('example',image)

   if cv2.waitKey(20) & 0xFF == 27:
        break

cv2.destroyAllWindows()

print (points)

在图像上绘制线条或其他内容是否有更简单的方法?


9
一旦在图片上画线或其他东西,就无法恢复原始图像。您可能需要有两个图像:1. 原始图像。2. 在原始图像的副本上绘制线条的图像。 - Micka
哇,谢谢你,我完全没有想到那个。它帮助我完成了工作! - danchy
4个回答

4

在每个操作之后保留图像的缓存,需要时可以简单地恢复到缓存状态。 就像撤消一样。

def draw_circle(event,x,y,flags,param):
    global img
    global cache
    if event == cv2.EVENT_LBUTTONDBLCLK:
        cache = copy.deepcopy(img)
        cv2.circle(img,(x,y),40,(255,0,0),5)

    if event == cv2.EVENT_MBUTTONDBLCLK:
        img = copy.deepcopy(cache)
        cv2.imshow('image', img)

img_path = "my_img.jpg"        
global img  
global cache      
img = cv2.imread(img_path)  
cache = copy.deepcopy(img)
cv2.namedWindow('image')  

while(1):
    cv2.setMouseCallback('image', draw_circle)
    cv2.imshow('image', img)
    k=cv2.waitKey(1) & 0xFF
    if k==27: #Escape KEY
        break

    cv2.imshow('image', img)

cv2.destroyAllWindows()

我认为你应该解释一下copy和deepcopy是什么……我不觉得需要导入额外的库——img.copy()和cache.copy()很好用。干得好! - Frank Musterman

1
你可以尝试使用缓存来保存图像的原始状态并重新绘制所有线条(除了最新删除的线条),以此来删除之前的线条、重置所有内容或退出。这个技巧可以让你撤销1次状态、2次状态……或者重置所有内容。
import cv2

image = cv2.imread('wallpaper.jpg')
clone = image.copy()
refPt = []
count = 0

# left click to draw the next line, right click to delete the previous line
def draw_line(event, x, y, flags, param):
    global refPt, count, image
    if event == cv2.EVENT_LBUTTONDOWN:        
        refPt.append((x, y))
        cv2.line(image, refPt[count-1], refPt[count], (0, 255, 0), 1)
        count = count + 1

    elif event == cv2.EVENT_RBUTTONDOWN:
        image = clone.copy()    
        refPt.remove(refPt[count-1])
        count = count - 1
        for i in range (1, count):
            cv2.line(image, refPt[i-1], refPt[i], (0,255,0), 1)

cv2.namedWindow("image")
cv2.setMouseCallback("image", draw_line)

while True:
    # display the image and wait for a keypress
    cv2.imshow("image", image)
    key = cv2.waitKey(1) & 0xFF
    # if the 'reset' key is pressed, reset to original state
    if key == ord("r"):
        image = clone.copy()    
    # if the 'exit' key is pressed, break from the loop
    elif key == ord("e"):
        break

cv2.destroyAllWindows() 

0
我使用了以下代码在for循环中刷新/“删除”行:
orig_img= cv2.imread('img.png')

for i in range(90):
    #copy the original
    img = orig_img.copy()

    #draw lines
    cv2.line(img, pt1, pt2, color)
    cv2.line(img, pt2, pt3, color)
    cv2.line(img, pt3, pt4, color)

    cv2.imshow('image', img)
    cv2.waitKey(100) #waits 100 miliseconds before next loop

-1

所以如果有人遇到这个问题,重要的是还要保持一个 while 循环来刷新 img - 没有它就无法工作

def click_event(event, x, y, flags, param):
    global img 
    if event == cv2.EVENT_LBUTTONDOWN:
        print(x,y)
        cv2.circle(img, (x, y), 10, (0, 0, 255), -1)
        cv2.imshow('image', img)
    if event == cv2.EVENT_RBUTTONDBLCLK:
        img = cv2.imread(img_path)
        print("cleaned")
        cv2.imshow('image', img)

img_path = "my_img.jpg"        
global img        
img = cv2.imread(img_path)        

while(1):
    cv2.setMouseCallback('image', click_event)
    cv2.imshow('image', img)
    k=cv2.waitKey(1) & 0xFF
    if k==27: #Escape KEY
        break

    cv2.imshow('image', img)

cv2.destroyAllWindows()    

OP 不想删除完整的图像,只是这个点。 - Pradeep Singh
@PradeepSingh 很抱歉听到它对你不起作用。我现在刚测试了一下,它对我来说是有效的。(你有注意到它是双击R按钮吗?而且它并没有擦除整个图像,而是上面的所有点。) 你能更具体地描述你的问题,这样我就可以尝试帮助你了吗? - Frank Musterman
以下是它将会抛出的错误信息... OpenCV(4.2.0) /io/opencv/modules/highgui/src/window_QT.cpp:714: 错误:(-27:Null pointer) 窗口句柄为空在函数 'cvSetMouseCallback' 中.... 这是因为您在调用 cv2.setMouseCallback(...) 方法之前没有指定一个名为 'image' 的 cv2.namedWindow('image')。 - Pradeep Singh
在cv2.setMouseCallback('image', click_event)这行代码之前,我指定了cv2.namedWindow('image'),并且没有出现错误。但是还存在逻辑错误,正如你所说的那样。它会清除所有点,这不是我和提问者想要的。尽管如此,我已经编写了自己的脚本来解决所有这些问题。 谢谢... - Pradeep Singh
我猜在任何情况下提前命名窗口都是个好习惯,但对我来说,即使没有这个也可以工作。这可能与操作系统和CV版本有关。很高兴听到你成功地移除了所有的点 - 也许你想与我们分享你的脚本,作为对原问题更好的回答 :) - Frank Musterman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接