理想是做全栈工程师 2023-11-22 13:27 采纳率: 37.5%
浏览 12

tkinter库调用摄像头画面一直闪烁

用python的tkinter库调用笔记本的摄像头,出现的画面一直闪烁白光。


# -*- coding: utf-8 -*-
"""
Created on Mon May 31 23:39:19 2021

@author: ZHOU
"""

# -*- coding: utf-8 -*-

import os  # 调用os多操作系统接口库
import threading  # 调用threading多线程运行库
import time  # 调用系统时间戳库
import tkinter as tk  # 调用窗口tk
import tkinter.messagebox
from tkinter import ttk, TOP

import cv2  # 调用OpenCV图像处理库
import matplotlib.pyplot as plt  # 调用matplotlib绘图库
from PIL import Image, ImageTk  # 调用图像处理库pillow

import api_face  # 调用本地函数库 用于登入外部机器学习库并调用人脸识别函数

plt.rcParams['font.sans-serif'] = ['SimHei']  # 载入字体


# print('请输入需录入的人脸图片路径/文件名:')
# pic_name=input()

# 利用matplotlib显示图片函数  
def pshow(words, picture):
    plt.imshow(picture[:, :, ::-1])  # 将读取的图片以RGB转换为BGR
    plt.title(words), plt.xticks([]), plt.yticks([])
    plt.show()  # 显示图片


class Login(ttk.Frame):  # 定义窗口大类
    def __init__(self, win):
        ttk.Frame.__init__(self, win)
        frame0 = ttk.Frame(self)
        frame1 = ttk.Frame(self)
        win.title("人脸识别")
        win.minsize(1240, 620)
        self.center_window()  # 执行中置窗口函数
        self.thread_run = None  # 赋值 线程1默认关闭
        self.thread_run2 = None  # 线程2默认关闭
        self.camera = None  # 摄像头默认关闭

        # 定义tk窗口属性

        # self.pilImage = Image.open("img/start.png")
        # self.tkImage = ImageTk.PhotoImage(image=self.pilImage)
        # self.image_ctl = tk.Label(frame0, image=self.tkImage)
        # self.image_ctl.pack()

        frame0.pack(side=TOP, fill=tk.Y, expand=1)
        frame1.pack(side=TOP, fill=tk.Y, expand=1)

        self.facer = ttk.Label(frame1, text='', font=('Times', '20'))  # 字体
        self.facer.pack()

        #        def filefound(): # 定义获取图片路径的函数
        #            filepath= askopenfilename() # 获取文件路径
        #            pic_name=filepath
        #            self.pic_path2 = pic_name # 赋值给图像2
        #
        #            pshow('所选人像图片',pic_img) # 显示所选图片
        self.pic_path3 = './star.png'
        #        pic_img=cv2.imread(self.pic_path3)
        #        pic_xz = pic_img.shape # 计算图像大小
        #        pic_h=pic_xz[0] # 得出图片高度
        #        pic_w=pic_xz[1] # 得出图片宽度
        #        turn_w=pic_w*500/pic_h # 限制最大高度为580 以防窗口过小不完全显示 等比例转换宽度
        #        turn_w=int(turn_w)
        #            print('人像图像大小(高 宽):',pic_h,pic_w)
        #            print ('路径:',filepath)
        #            # 在tk窗口中显示所选图片

        self.pilImage = Image.open(self.pic_path3)
        self.photo = self.pilImage.resize((500, 500))  # 限制最大高度为580 等比缩放显示
        self.tkImage = ImageTk.PhotoImage(image=self.photo)
        self.image_ctl = tk.Label(frame0, image=self.tkImage)
        self.image_ctl.pack()
        #            #e.delete(0, END)  # 将输入框里面的内容清空
        #            #e.insert(0, filepath)
        #
        #        #button2=Button(frame1,text="button2",command=filefound).grid(row=0,column=3)
        #        # 按钮1 调用filefound函数 获取选择图片的路径 并赋值给self.pic_path2 输出图像
        #        self.face_button1 = ttk.Button(frame1, text="1. 选择人像图片", width=15, command=filefound)
        #        self.face_button1.pack(side=TOP)
        # 按钮2 调用摄像头函数
        self.url_face_button = ttk.Button(frame1, text="使用相机识别", width=15, command=self.cv_face)
        self.url_face_button.pack(side=TOP)
        # self.file_pic_button = ttk.Button(frame1, text="本地文件识别", width=15, command=self.file_pic)
        # self.file_pic_button.pack(side=TOP)

        self.pack(fill=tk.BOTH, expand=tk.YES, padx="10", pady="10")

    # 使弹出的窗体处于屏幕的中间位置
    def center_window(self):
        screenwidth = log.winfo_screenwidth()  # 获取屏幕分辨率宽
        screenheight = log.winfo_screenheight()  # 获取屏幕分辨率高
        log.update()  # 更新窗口
        width = log.winfo_width()  # 重新赋值
        height = log.winfo_height()
        size = '+%d+%d' % ((screenwidth - width) / 2, (screenheight - height) / 2)
        # 重新赋值大小 大小为屏幕大小/2
        log.geometry(size)  # 以新大小定义窗口

    #    def file1(self):
    #        self.pic_path = askopenfilename(title="选择识别图片", filetypes=[("jpg图片", "*.jpg"), ("png图片", "*.png")])

    def cv_face(self):  # 调用摄像头函数
        if self.thread_run:
            if self.camera.isOpened():  # 如果已经打开则关闭
                self.camera.release()
                print("关闭摄像头")
                self.camera = None
                self.thread_run = False
            return
        if self.camera is None:  # 如果没有摄像头则尝试打开
            self.camera = cv2.VideoCapture(0)  # 利用OpenCV调用外摄像头
            if not self.camera.isOpened():  # 如果没有打开 则调用内摄像头
                self.camera = None
                print("没有外置摄像头")
                self.camera = cv2.VideoCapture(1)  # 用OpenCV调用内摄像头
                if not self.camera.isOpened():  # 如果没有打开 则打开失败
                    print("没有内置摄像头")
                    tkinter.messagebox.showinfo('警告', '摄像头打开失败!')
                    self.camera = None
                    return
                else:
                    print("打开内置摄像头")
            else:
                print("打开外置摄像头")
        self.thread = threading.Thread(target=self.video_thread)  # 多线程函数执行摄像头运行函数
        self.thread.daemon = True
        self.thread.start()
        self.thread_run = True

    def video_thread(self):  # 开始摄像头运行
        self.thread_run = True  # 多线程1开启
        self.thread2 = threading.Thread(target=self.video_pic)
        self.thread2.daemon = True
        self.thread2.start()
        self.thread_run2 = True
        while self.thread_run:  # 循环一直调用摄像头
            _, img_bgr = self.camera.read()  # 以bgr格式读取摄像头内的截图
            gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)  # 灰度转换
            # 在CV官方机器学习库内加载人脸识别分类器                
            # Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
            classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
            color = (0, 255, 0)  # 绿色线
            # 识别器进行识别
            faceRects = classifier.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
            # 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
            # print(faceRects)  #[[113  42  60  60]]    前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的

            # 判断识别结果集合长度
            if len(faceRects):
                for faceRect in faceRects:
                    x, y, w, h = faceRect
                    # 用矩形框选出人脸   最后一个参数2是框线宽度
                    cv2.rectangle(img_bgr, (x, y), (x + h, y + w), color, 2)
            img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)  # 颜色转换
            im = Image.fromarray(img)
            w, h = im.size
            pil_image_resized = self.resize(w, h, im)  # 调整大小函数
            self.imgtk = ImageTk.PhotoImage(image=pil_image_resized)
            self.image_ctl.configure(image=self.imgtk)
            abc = None
            abc = img  # 解决摄像头图像闪烁的问题..
        print("结束运行")

    def video_pic(self):  # 视频截图保存及框选函数
        dir_name = './img'  # 给出图片目录路径
        fileimg_list = []  # 建立列表型图片列表
        fileimg_list = os.listdir(dir_name)  # 获取目录下文件名
        file_num = len(fileimg_list)  # 获取列表长度
        fileimg_num = 0  # 定义文件序号初始值为0
        self.thread_run2 = True  # 开启多线程
        predict_time = time.time()  # 获得系统时间
        while self.thread_run2:  # 循环读取
            if time.time() - predict_time > 0.1:  # 每0.1s读取一次摄像头截图
                print("正在识别中")
                _, img_bgr = self.camera.read()  # 重新读取摄像头图像
                cv2.imwrite("tmp/test.jpg", img_bgr)  # 利用cv写入到tmp/test.jpg路径下
                #                test_pic=cv2.imread('tmp/test.jpg') # 重新读取截图
                #                pshow('识别截图',test_pic) # 显示截图
                #
                #                # 图像路径 我用的相对路径
                #                face_mark = 'tmp/test.jpg'
                #                # 读取截图
                #                faceImg = cv2.imread(face_mark)
                #                # 转换灰色
                #                gray = cv2.cvtColor(faceImg,cv2.COLOR_RGB2GRAY) # 由于tmp/test.jpg路径下的已经转换成RGB保存 所以不用再进行BGR转换
                #
                #                # 在CV官方机器学习库内加载人脸识别分类器
                #                # Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
                #                classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
                #                color = (0,255,0) # 绿色线
                #
                #                # 识别器进行识别
                #                faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))
                #                # 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
                #                # print(faceRects)  #[[113  42  60  60]]    前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的
                #
                #                # 判断识别结果集合长度
                #                if len(faceRects):
                #                    for faceRect in faceRects:
                #                        x,y,w,h = faceRect
                #                        # 用矩形框选出人脸   最后一个参数2是框线宽度
                #                        cv2.rectangle(faceImg,(x, y), (x + h, y + w), color, 2)
                #
                #
                #                pshow('识别结果',faceImg)    # 输出框选结果图像并显示
                #                cv2.imwrite("tmp/test2.jpg", faceImg)
                self.pic_path = "tmp/test.jpg"  # 重新读取摄像头截图
                #                self.pilImage = Image.open(self.pic_path)
                #                self.tkImage = ImageTk.PhotoImage(image=self.pilImage)
                #                self.image_ctl = tk.Label(frame0, image=self.tkImage)
                #                self.image_ctl.pack()
                try:

                    if fileimg_num < file_num:  # 当文件序号小于总列表长度时
                        # print(fileimg_num)
                        # img=cv2.imread(dir_name + '/' + fileimg_list[fileimg_num])
                        # pshow(fileimg_list[fileimg_num],img)
                        self.pic_path2 = dir_name + '/' + fileimg_list[fileimg_num]  # 给出pic_path2的值为目录名+/+文件名
                        # print(self.pic_path2)
                        #                        self.pic_path2='./img/111.jpg'
                        facestr, result = api_face.facef(self.pic_path, self.pic_path2)  # 调用api_face库的人脸识别函数
                        self.facer.configure(text=str(facestr))
                        # self.pic() # 对摄像头图像进行尺度变换
                        if result > 80:  # 识别结果大于80
                            humantitle = '  姓名:  ' + fileimg_list[fileimg_num].partition('.')[0] + "  人脸匹配成功!  \n"
                            print(humantitle)  # 输出人像文件文件名
                            tkinter.messagebox.showinfo('提示', humantitle)  # tk窗口提示

                            try:
                                f = open("识别记录.txt", "r")
                                fi = open("识别记录.txt", "a")
                                txt = time.ctime()
                                fi.write(txt + humantitle)
                                f.close()
                                fi.close()  # 将识别成功的记录保存在txt文件下
                            except:
                                f = open("识别记录.txt", "w")
                                txt = time.ctime()
                                f.write(txt + humantitle)
                                f.close()

                                # close_window()
                                # os.system("python3 ./main.py")
                                # if result < 20:
                                #    tkinter.messagebox.showinfo('提示', '未检测到人脸!')
                            break
                        else:  # 小于80失败
                            fileimg_num = fileimg_num + 1  # 文件序号+1


                    else:  # 超出文件列表长度
                        tkinter.messagebox.showinfo('提示', '查无此人!人脸匹配失败!')
                        break
                except:
                    pass
                predict_time = time.time()  # 读取时间
                print("识别结束")

                # 看门狗程序(调试用)
                # 防止程序关闭时进入死循环跑飞
                # print('请输入任意值以继续,否则请关闭窗口以终止程序:')
                # a=input()
                # print(a)
        pass

    #    def file_pic(self): #识别函数
    #        dir_name='./img'
    #        fileimg_list = []
    #        fileimg_list=os.listdir(dir_name)
    #        file_num=len(fileimg_list)
    #        fileimg_num=0
    #        while True:
    #            if fileimg_num<file_num:
    #                print(fileimg_num)
    #                img=cv2.imread(dir_name + '/' + fileimg_list[fileimg_num])
    #                pshow(fileimg_list[fileimg_num],img)
    #                self.pic_path2 = dir_name + '/' + fileimg_list[fileimg_num]
    #                print(self.pic_path2)
    ##                self.pic_path2='./img/111.jpg'
    #                facestr, result = api_face.facef(self.pic_path, self.pic_path2) # 调用api_face库的人脸识别函数
    #                self.facer.configure(text=str(facestr))
    #                        #self.pic() # 对摄像头图像进行尺度变换
    #                if result > 80: #识别结果大于80
    #                    tkinter.messagebox.showinfo('提示', '人脸匹配成功!') # tk窗口提示
    #                    print('人像图片路径:'+self.pic_path2) # 输出人像文件路径
    #                    try:
    #                        f=open("识别记录.txt","r")
    #                        fi=open("识别记录.txt","a")
    #                        txt=time.ctime()
    #                        fi.write(txt+'   人像图片路径:   '+self.pic_path2+"     人脸匹配成功! \n")
    #                        f.close()
    #                        fi.close() # 将识别成功的记录保存在txt文件下
    #                    except:
    #                        f=open("识别记录.txt","w")
    #                        txt=time.ctime()
    #                        f.write(txt+'   人像图片路径:   '+self.pic_path2+"     人脸匹配成功! \n")
    #                        f.close()
    #
    #
    #
    #                            # close_window()
    #                            # os.system("python3 ./main.py")
    #                        #if result < 20:
    #                        #    tkinter.messagebox.showinfo('提示', '未检测到人脸!')
    #                        break
    #                    else: # 小于80失败
    #                        fileimg_num=fileimg_num+1
    #
    #
    #            else:
    #                tkinter.messagebox.showinfo('提示', '人脸匹配失败!')
    #                break

    #    def pic(self): # 对摄像头图像进行尺度变换
    #        self.pilImage3 = Image.open(self.pic_path) # 用pillow读取摄像头图像
    #        w, h = self.pilImage3.size # 计算大小赋值给宽 高
    #        pil_image_resized = self.resize(w, h, self.pilImage3) # 调整大小函数
    #        self.tkImage3 = ImageTk.PhotoImage(image=pil_image_resized)
    #        self.image_ctl.configure(image=self.tkImage3) # 输出结果

    def resize(self, w, h, pil_image):  # 调整大小函数
        w_box = 1000  # 定义最大宽度
        h_box = 500  # 最大高度
        f1 = 1.0 * w_box / w  # 最大值/真实值
        f2 = 1.0 * h_box / h
        factor = min([f1, f2])  # 取最小值
        width = int(w * factor)  # 用最小值*对应值 调整到最大定义值 等比调整另一个值
        height = int(h * factor)
        return pil_image.resize((width, height), Image.LANCZOS)  # 输出调整


def close_window():
    print("已关闭人脸识别")
    if Login.thread_run:
        Login.thread_run = False
        Login.thread.join(2.0)
    log.destroy()


if __name__ == '__main__':
    log = tk.Tk()

    login = Login(log)
    # close,退出输出destroy
    log.protocol('清除窗口', close_window)
    # 进入消息循环
    log.mainloop()

  • 写回答

1条回答 默认 最新

  • CSDN-Ada助手 CSDN-AI 官方账号 2023-11-22 16:15
    关注

    【相关推荐】



    • 这个问题的回答你可以参考下: https://ask.csdn.net/questions/7529387
    • 我还给你找了一篇非常好的博客,你可以看看是否有帮助,链接:python -- tkinter 滑块
    • 您还可以看一下 周华健老师的Python Tkinter 绘图项目课程中的 画笔颜色小节, 巩固相关知识点
    • 除此之外, 这篇博客: 简单python界面设计(tkinter库)中的 简单python界面设计(tkinter库) 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:

      做这个界面初衷是为舵机做一个控制界面,这个文件被遗弃在角落里,今天刚发现,所以就简单介绍
      引用

      https://blog.csdn.net/superfanstoprogram/article/details/83713196
      主界面以及我对tkinter库的学习是参考的上面这个链接的(感谢🙈🙉),并且我在其他界面做了一些改动

      下面为部分代码,全代码以及用到的资源文件见下面我的 GitHub 链接

      """
          标题:XXX界面
          时间:2020.02.15
          作者:Sunqk
      """
      import tkinter as tk
      from PIL import Image, ImageTk
      from tkinter import messagebox
      import tkinter.font as tkFont
      
      def tkimg_resized(img, w_box, h_box, keep_ratio=True):
      	"""对图片进行按比例缩放处理"""
      	w, h = img.size
      
      	if keep_ratio:
      		if w > h:
      			width = w_box
      			height = int(h_box * (1.0 * h / w))
      
      		if h >= w:
      			height = h_box
      			width = int(w_box * (1.0 * w / h))
      	else:
      		width = w_box
      		height = h_box
      
      	img1 = img.resize((width, height), Image.ANTIALIAS)
      	tkimg = ImageTk.PhotoImage(img1)
      	return tkimg
      def image_label(frame, img, width, height, keep_ratio=True):
      	"""输入图片信息,及尺寸,返回界面组件"""
      	if isinstance(img, str):
      		_img = Image.open(img)
      	else:
      		_img = img
      	lbl_image = tk.Label(frame, width=width, height=height)
      
      	tk_img = tkimg_resized(_img, width, height, keep_ratio)
      	lbl_image.image = tk_img
      	lbl_image.config(image=tk_img)
      	return lbl_image
      
      
      def show_confirm(message=""):
      	"""
      		True  : yes
      		False : no
      	"""
      	return messagebox.askyesno("确认框", message)
      
      
      
      a=None
      def main():
          window = tk.Tk()                        # 创建窗口
          window.geometry("700x400")              # 窗体尺寸设置
          window.iconbitmap("Money.ico")          # 窗体左上角图标设置
          window.title("XXXXX控制界面")
          window.resizable(True, True)            # 设置窗体不可改变大小
      
          #背景图选择并放置
          img=ImageTk.PhotoImage(file="bg1.png")
          canvas = tk.Canvas(window, width=1000, height=800)
          canvas.create_image(300, 200, image=img)
          canvas.pack(expand=tk.YES, fill=tk.BOTH)
      
          def show_title(self):                     #窗口边框消失、复原函数
              global a
              a=not a
              window.overrideredirect(a)
          def close(self):                          #窗口退出函数
              if show_confirm("确认退出吗 ?"):
                  window.quit()
      
      
          #**************************标题设置开始处*****************************
          f1 = tk.Frame(canvas)
          #图像按钮 1
          im1 = image_label(f1, "laugh.jpg", 86, 86, False)
          im1.configure(bg="Teal")
          im1.bind('<Button-1>',show_title)
          im1.pack(side=tk.LEFT, anchor=tk.NW, fill=tk.Y)
          #标题背景、字体......
          ft1 = tkFont.Font(family="微软雅黑", size=24, weight=tkFont.BOLD)
          tk.Label(f1, text="欢迎来到XXX控制界面", height=2, fg="white", font=ft1, bg="Teal") \
              .pack(side=tk.LEFT, expand=tk.YES, fill=tk.X)
          #图像按钮 2
          im2 = image_label(f1, "close.png", 86, 86, False)
          im2.configure(bg="Teal")
          im2.bind('<Button-1>',close)
          im2.pack(side=tk.RIGHT, anchor=tk.NW, fill=tk.Y)
      
          f1.pack(fill=tk.X)
          #**************************标题设置结束*****************************
      
      # ---------------------------------------------------------------------
      # 两个功能按钮组的设置
      # ---------------------------------------------------------------------
      
          # ‘手动抓取’和‘自动抓取’功能按钮所调用的函数
          def shou_dong():
              Wd_1()
          def zi_dong():
              Wd_2()
          # 子窗口一
          def Wd_1():
              Wd=tk.Toplevel()
              Wd.geometry("%dx%d" % (800,620))        # 窗体尺寸
              Wd.title("手动抓取界面")                 # 窗体标题
              Wd.grab_set()
              Wd.resizable(True, True)
      
              frame = tk.Frame(Wd, height=20,bg="Goldenrod")
              ft0 = tkFont.Font(family="微软雅黑", size=18, weight=tkFont.BOLD)
              tk.Label(frame, font=ft0, bg="Chocolate", fg="white", text="请手动调控机械臂的各个舵机参数") \
                  .pack(padx=20)
              frame.pack(fill=tk.X)
      
              ft1 = tkFont.Font(family="微软雅黑", size=12, weight=tkFont.BOLD)
              ft2 = tkFont.Font(family="微软雅黑", size=14, weight=tkFont.BOLD)
      
              #右边绿框(画布形式)
              canvas = tk.Canvas(frame, bg='green', height=238, width=450)
              # 定义多边形参数,然后在画布上画出指定图形
              x0, y0, x1, y1 = 100, 100, 150, 150
              line = canvas.create_line(x0 - 50, y0 - 50, x1 - 50, y1 - 50)  # 画直线
              oval = canvas.create_oval(x0 + 120, y0 + 50, x1 + 120, y1 + 50, fill='yellow')  # 画圆 用黄色填充
              arc = canvas.create_arc(50, 150, 100, 200, start=0, extent=180,fill='red')  # 画扇形 从0度打开收到180度结束
              rect = canvas.create_rectangle(330, 30, 330 + 20, 30 + 20,fill='blue')  # 画矩形正方形
              triangle = canvas.create_polygon(208,135,222,43,268,95,fill='DeepPink')
      
              canvas.pack(side=tk.RIGHT,anchor=tk.N)
      
      
              #舵机一框区
              v_1 = tk.StringVar()
              v_2 = tk.StringVar()
              v_3 = tk.StringVar()
              v_4 = tk.StringVar()
              v_5 = tk.StringVar()
              v_6 = tk.StringVar()
      
      		#中间部分代码见GitHub----->!!!!!!!!
      		#中间部分代码见GitHub----->!!!!!!!!
      		#中间部分代码见GitHub----->!!!!!!!!
      		#中间部分代码见GitHub----->!!!!!!!!
      		#中间部分代码见GitHub----->!!!!!!!!
      		#中间部分代码见GitHub----->!!!!!!!!
      
      if __name__ == '__main__':
          main()
      

      在这里插入图片描述
      在这里插入图片描述
      图片有点炫目奥😵
      在这里插入图片描述
      参考文章https://blog.csdn.net/superfanstoprogram/article/details/83713196
      GitHub源码https://github.com/Sunqk5665/Python_projects/tree/master/%E8%88%B5%E6%9C%BA%E6%8E%A7%E5%88%B6%E7%95%8C%E9%9D%A2


    如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^
    评论

报告相同问题?

问题事件

  • 创建了问题 11月22日