# 使用相機拍照並在上面使用鼠標畫圖輸出一張雷射切個機能夠使用的檔案
**1. 第一個部分為滑鼠事件、影像處理(mask)以及照相的部分**
* 用滑鼠再圖片上畫圖後儲存
http://opencv123.blogspot.com/2015/08/blog-post.html
https://blog.csdn.net/sinat_41104353/article/details/85171185
https://steam.oxxostudio.tw/category/python/ai/opencv-video-draw.html
https://steam.oxxostudio.tw/category/python/example/image-exif.html
* 將圖片上影像遮罩轉換成特定顏色的影像
https://steam.oxxostudio.tw/category/python/ai/opencv-inrange.html
功能說明:在圖片上按下滑鼠左鍵並拖曳畫出線條,畫出線條的顏色為全黑,圖片其他部分為全白,輸出成一張為雷射切割軟體可以使用的檔案需求。
```python=
import cv2
import numpy as np
dots = [] # 建立空串列記錄座標
w = 420
h = 240
draw = cv2.imread('image.jpg') # 建立 420x240 的 RGBA 黑色畫布
#mask
lower_red = np.array([0,43,46])
upper_red = np.array([1,255,255])
#mask
def show_xy(event,x,y,flags,param):
global dots, draw # 定義全域變數
if flags == 1:
if event == 1:
dots.append([x,y]) # 如果拖曳滑鼠剛開始,記錄第一點座標
if event == 4:
dots = [] # 如果放開滑鼠,清空串列內容
if event == 0 or event == 4:
dots.append([x,y]) # 拖曳滑鼠時,不斷記錄座標
x1 = dots[len(dots)-2][0] # 取得倒數第二個點的 x 座標
y1 = dots[len(dots)-2][1] # 取得倒數第二個點的 y 座標
x2 = dots[len(dots)-1][0] # 取得倒數第一個點的 x 座標
y2 = dots[len(dots)-1][1] # 取得倒數第一個點的 y 座標
cv2.line(draw,(x1,y1),(x2,y2),(0,0,255,255),2) # 畫直線
cv2.imshow('image', draw)
cv2.imshow('image', draw)
cv2.setMouseCallback('image', show_xy)
while True:
keyboard = cv2.waitKey(5) # 每 5 毫秒偵測一次鍵盤事件
if keyboard == ord('q'):
cv2.destroyAllWindows()
break # 如果按下 q 就跳出
if keyboard == ord('r'):
draw = cv2.imread('image.jpg') # 如果按下 r 就變成原本畫面的畫布
cv2.imshow('image', draw)
if keyboard == ord('s'): #儲存影像
cv2.imwrite('output02.jpg', draw, [cv2.IMWRITE_JPEG_QUALITY, 100])
cv2.imshow('output02.jpg', draw)
cv2.waitKey(0)
cv2.destroyAllWindows()
break
if keyboard == ord('m'):
#mask取紅色特徵
hsv = cv2.cvtColor(draw, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_red, upper_red) # 使用 inRange
output = cv2.bitwise_and(hsv, hsv, mask = mask ) # 套用影像遮罩
#BGR影象的反轉
dst=~mask
#存檔
cv2.imwrite('output02.jpg', dst, [cv2.IMWRITE_JPEG_QUALITY, 100])
cv2.imshow('output02.jpg', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
break
cv2.destroyAllWindows()
```
**2. 第二部分為GUI的部分,在介面上使用open、close、quit、mask按鈕對照相機畫面做處理**
* python-用tkinter顯示影像&關閉
https://programming727.pixnet.net/blog/post/20483870-python-%E7%94%A8tkinter%E9%A1%AF%E7%A4%BA%E5%BD%B1%E5%83%8F%26%E9%97%9C%E9%96%89
功能說明:使用tkinter作出基礎的介面,定義出check()、open()、close()、quit()、mask()讓按鈕成功實現功能。
```python=
#-*- coding: UTF-8 -*-
import tkinter as tk #大小寫要注意,如果小寫不行就改大寫
import time
from PIL import ImageTk, Image, ImageDraw
import cv2
import random
import numpy as np
captrue = cv2.VideoCapture(1) #開啟相機,0為預設筆電內建相機
captrue.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) #設置影像參數
captrue.set(3,500) #像素
captrue.set(4,500) #像素
img_viode = 'D:\pythoncode\coding\python\Python GUI Tkinter\image.jpg' #影像存放位置 要改
#mask
draw = cv2.imread('D:\pythoncode\coding\python\Python GUI Tkinter\image.jpg') # 建立 420x240 的 RGBA 黑色畫布
lower_red = np.array([0,43,46])
upper_red = np.array([1,255,255])
dots = [] # 建立空串列記錄座標
def check():
global captrue
if captrue.isOpened(): #判斷相機是否有開啟
open()
else:
captrue = cv2.VideoCapture(1)
captrue.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) #設置影像參數
captrue.set(3,500) #像素
captrue.set(4,500) #像素
open()
#mask
def show_xy(event,x,y,flags,param):
global dots, draw # 定義全域變數
if flags == 1:
if event == 1:
dots.append([x,y]) # 如果拖曳滑鼠剛開始,記錄第一點座標
if event == 4:
dots = [] # 如果放開滑鼠,清空串列內容
if event == 0 or event == 4:
dots.append([x,y]) # 拖曳滑鼠時,不斷記錄座標
x1 = dots[len(dots)-2][0] # 取得倒數第二個點的 x 座標
y1 = dots[len(dots)-2][1] # 取得倒數第二個點的 y 座標
x2 = dots[len(dots)-1][0] # 取得倒數第一個點的 x 座標
y2 = dots[len(dots)-1][1] # 取得倒數第一個點的 y 座標
cv2.line(draw,(x1,y1),(x2,y2),(0,0,255,255),2) # 畫直線
cv2.imshow('image', draw)
def open():
global s
ret,frame = captrue.read() #取得相機畫面
cv2.imwrite(img_viode,frame) #儲存圖片
img_right = ImageTk.PhotoImage(Image.open(img_viode)) #讀取圖片
label_right.imgtk=img_right #換圖片
label_right.config(image=img_right) #換圖片
s = label_right.after(1, open) #持續執行open方法,1000為1秒
def close():
label_right.after_cancel(s) #結束拍照
label_right.config(image=img) #換圖片
#離開程式
def quit():
top.destroy()
captrue.release()
def mask():
global dots, draw
cv2.imshow('image', draw)
cv2.setMouseCallback('image', show_xy)
while True:
keyboard = cv2.waitKey(5) # 每 5 毫秒偵測一次鍵盤事件
if keyboard == ord('q'):
cv2.destroyAllWindows()
break # 如果按下 q 就跳出
if keyboard == ord('r'):
draw = cv2.imread('D:\pythoncode\coding\python\Python GUI Tkinter\image.jpg') # 如果按下 r 就變成原本畫面的畫布
cv2.imshow('image', draw)
if keyboard == ord('s'): #儲存影像
cv2.imwrite('output02.jpg', draw, [cv2.IMWRITE_JPEG_QUALITY, 100])
cv2.imshow('output02.jpg', draw)
cv2.waitKey(0)
cv2.destroyAllWindows()
break
if keyboard == ord('m'):
#mask取紅色特徵
hsv = cv2.cvtColor(draw, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_red, upper_red) # 使用 inRange
output = cv2.bitwise_and(hsv, hsv, mask = mask ) # 套用影像遮罩
#BGR影象的反轉
dst=~mask
#存檔
cv2.imwrite('output02.jpg', dst, [cv2.IMWRITE_JPEG_QUALITY, 100])
cv2.imshow('output02.jpg', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
break
cv2.destroyAllWindows()
#創建一個視窗
top = tk.Tk()
#視窗名稱
top.title('GUI')
#寬:300高:200的視窗,放在寬:600高:300的位置
top.geometry('1200x800')
#開啟照片
img= ImageTk.PhotoImage(Image.open('D:\pythoncode\coding\python\Python GUI Tkinter\cameraoff.png')) #要改
#用label來放照片
label_right= tk.Label(top,height=480,width=480,bg ='gray94',fg='blue',image = img)
#按鈕
button_1 = tk.Button(top,text = 'Open',bd=4,height=4,width=22,bg ='gray94',command =check)
button_2 = tk.Button(top,text = 'Close',bd=4,height=4,width=22,bg ='gray94',command =close)
button_3 = tk.Button(top, text = 'Quit',bd=4,height=4,width=22,bg ='gray94', command=quit)
button_4 = tk.Button(top, text = 'Mask',bd=4,height=4,width=22,bg ='gray94', command=mask)
#位置
label_right.grid(row=1,column=0,padx=20, pady=20, sticky="nw")
button_1.grid(row=1, column=0, padx=80, pady=550, sticky="nw")
button_2.grid(row=1, column=0, padx=280, pady=550, sticky="nw")
button_3.grid(row=1, column=0, padx=480, pady=550, sticky="nw")
button_4.grid(row=1, column=0, padx=80, pady=650, sticky="nw")
top.mainloop() #執行視窗
```