对视频录制进行优化,无人机界面优化

pull/30/head
lvs 3 years ago
parent 1783157e5e
commit 61202d6cce

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,27 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="7">
<item index="0" class="java.lang.String" itemvalue="numba" />
<item index="1" class="java.lang.String" itemvalue="pynput" />
<item index="2" class="java.lang.String" itemvalue="py7zr" />
<item index="3" class="java.lang.String" itemvalue="opencv-python" />
<item index="4" class="java.lang.String" itemvalue="ffmpeg-python" />
<item index="5" class="java.lang.String" itemvalue="numpy" />
<item index="6" class="java.lang.String" itemvalue="Pillow" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyStubPackagesAdvertiser" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<list>
<option value="PyQt5-stubs==5.15.6.0" />
</list>
</option>
</inspection_tool>
</profile>
</component>

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (pythonproject)" project-jdk-type="Python SDK" />
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/src.iml" filepath="$PROJECT_DIR$/.idea/src.iml" />
</modules>
</component>
</project>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.9 (pythonproject)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

@ -1,133 +0,0 @@
import time
import wave
from pathlib import Path
from threading import Thread
from pyaudio import PyAudio, paInt16, paContinue, paComplete
class AudioRecord(PyAudio):
def __init__(self, channels=2):
super().__init__()
self.chunk = 1024 # 每个缓冲区的帧数
self.format_sample = paInt16 # 采样位数
self.channels = channels # 声道: 1单声道2双声道
self.fps = 44100 # 采样频率
self.input_dict = None
self.output_dict = None
self.stream = None
self.filename = '~test.wav'
self.wf = None
self.stop_flag = False
self.kill = False
def callback_input(self, in_data, frame_count, time_info, status):
"""录制回调函数"""
self.wf.writeframes(in_data)
if not self.stop_flag:
return (in_data, paContinue)
else:
return (in_data, paComplete)
def callback_output(self, in_data, frame_count, time_info, status):
"""播放回调函数"""
data = self.wf.readframes(frame_count)
return (data, paContinue)
def open_stream(self, name):
"""打开录制流"""
input_device_index = self.get_device_index(name, True) if name else None
return self.open(format=self.format_sample,
channels=self.channels,
rate=self.fps,
frames_per_buffer=self.chunk,
input=True,
input_device_index=input_device_index,
stream_callback=self.callback_input
)
def audio_record_run(self, name=None):
"""音频录制"""
self.wf = self.save_audio_file(self.filename)
self.stream = self.open_stream(name)
self.stream.start_stream()
while self.stream.is_active():
time.sleep(0.1)
self.wf.close()
if self.kill:
Path(self.filename).unlink()
self.duration = self.get_duration()
self.terminate_run()
def run(self, filename=None, name=None, record=True):
"""音频录制启动"""
if record:
if filename:
self.filename = filename
thread_1 = Thread(target=self.audio_record_run, args=(name,))
else:
if not filename:
raise Exception('未输入音频文件名,不能播放,请输入后再试!')
thread_1 = Thread(target=self.read_audio, args=(filename, name,))
thread_1.start()
def read_audio(self, filename, name=None):
"""音频播放"""
output_device_index = self.get_device_index(name, False) if name else None
with wave.open(filename, 'rb') as self.wf:
# 获取音频长度
self.duration = self.get_duration()
self.stream = self.open(format=self.get_format_from_width(self.wf.getsampwidth()),
channels=self.wf.getnchannels(),
rate=self.wf.getframerate(),
output=True,
output_device_index=output_device_index,
stream_callback=self.callback_output
)
self.stream.start_stream()
while self.stream.is_active():
time.sleep(0.1)
print(self.duration)
self.terminate_run()
def get_duration(self):
"""获取音频时长"""
return round(self.wf.getnframes() / self.wf.getframerate(), 2)
def get_in_out_devices(self):
"""获取系统输入输出设备"""
self.input_dict = {}
self.output_dict = {}
for i in range(self.get_device_count()):
devinfo = self.get_device_info_by_index(i)
if not devinfo['hostApi'] and int(devinfo['defaultSampleRate']) == self.fps \
and '映射器' not in devinfo['name']:
if devinfo['maxInputChannels']:
self.input_dict[devinfo['name'].split(' ')[0]] = i
elif devinfo['maxOutputChannels']:
self.output_dict[devinfo['name'].split(' ')[0]] = i
def get_device_index(self, name, inp=True):
"""获取选定设备索引"""
if inp and self.input_dict:
return self.input_dict.get(name, -1)
elif not inp and self.output_dict:
return self.output_dict.get(name, -1)
def save_audio_file(self, filename):
"""音频文件保存"""
wf = wave.open(filename, 'wb')
wf.setnchannels(self.channels)
wf.setsampwidth(self.get_sample_size(self.format_sample))
wf.setframerate(self.fps)
return wf
def terminate_run(self):
"""结束流录制或流播放"""
if self.stream is not None:
self.stream.stop_stream()
self.stream.close()
self.stream = None
self.wf = None
self.terminate()

@ -1,39 +0,0 @@
# 使用python动手做一个屏幕录制工具
*更新日期2022-04-20*
> windows 10
> python 3.7
- Screeshot_Gui.exe [58.5M] [下载地址](https://github.com/lk-itween/FunnyCodeRepository/releases/download/Screenshot_Gui/Screenshot_Gui.exe)
先将ffmpeg.exe压缩成7z格式再一并打包进入exe文件中且使用pyinstaller打包生成文件较大
- Screenshot_Gui.py
pyqt5制作的屏幕录制窗口窗口程序入口
- Screenshot_record.py
使用ffmpeg工具利用python调用ffmpeg命令行来录制屏幕
- Audio_record.py
pyaudio读取可录制音频设备设定需要使用的设备进行录制音频
- Screenshot_record_with_cv2.py
使用opencv-python录制视频并保存
- Screenshot_test.py
无窗口化测试联动屏幕录制及音频录制,保持音视频同时录制和同时停止录制
- resource
保存程序运行过程中需要使用到的程序或图片
- requirements.txt
已将所需模块放在requirements.txt中可直接pip install -r requirements.txt下载安装。
- PyAudio-0.2.11-cp37-cp37m-win_amd64.whl
python3.7环境的PyAudio的轮子
**ps:**
*1. ffmpeg.exe 请至此https://www.gyan.dev/ffmpeg/builds/#release-builds 下载符合的版本并解压将bin下的ffmpeg.exe移动到resource文件夹下*
*2. 如果PyAudio包不能使用pip在线安装python3.7环境请下载PyAudio-0.2.11-cp37-cp37m-win_amd64.whl至本地在该路径下打开cmd窗口执行pip install 该whl文件*
*3. 其他python环境可在https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio 下自行安装对应版本的whl文件*

@ -1,359 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'Screenshot_GUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../PaddleClas-release-2.3")
import shutil
from datetime import datetime
from pathlib import Path
from PIL import ImageGrab
from PyQt5.QtCore import QRect, Qt, QPoint, QMetaObject, QThread
from PyQt5.QtGui import QIcon, QFont, QColor, QImage, QPixmap, QPen, QPainter
from PyQt5.QtWidgets import (QMainWindow, QApplication, QDesktopWidget, QWidget, QFrame,
QLabel, QPushButton, QComboBox, QCheckBox, QSpinBox, QDialog, QFileDialog, QMessageBox)
from Screenshot.Audio_record import AudioRecord
from Screenshot.Screenshot_record import Screenshot, file_path
def unpack_7zip_to_exe(filename):
file_basename = Path(file_path(f'{filename}.exe'))
if file_basename.exists():
return True
elif file_basename.with_suffix('.7z').exists():
shutil.unpack_archive(file_basename.with_suffix('.7z'), file_basename.parent)
return unpack_7zip_to_exe(file_basename.stem)
else:
return False
class Thread_screenshot(QThread):
def __init__(self):
super().__init__()
self.offset_x = 0
self.offset_y = 0
self.draw_mouse = 1
self.filename = ''
self.screen = Screenshot()
def config_modify(self, **kwargs):
self.screen.width = kwargs.get('width', self.screen.width)
self.screen.height = kwargs.get('height', self.screen.height)
self.screen.fps = kwargs.get('fps', self.screen.fps)
self.offset_x = kwargs.get('offset_x', self.offset_x)
self.offset_y = kwargs.get('offset_y', self.offset_y)
self.draw_mouse = kwargs.get('draw_mouse', self.draw_mouse)
self.filename = kwargs.get('filename', self.filename)
def compose(self):
video = self.filename + '.mp4'
audio = self.filename + '.mp3'
self.screen.compose_audio(video, audio, video.replace('缓存', ''))
QThread.sleep(1)
self.stop()
Path(audio).unlink()
Path(video).unlink()
def run(self):
if not self.filename:
raise Exception('请输入filename!')
filename = self.filename + '.mp4'
self.screen.record(filename, self.offset_x, self.offset_y, self.draw_mouse)
def stop(self):
self.screen.terminate()
class Ui_MainWindow2(QMainWindow):
"""主体窗口设置"""
def __init__(self):
super().__init__()
self.setObjectName("MainWindow")
self.setWindowTitle('屏幕录制')
windowico = file_path('gui_svg.svg')
self.setWindowIcon(QIcon(windowico))
self.resize(512, 352)
self.setFixedSize(512, 352)
self._dir = '.'
self.fname = ''
self.offset_x, self.offset_y = 0, 0
self.screen_x, self.screen_y = ImageGrab.grab().size
self.audio = AudioRecord()
self.audio.get_in_out_devices()
self.device_name = None
self.record_audio = True if self.audio.input_dict else False
self.hotkey_start = False
self.screenshot = Thread_screenshot()
self.setupUi()
ffmpeg_exists = unpack_7zip_to_exe('ffmpeg')
if not ffmpeg_exists:
QMessageBox.warning(self, '请下载ffmpeg', '请将ffmpeg.exe放在resource目录下再录制')
self.show()
def setupUi(self):
self.centralwidget = QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.label0 = QLabel(self.centralwidget)
self.label0.setGeometry(QRect(0, 0, 421, 351))
self.label0.setObjectName("scrollArea")
# 添加边框
self.label0.setFrameShape(QFrame.Box)
self.label0.setFrameShadow(QFrame.Sunken)
self.get_screen_grab((0, 0, self.screen_x, self.screen_y))
self.label0.setAlignment(Qt.AlignCenter)
self.label_1 = QLabel(self.centralwidget)
font = QFont()
font.setBold(True)
self.label_1.setGeometry(QRect(430, 0, 85, 31))
self.label_1.setTextFormat(Qt.AutoText)
self.label_1.setFont(font)
self.label_1.setScaledContents(True)
self.label_1.setObjectName("屏幕录制器")
self.label_6 = QLabel(self.centralwidget)
self.label_6.setGeometry(QRect(430, 20, 85, 31))
self.label_6.setTextFormat(Qt.AutoText)
self.label_6.setScaledContents(True)
self.label_6.setObjectName("注释1")
self.checkbox = QCheckBox(self.centralwidget)
self.checkbox.setGeometry(QRect(430, 100, 85, 20))
self.checkbox.setChecked(True)
self.checkbox.stateChanged.connect(self.mouse_draw)
self.checkbox.setObjectName("录制鼠标")
self.comboBox = QComboBox(self.centralwidget)
self.comboBox.setGeometry(QRect(430, 200, 85, 20))
self.comboBox.setEditable(False)
self.comboBox.addItems(self.audio.input_dict.keys())
self.comboBox.addItems([''])
self.comboBox.setMaxVisibleItems(9)
self.comboBox.currentTextChanged.connect(self.change_combox)
self.comboBox.setObjectName("音频来源")
self.comboBox_3 = QComboBox(self.centralwidget)
self.comboBox_3.setGeometry(QRect(430, 260, 80, 20))
self.comboBox_3.addItems(['双声道', '单声道'])
self.comboBox_3.currentIndexChanged.connect(self.change_combox)
self.comboBox_3.setObjectName("声道设置")
self.pushButton = QPushButton(self.centralwidget)
self.pushButton.setGeometry(QRect(470, 320, 41, 31))
self.pushButton.clicked.connect(self.stop)
self.pushButton.setObjectName("停止")
self.pushButton_2 = QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QRect(430, 320, 41, 31))
self.pushButton_2.setObjectName("开始")
self.pushButton_2.clicked.connect(self.start)
self.pushButton_3 = QPushButton(self.centralwidget)
self.pushButton_3.setGeometry(QRect(430, 290, 80, 23))
self.pushButton_3.setObjectName("保存目录")
self.pushButton_3.clicked.connect(self.open_dirpath)
self.pushButton_4 = QPushButton(self.centralwidget)
self.pushButton_4.setGeometry(QRect(430, 70, 80, 23))
self.pushButton_4.setCheckable(True)
self.pushButton_4.setObjectName("Size")
self.pushButton_4.clicked.connect(self.get_screen_area)
self.spinBox = QSpinBox(self.centralwidget)
self.spinBox.setValue(15)
self.spinBox.setRange(10, 60)
self.spinBox.setGeometry(QRect(430, 145, 80, 20))
self.spinBox.valueChanged.connect(self.screen_fps_mofify)
self.spinBox.setObjectName("spinBox")
self.label_2 = QLabel(self.centralwidget)
self.label_2.setGeometry(QRect(430, 50, 54, 16))
self.label_2.setTextFormat(Qt.AutoText)
self.label_2.setObjectName("选择区域")
self.label_3 = QLabel(self.centralwidget)
self.label_3.setGeometry(QRect(430, 125, 54, 16))
self.label_3.setObjectName("帧率")
self.label_4 = QLabel(self.centralwidget)
self.label_4.setGeometry(QRect(430, 240, 54, 16))
self.label_4.setObjectName("声道设置")
self.label_5 = QLabel(self.centralwidget)
self.label_5.setGeometry(QRect(430, 180, 54, 16))
self.label_5.setObjectName("音频来源")
self.setCentralWidget(self.centralwidget)
self.retranslateUi()
QMetaObject.connectSlotsByName(self)
def retranslateUi(self):
self.pushButton.setText("停止")
self.pushButton_2.setText("开始")
self.pushButton_3.setText("保存目录")
self.pushButton_4.setText("Size*Size")
self.checkbox.setText("录制鼠标")
self.label_1.setText("屏幕录制器")
self.label_2.setText("选择区域:")
self.label_3.setText("帧率:")
self.label_4.setText("声道设置:")
self.label_5.setText("音频来源:")
self.label_6.setText("F7开始/停止")
def keyPressEvent(self, event):
"""监测键盘是否按下F3如果已开始录制则停止否则开始录制"""
if event.key() == Qt.Key_F7:
if not self.hotkey_start:
self.start()
else:
self.stop()
def change_combox(self, event):
sendername = self.sender().objectName()
if sendername == '声道设置':
self.channels = 2 if event == 0 else 1
elif sendername == '音频来源':
if event == '':
self.record_audio = False
else:
self.record_audio = True
self.device_name = event
def get_screen_grab(self, crop_size):
# 获取屏幕截图并更新至self.label0中
screen = ImageGrab.grab().convert('RGBA')
screen = screen.crop(box=crop_size) # box=(left, upper, right, lower)
data = screen.tobytes("raw", "RGBA")
qim = QImage(data, screen.size[0], screen.size[1], QImage.Format_RGBA8888)
pix = QPixmap.fromImage(qim).scaled(self.label0.size(), aspectRatioMode=Qt.KeepAspectRatio)
pix.detach() # QPixmap为另开线程操作的避免程序崩溃使用detach方法使用一个QImage对象直到QPixmap对象销毁
self.label0.setPixmap(pix)
self.label0.repaint()
return screen.size
def get_screen_area(self):
# 获取截图区间
SizeScreen = MousePaint()
SizeScreen.exec_()
self.offset_x, self.offset_y = SizeScreen.lastpoint
end_x, end_y = SizeScreen.endpoint
self.screen_x, self.screen_y = self.get_screen_grab((self.offset_x, self.offset_y, end_x, end_y))
self.screenshot.config_modify(offset_x=self.offset_x, offset_y=self.offset_y,
width=self.screen_x, height=self.screen_y)
self.pushButton_4.setText(f'{self.screen_x}*{self.screen_y}')
def screen_fps_mofify(self, fps):
self.screenshot.config_modify(fps=fps)
def mouse_draw(self, checked):
draw_mouse = 1 if checked else 0
self.screenshot.config_modify(draw_mouse=draw_mouse)
def open_dirpath(self):
fdir = QFileDialog.getExistingDirectory(self, '选择目录', self._dir)
if not fdir:
return None
self._dir = fdir
self.set_filename()
def set_filename(self):
self.fname = f'{self._dir}/屏幕录制缓存_{int(datetime.now().replace(microsecond=0).timestamp())}'
self.screenshot.config_modify(filename=self.fname)
self.audio = AudioRecord()
self.setWindowTitle(f'屏幕录制 {self.fname}')
def start(self):
if self.hotkey_start:
return
if not self.fname:
QMessageBox.warning(self, '请选择保存目录!', '请选择保存目录!')
return
self.screenshot.start()
if self.record_audio:
self.audio.stop_flag = False
self.audio.run(filename=self.fname + '.mp3', name=self.device_name)
self.hotkey_start = True
def stop(self):
if not self.hotkey_start:
return
self.audio.stop_flag = True
self.hotkey_start = False
self.screenshot.stop()
if self.record_audio:
self.screenshot.compose()
else:
filename = self.fname + '.mp4'
target = filename.replace('缓存', '')
Path(filename).replace(target)
self.set_filename()
class MousePaint(QDialog):
"""移动鼠标获取屏幕捕获范围"""
def __init__(self):
super().__init__()
self.setMouseTracking(True)
# 设置窗口布满整个屏幕
self.showFullScreen()
# 设置窗体无边框
self.setWindowFlags(Qt.FramelessWindowHint) # 窗口置顶,无边框
# 设置背景透明
self.setWindowOpacity(0.5)
self.initUI()
self.setFocus()
def initUI(self):
self.setGeometry(*(QDesktopWidget().screenGeometry()).getRect())
self.pix = QPixmap()
self.lastpoint = QPoint()
self.endpoint = QPoint()
self.pos = None
self.bline = 0
def mousePressEvent(self, event):
# 监听鼠标按压事件
if event.button() == Qt.LeftButton:
self.lastpoint = event.x(), event.y()
self.bline = 1
elif event.button() == Qt.RightButton:
self.close()
event.accept()
def mouseReleaseEvent(self, event):
# 监听鼠标释放事件
self.endpoint = event.x(), event.y()
self.bline = 0
event.accept()
self.close()
def mouseMoveEvent(self, event):
# 监听鼠标移动事件
if self.bline == 1:
self.pos = event.x(), event.y()
event.accept()
self.update()
def paintEvent(self, event):
# 绘画事件
if self.bline == 1:
pp = QPainter(self)
pen = QPen() # 定义笔格式对象
pen.setWidth(5) # 设置笔的宽度
pen.setColor(QColor(255, 0, 0))
pp.setPen(pen)
lpx, lpy = self.lastpoint
pp.drawRect(lpx, lpy, self.pos[0] - lpx, self.pos[1] - lpy)
event.accept()
# def main():
# """运行函数"""
# # 将7zip压缩格式添加到shutil中
# shutil.register_archive_format('7zip', pack_7zarchive, description='7zip archive')
# shutil.register_unpack_format('7zip', ['.7z'], unpack_7zarchive, description='7zip archive')
# app = QApplication(sys.argv)
# app.setAttribute(Qt.AA_UseHighDpiPixmaps)
# ui = Ui_MainWindow1()
# sys.exit(app.exec_())
#if __name__ == '__main__':
# main()

@ -1,67 +0,0 @@
"""
使用ffmpeg录制屏幕并结合Pyaudio同步录制的音频合成带有声音的视频文件
"""
import sys
from pathlib import Path
from time import sleep
import ffmpeg
def file_path(filename):
relative_path = Path('resource').joinpath(filename)
if getattr(sys, 'frozen', False):
base_path = Path(sys.MEIPASS)
else:
base_path = Path('.').absolute()
return Path(base_path).joinpath(relative_path).as_posix()
class Screenshot:
def __init__(self, width=1920, height=1080, fps=15):
self.width = width
self.height = height
self.fps = fps
self.process = None
self.ffmpeg_path = file_path('ffmpeg.exe')
def __call__(self, width, height, fps=None):
self.width = width
self.height = height
self.fps = fps if fps else self.fps
@staticmethod
def unlink(filename):
Path(filename).unlink()
def record(self, filename, offset_x=0, offset_y=0, draw_mouse=0):
self.process = (
ffmpeg.output(
ffmpeg.input(
filename='desktop', format='gdigrab', framerate=self.fps, offset_x=offset_x, offset_y=offset_y,
draw_mouse=draw_mouse, s=f'{self.width}x{self.height}'),
filename=filename, pix_fmt='yuv420p'
).overwrite_output()
)
self.ffmpeg_async()
def compose_audio(self, video_path, audio_path, output_path):
self.process = (
ffmpeg.output(
ffmpeg.input(filename=video_path),
ffmpeg.input(filename=audio_path),
filename=output_path, vcodec='copy', acodec='aac', strict='experimental', pix_fmt='yuv420p'
).overwrite_output()
)
sleep(1)
self.ffmpeg_async()
def ffmpeg_async(self):
self.process = self.process.run_async(cmd=self.ffmpeg_path, pipe_stdin=True, pipe_stdout=False,
pipe_stderr=False)
def terminate(self):
if self.process is not None:
self.process.communicate(str.encode("q"))
self.process.terminate()
self.process = None

@ -1,151 +0,0 @@
import time
from pathlib import Path
from threading import Thread
import cv2
import numpy as np
from PIL import ImageGrab
from numba import jit
from pynput import keyboard
@jit(nopython=True)
def average_n(x, y):
"""Numpy计算趋近值"""
return ((x + y + y) // 3).astype(x.dtype)
class ScreenshotVideo(Thread):
def __init__(self, width, high, path='', fps=15):
"""初始化参数"""
super().__init__()
self.save_file = path
self.best_fps = fps
self.fps = fps
self.width = width
self.high = high
self.spend_time = 1
self.flag = False
self.kill = False
self.video = None
def __call__(self, path):
"""重载视频路径,便于类的二次调用"""
self.save_file = Path(path)
self.video = self.init_videowriter(self.save_file)
@staticmethod
def screenshot():
"""静态方法屏幕截图并转换为np.array数组"""
return np.array(ImageGrab.grab())
@staticmethod
def get_fourcc(name):
"""视频编码字典"""
fourcc_maps = {'.avi': 'I420',
'.m4v': 'mp4v',
'.mp4': 'avc1',
'.ogv': 'THEO',
'.flv': 'FLV1',
}
return fourcc_maps.get(name)
def init_videowriter(self, path):
"""获取视频编码并新建视频文件"""
if not path:
raise Exception('视频路径未设置,请设置\nvideo = ScreenshotVideo(fps,width,high)\nvideo = video(video_path)')
path = Path(path) if isinstance(path, str) else path
fourcc = cv2.VideoWriter_fourcc(*self.get_fourcc(path.suffix))
return cv2.VideoWriter(path.as_posix(), fourcc, self.fps, (self.width, self.high))
def video_record_doing(self, img):
"""将BGR数组转换为RGB数组"""
im_cv = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
self.video.write(im_cv)
def video_record_end(self):
"""录制结束,根据条件判断文件是否保存"""
self.video.release()
cv2.destroyAllWindows()
if self.save_file and self.kill:
Path(self.save_file).unlink()
def video_best_fps(self, path):
"""获取电脑录制视频的最优帧率"""
video = cv2.VideoCapture(path)
fps = video.get(cv2.CAP_PROP_FPS)
count = video.get(cv2.CAP_PROP_FRAME_COUNT)
self.best_fps = int(fps * ((int(count) / fps) / self.spend_time))
video.release()
def pre_video_record(self):
"""预录制以获取最佳fps值"""
self.video = self.init_videowriter('test.mp4')
start_time = time.time()
for _ in range(10):
im = self.screenshot()
self.video_record_doing(im)
self.spend_time = round(time.time() - start_time, 4)
self.video_record_end()
time.sleep(2)
self.video_best_fps('test.mp4')
Path('test.mp4').unlink()
def insert_frame_array(self, frame_list):
"""Numpy增强截图信息"""
fps_n = round(self.fps / self.best_fps)
if fps_n <= 0:
return frame_list
times = int(np.log2(fps_n)) # 倍率
for _ in range(times):
frame_list2 = map(average_n, [frame_list[0]] + frame_list[:-1], frame_list)
frame_list = [[x, y] for x, y in zip(frame_list2, frame_list)]
frame_list = [j for i in frame_list for j in i]
return frame_list
def frame2video_run(self):
"""使用opencv将连续型截图转换为视频"""
self.video = self.init_videowriter(self.save_file)
start_time = time.time()
frame_list = []
while True:
frame_list.append(self.screenshot())
if self.flag:
break
self.spend_time = round(time.time() - start_time, 4)
if not self.kill: # 视频录制不被终止将逐帧处理图像
frame_list = self.insert_frame_array(frame_list)
for im in frame_list:
self.video_record_doing(im)
self.video_record_end()
def hotkey(self):
"""热键监听"""
with keyboard.Listener(on_press=self.on_press) as listener:
listener.join()
def on_press(self, key):
try:
if key.char == 't': # 录屏结束,保存视频
self.flag = True
elif key.char == 'k': # 录屏中止,删除文件
self.flag = True
self.kill = True
except Exception as e:
print(e)
def run(self):
# 运行函数
# 设置守护线程
Thread(target=self.hotkey, daemon=True).start()
# 运行截图函数
self.frame2video_run()
screen = ImageGrab.grab()
width, high = screen.size
video = ScreenshotVideo(width, high, fps=60)
video.pre_video_record() # 预录制获取最优fps
video('test1.mp4')
video.run()

@ -1,32 +0,0 @@
from threading import Thread
from pynput import keyboard # pip install pynput
from Audio_record import AudioRecord
from Screenshot_record import Screenshot
def hotkey():
"""热键监听"""
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
def on_press(key):
try:
video.terminate()
if key.char == 't': # t键录制结束保存音视频
audio.stop_flag = True
elif key.char == 'k': # k键录制中止删除文件
audio.stop_flag = True
audio.kill = True
video.unlink('test.mp4')
except Exception as e:
print(e)
key_thread = Thread(target=hotkey, daemon=True)
audio = AudioRecord()
video = Screenshot()
key_thread.start()
video.record('test.mp4')

@ -1,9 +0,0 @@
ffmpeg-python==0.2.0
numba==0.54.1
numpy==1.20.3
opencv-python==4.5.4.60
Pillow==8.4.0
PyAudio
pynput==1.7.5
PyQt5==5.15.4
py7zr==0.18.4

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

@ -1,4 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1649602952440" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1892"
width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M196.608 806.570667c-0.341333-19.114667 15.018667-34.474667 34.133333-34.816h562.176c9.216 0 17.749333 3.754667 24.234667 10.24 6.485333 6.485333 9.898667 15.36 10.24 24.576 0 19.114667-15.36 34.474667-34.474667 34.474666H231.082667c-19.114667 0-34.474667-15.36-34.474667-34.474666zM815.445333 703.488H208.554667c-41.642667 0-75.093333-33.792-75.093334-75.093333V291.157333c0-41.642667 33.792-75.093333 75.093334-75.093333H815.786667c41.642667 0 75.093333 33.792 75.093333 75.093333v337.237334c0 41.301333-33.792 75.093333-75.434667 75.093333z" fill="#635DF7" opacity=".7" p-id="1893"></path><path d="M323.584 553.642667c-9.557333-9.557333-9.557333-25.258667 0-34.816l59.050667-59.050667-59.050667-59.050667c-9.557333-9.557333-9.557333-25.258667 0-34.816 9.557333-9.557333 25.258667-9.557333 34.816 0l76.458667 76.458667c9.557333 9.557333 9.557333 25.258667 0 34.816L358.4 553.642667c-9.557333 9.898667-25.258667 9.898667-34.816 0zM683.008 484.352h-107.52c-13.653333 0-24.576-10.922667-24.576-24.576s10.922667-24.576 24.576-24.576h107.52c13.653333 0 24.576 10.922667 24.576 24.576s-10.922667 24.576-24.576 24.576z" fill="#FFFFFF" p-id="1894"></path></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@ -1,19 +1,18 @@
# @Time : 2022/5/9 20:49
# @Author : 2890199310@qq.com
# @File : KeyPress.py
# @Software: PyCharm
# @Function:
import pygame
def main():
keyPress()
def keyPress(key):
if(key == 1):
return "e"
def result():
return keyPress()
def init():
pygame.init()
win = pygame.display.set_mode((400, 400))
def getKey(keyName):
ans = False
for eve in pygame.event.get(): pass
keyInput = pygame.key.get_pressed()
myKey = getattr(pygame,'K_{}'.format(keyName))
if keyInput[myKey]:
ans = True
pygame.display.update()
return ans
if __name__ == '__main__':
main()
init()

@ -1,31 +1,18 @@
# @Time : 2022/4/20 12:27
# @Author : 2890199310@qq.com
# @File : KeyPressModule.py.py
# @Software: PyCharm
# @Function:
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../PaddleClas-release-2.3")
# import pygame
import Tello.KeyPress as k
import pygame
def init():
return
pygame.init()
win = pygame.display.set_mode((400, 400))
def getKey(keyName):
# ans = False
# for eve in pygame.event.get(): pass
# keyInput = pygame.key.get_pressed()
# myKey = getattr(pygame,'K_{}'.format(keyName))
# if keyInput[myKey]:
if keyName == k.result():
ans = False
for eve in pygame.event.get(): pass
keyInput = pygame.key.get_pressed()
myKey = getattr(pygame,'K_{}'.format(keyName))
if keyInput[myKey]:
ans = True
# pygame.display.update()
pygame.display.update()
return ans
def key(a):
return a
if __name__ == '__main__':
init()

@ -1,8 +1,8 @@
# @Time : 2022/4/20 12:27
# @Author : 2890199310@qq.com
# @File : KeyboardControl.py.py
# @Software: PyCharm
# @Function:
# # @Time : 2022/4/20 12:27
# # @Author : 2890199310@qq.com
# # @File : KeyboardControl.py.py
# # @Software: PyCharm
# # @Function:
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../PaddleClas-release-2.3")
@ -13,16 +13,45 @@ from djitellopy import tello
# import Tello.KeyPressModule as kp # 用于获取键盘按键
from time import sleep
# Tello初始化设置
Drone = tello.Tello() # 创建飞行器对象
Drone.connect() # 连接到飞行器
Drone.streamon() # 开启视频传输
Drone.LOGGER.setLevel(logging.ERROR) # 只显示错误信息
sleep(5) # 等待视频初始化
# kp.init() # 初始化按键处理模块
Camera_Width = 720
Camera_Height = 480
DetectRange = [6000, 11000] # DetectRange[0] 是保持静止的检测人脸面积阈值下限DetectRange[0] 是保持静止的检测人脸面积阈值上限
PID_Parameter = [0.5, 0.0004, 0.4]
pErrorRotate, pErrorUp = 0, 0
# 字体设置
font = cv2.FONT_HERSHEY_SIMPLEX
fontScale = 0.5
fontColor = (255, 0, 0)
lineThickness = 1
def connect():
# Tello初始化设置
# 创建飞行器对象
# kp.init() # 初始化按键处理模块
global Drone
Drone = tello.Tello()
Drone.connect() # 连接到飞行器
Drone.streamon() # 开启视频传输
Drone.LOGGER.setLevel(logging.ERROR) # 只显示错误信息
sleep(5) # 等待视频初始化
flag = True
while flag:
global OriginalImage
OriginalImage = Drone.get_frame_read().frame
global image
image = cv2.resize(OriginalImage, (Camera_Width, Camera_Height))
# getKeyboardInput(drone=Drone, speed=70, image=Image) # 按键控制
cv2.imshow("Drone Control Centre", image)
cv2.waitKey(1)
if cv2.waitKey(1) == ord('q'):
flag = False
cv2.destroyAllWindows()
def getKeyboardInput(key):
image = cv2.resize(OriginalImage, (Camera_Width, Camera_Height))
# image = cv2.resize(OriginalImage, (Camera_Width, Camera_Height))
speed = 70
drone = Drone
lr, fb, ud, yv = 0, 0, 0, 0
@ -36,7 +65,6 @@ def getKeyboardInput(key):
# elif kp.getKey("DOWN"):#下降
if key == "DOWN":
Drone.land()
# if kp.getKey("j"):# 向左飞行
if key == "j":
key_pressed = 1
@ -45,11 +73,14 @@ def getKeyboardInput(key):
if key == "l":
key_pressed = 1
lr = speed
# if kp.getKey("i"): #向前飞行
if key == "i":
key_pressed = 1
fb = speed
# stop
if key == "s":
key_pressed = 1
fb = 0
# elif kp.getKey("k"):# 向后飞行
if key == "k":
key_pressed = 1
@ -80,26 +111,92 @@ def getKeyboardInput(key):
drone.send_rc_control(lr, fb, ud, yv)
# 主程序
# 摄像头设置
Camera_Width = 720
Camera_Height = 480
DetectRange = [6000, 11000] # DetectRange[0] 是保持静止的检测人脸面积阈值下限DetectRange[0] 是保持静止的检测人脸面积阈值上限
PID_Parameter = [0.5, 0.0004, 0.4]
pErrorRotate, pErrorUp = 0, 0
# 字体设置
font = cv2.FONT_HERSHEY_SIMPLEX
fontScale = 0.5
fontColor = (255, 0, 0)
lineThickness = 1
while True:
OriginalImage = Drone.get_frame_read().frame
Image = cv2.resize(OriginalImage, (Camera_Width, Camera_Height))
# getKeyboardInput(drone=Drone, speed=70, image=Image) # 按键控制
cv2.imshow("Drone Control Centre", Image)
cv2.waitKey(1)
# connect()
# # 主程序
# # 摄像头设置
#
#
#
#
# import logging
# import time
# import cv2
# from djitellopy import tello
# import KeyPressModule as kp # 用于获取键盘按键
# from time import sleep
#
# def getKeyboardInput(drone, speed, image):
# lr, fb, ud, yv = 0, 0, 0, 0
# key_pressed = 0
# if kp.getKey("e"):
# cv2.imwrite('D:/snap-{}.jpg'.format(time.strftime("%H%M%S", time.localtime())), image)
# if kp.getKey("UP"):
# Drone.takeoff()
# elif kp.getKey("DOWN"):
# Drone.land()
#
# if kp.getKey("j"):
# key_pressed = 1
# lr = -speed
# elif kp.getKey("l"):
# key_pressed = 1
# lr = speed
#
# if kp.getKey("i"):
# key_pressed = 1
# fb = speed
# elif kp.getKey("k"):
# key_pressed = 1
# fb = -speed
#
# if kp.getKey("w"):
# key_pressed = 1
# ud = speed
# elif kp.getKey("s"):
# key_pressed = 1
# ud = -speed
#
# if kp.getKey("a"):
# key_pressed = 1
# yv = -speed
# elif kp.getKey("d"):
# key_pressed = 1
# yv = speed
# InfoText = "battery : {0}% height: {1}cm time: {2}".format(drone.get_battery(), drone.get_height(), time.strftime("%H:%M:%S",time.localtime()))
# cv2.putText(image, InfoText, (10, 20), font, fontScale, (0, 0, 255), lineThickness)
# if key_pressed == 1:
# InfoText = "Command : lr:{0}% fb:{1} ud:{2} yv:{3}".format(lr, fb, ud, yv)
# cv2.putText(image, InfoText, (10, 40), font, fontScale, (0, 0, 255), lineThickness)
#
# drone.send_rc_control(lr, fb, ud, yv)
#
# # 主程序
# # 摄像头设置
# Camera_Width = 720
# Camera_Height = 480
# DetectRange = [6000, 11000] # DetectRange[0] 是保持静止的检测人脸面积阈值下限DetectRange[0] 是保持静止的检测人脸面积阈值上限
# PID_Parameter = [0.5, 0.0004, 0.4]
# pErrorRotate, pErrorUp = 0, 0
#
# # 字体设置
# font = cv2.FONT_HERSHEY_SIMPLEX
# fontScale = 0.5
# fontColor = (255, 0, 0)
# lineThickness = 1
#
# # Tello初始化设置
# Drone = tello.Tello() # 创建飞行器对象
# Drone.connect() # 连接到飞行器
# Drone.streamon() # 开启视频传输
# Drone.LOGGER.setLevel(logging.ERROR) # 只显示错误信息
# sleep(5) # 等待视频初始化
# kp.init() # 初始化按键处理模块
#
#
# while True:
# OriginalImage = Drone.get_frame_read().frame
# Image = cv2.resize(OriginalImage, (Camera_Width, Camera_Height))
# getKeyboardInput(drone=Drone, speed=70, image=Image) # 按键控制
# cv2.imshow("Drone Control Centre", Image)
# cv2.waitKey(1)

@ -6,13 +6,16 @@
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
import os
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import *
from findpath_UI import *
from Screenshot.Screenshot_Gui import *
from app import ScreenBar
import qdarkstyle
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../PaddleClas-release-2.3")
import Tello.KeyboardControl as KeyControl
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
@ -30,7 +33,7 @@ class Ui_MainWindow(object):
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.label = QtWidgets.QLabel(self.frame)
self.label.setGeometry(QtCore.QRect(0, 0, 111, 21))
self.label.setGeometry(QtCore.QRect(0, 0, 111, 25))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(14)
@ -38,9 +41,9 @@ class Ui_MainWindow(object):
font.setWeight(75)
self.label.setFont(font)
self.label.setStyleSheet("QLabel\n"
"{\n"
" color:white\n"
"}")
"{\n"
" color:white\n"
"}")
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(self.frame)
self.label_2.setGeometry(QtCore.QRect(20, 30, 341, 41))
@ -51,6 +54,15 @@ class Ui_MainWindow(object):
font.setWeight(75)
self.label_2.setFont(font)
self.label_2.setObjectName("label_2")
self.label_7 = QtWidgets.QLabel(self.frame)
self.label_7.setGeometry(QtCore.QRect(100, 30, 341, 41))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.label_7.setFont(font)
self.label_7.setObjectName("label_7")
self.label_3 = QtWidgets.QLabel(self.frame)
self.label_3.setGeometry(QtCore.QRect(20, 100, 341, 41))
font = QtGui.QFont()
@ -60,12 +72,21 @@ class Ui_MainWindow(object):
font.setWeight(75)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.label_8 = QtWidgets.QLabel(self.frame)
self.label_8.setGeometry(QtCore.QRect(100, 100, 341, 41))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.label_8.setFont(font)
self.label_8.setObjectName("label_8")
self.frame_2 = QtWidgets.QFrame(self.centralwidget)
self.frame_2.setGeometry(QtCore.QRect(840, 250, 391, 421))
self.frame_2.setStyleSheet("QPushButton\n"
"{\n"
" color:white\n"
"}")
"{\n"
" color:white\n"
"}")
self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_2.setObjectName("frame_2")
@ -78,13 +99,13 @@ class Ui_MainWindow(object):
font.setWeight(75)
self.label_4.setFont(font)
self.label_4.setStyleSheet("QLabel\n"
"{\n"
" color:white\n"
"}\n"
"")
"{\n"
" color:white\n"
"}\n"
"")
self.label_4.setObjectName("label_4")
self.pushButton_3 = QtWidgets.QPushButton(self.frame_2)
self.pushButton_3.setGeometry(QtCore.QRect(70, 30, 231, 91))
self.pushButton_3.setGeometry(QtCore.QRect(60, 30, 231, 91))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(10)
@ -96,6 +117,24 @@ class Ui_MainWindow(object):
self.pushButton_3.setIcon(icon1)
self.pushButton_3.setIconSize(QtCore.QSize(150, 150))
self.pushButton_3.setObjectName("pushButton_3")
self.upload = QtWidgets.QPushButton(self.frame_2)
self.upload.setGeometry(QtCore.QRect(300, 30, 50, 180))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.upload.setFont(font)
self.upload.setObjectName("upload")
self.download = QtWidgets.QPushButton(self.frame_2)
self.download.setGeometry(QtCore.QRect(300, 220, 50, 180))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.download.setFont(font)
self.download.setObjectName("download")
self.pushButton_6 = QtWidgets.QPushButton(self.frame_2)
self.pushButton_6.setGeometry(QtCore.QRect(60, 310, 231, 91))
font = QtGui.QFont()
@ -130,7 +169,7 @@ class Ui_MainWindow(object):
self.pushButton_5.setGeometry(QtCore.QRect(140, 220, 71, 71))
self.pushButton_5.setText("")
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap("tello_png/return.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon5.addPixmap(QtGui.QPixmap("tello_png/hold.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_5.setIcon(icon5)
self.pushButton_5.setIconSize(QtCore.QSize(70, 100))
self.pushButton_5.setObjectName("pushButton_5")
@ -142,6 +181,22 @@ class Ui_MainWindow(object):
self.pushButton.setIcon(icon6)
self.pushButton.setIconSize(QtCore.QSize(70, 100))
self.pushButton.setObjectName("pushButton")
self.pushButton_8 = QtWidgets.QPushButton(self.frame_2)
self.pushButton_8.setGeometry(QtCore.QRect(220, 140, 71, 71))
self.pushButton_8.setText("")
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap("tello_png/return.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_8.setIcon(icon6)
self.pushButton_8.setIconSize(QtCore.QSize(70, 100))
self.pushButton_8.setObjectName("pushButton_8")
self.pushButton_9 = QtWidgets.QPushButton(self.frame_2)
self.pushButton_9.setGeometry(QtCore.QRect(60, 140, 71, 71))
self.pushButton_9.setText("")
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap("tello_png/return_2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_9.setIcon(icon6)
self.pushButton_9.setIconSize(QtCore.QSize(70, 100))
self.pushButton_9.setObjectName("pushButton_9")
self.frame_3 = QtWidgets.QFrame(self.centralwidget)
self.frame_3.setGeometry(QtCore.QRect(840, 680, 391, 80))
self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel)
@ -164,6 +219,20 @@ class Ui_MainWindow(object):
font.setWeight(75)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.label_6 = QtWidgets.QLabel(self.frame_3)
self.label_6.setGeometry(QtCore.QRect(200, 15, 231, 51))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.label_6.setFont(font)
self.label_6.setStyleSheet("QLabel\n"
"{\n"
" color:red\n"
"}\n"
"")
self.label_6.setObjectName("label_6")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1260, 23))
@ -201,8 +270,16 @@ class Ui_MainWindow(object):
self.toolBar.addAction(self.actionicon)
self.toolBar.addAction(self.actionicon_2)
self.toolBar.addAction(self.actionicon_3)
self.connect_flag = False
self.actionicon_2.triggered.connect(lambda: self.path())
self.actionicon_3.triggered.connect(lambda: self.video())
self.pushButton_7.clicked.connect(lambda: self.connect())
self.pushButton_3.clicked.connect(lambda: self.up())
self.pushButton_6.clicked.connect(lambda: self.down())
self.pushButton.clicked.connect(lambda: self.forward())
self.pushButton_4.clicked.connect(lambda: self.right())
self.pushButton_2.clicked.connect(lambda: self.left())
self.pushButton_5.clicked.connect(lambda: self.Return())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
@ -213,25 +290,89 @@ class Ui_MainWindow(object):
self.MainWindow1.show()
def video(self):
self.ui = Ui_MainWindow2()
self.ui.show()
bar.show()
def connect(self):
self.connect_flag = True
self.label_6.setStyleSheet("QLabel\n"
"{\n"
" color:green\n"
"}\n"
"")
self.label_6.setText("已连接")
self.label_7.setStyleSheet("QLabel\n"
"{\n"
" color:green\n"
"}\n"
"")
self.label_7.setText("100%")
self.label_8.setStyleSheet("QLabel\n"
"{\n"
" color:green\n"
"}\n"
"")
self.label_8.setText("")
KeyControl.connect()
def up(self):
if self.connect_flag:
KeyControl.getKeyboardInput("UP")
else:
return
def down(self):
if self.connect_flag:
KeyControl.getKeyboardInput("DOWN")
else:
return
def forward(self):
if self.connect_flag:
KeyControl.getKeyboardInput("i")
else:
return
def right(self):
if self.connect_flag:
KeyControl.getKeyboardInput("l")
else:
return
def left(self):
if self.connect_flag:
KeyControl.getKeyboardInput("j")
else:
return
def Return(self):
if self.connect_flag:
KeyControl.getKeyboardInput("a")
KeyControl.getKeyboardInput("dffsfdsa")
else:
return
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "无人机路径生成系统"))
self.label.setText(_translate("MainWindow", "无人机状态"))
self.label_2.setText(_translate("MainWindow", "剩余电量:"))
self.label_7.setText(_translate("MainWindow", "--"))
self.label_3.setText(_translate("MainWindow", "WIFI强度"))
self.label_8.setText(_translate("MainWindow", "--"))
self.label_4.setText(_translate("MainWindow", "控制面板"))
self.pushButton_3.setText(_translate("MainWindow", "起飞"))
self.pushButton_6.setText(_translate("MainWindow", "降落"))
self.upload.setText(_translate("MainWindow", "上升"))
self.download.setText(_translate("MainWindow", "下降"))
self.label_5.setText(_translate("MainWindow", "连接状态:"))
self.label_6.setText(_translate("MainWindow", "未连接"))
self.menu.setTitle(_translate("MainWindow", "实时画面"))
self.menu_2.setTitle(_translate("MainWindow", "路径分析"))
self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
self.actionicon.setText(_translate("MainWindow", "实时画面"))
self.actionicon_2.setText(_translate("MainWindow", "路径分析"))
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
@ -239,4 +380,6 @@ if __name__ == '__main__':
ui.setupUi(MainWindow)
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
MainWindow.show()
sys.exit(app.exec_())
bar = ScreenBar()
bar.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
sys.exit(app.exec_())

@ -1,133 +0,0 @@
import time
import wave
from pathlib import Path
from threading import Thread
from pyaudio import PyAudio, paInt16, paContinue, paComplete
class AudioRecord(PyAudio):
def __init__(self, channels=2):
super().__init__()
self.chunk = 1024 # 每个缓冲区的帧数
self.format_sample = paInt16 # 采样位数
self.channels = channels # 声道: 1单声道2双声道
self.fps = 44100 # 采样频率
self.input_dict = None
self.output_dict = None
self.stream = None
self.filename = '~test.wav'
self.wf = None
self.stop_flag = False
self.kill = False
def callback_input(self, in_data, frame_count, time_info, status):
"""录制回调函数"""
self.wf.writeframes(in_data)
if not self.stop_flag:
return (in_data, paContinue)
else:
return (in_data, paComplete)
def callback_output(self, in_data, frame_count, time_info, status):
"""播放回调函数"""
data = self.wf.readframes(frame_count)
return (data, paContinue)
def open_stream(self, name):
"""打开录制流"""
input_device_index = self.get_device_index(name, True) if name else None
return self.open(format=self.format_sample,
channels=self.channels,
rate=self.fps,
frames_per_buffer=self.chunk,
input=True,
input_device_index=input_device_index,
stream_callback=self.callback_input
)
def audio_record_run(self, name=None):
"""音频录制"""
self.wf = self.save_audio_file(self.filename)
self.stream = self.open_stream(name)
self.stream.start_stream()
while self.stream.is_active():
time.sleep(0.1)
self.wf.close()
if self.kill:
Path(self.filename).unlink()
self.duration = self.get_duration()
self.terminate_run()
def run(self, filename=None, name=None, record=True):
"""音频录制启动"""
if record:
if filename:
self.filename = filename
thread_1 = Thread(target=self.audio_record_run, args=(name,))
else:
if not filename:
raise Exception('未输入音频文件名,不能播放,请输入后再试!')
thread_1 = Thread(target=self.read_audio, args=(filename, name,))
thread_1.start()
def read_audio(self, filename, name=None):
"""音频播放"""
output_device_index = self.get_device_index(name, False) if name else None
with wave.open(filename, 'rb') as self.wf:
# 获取音频长度
self.duration = self.get_duration()
self.stream = self.open(format=self.get_format_from_width(self.wf.getsampwidth()),
channels=self.wf.getnchannels(),
rate=self.wf.getframerate(),
output=True,
output_device_index=output_device_index,
stream_callback=self.callback_output
)
self.stream.start_stream()
while self.stream.is_active():
time.sleep(0.1)
print(self.duration)
self.terminate_run()
def get_duration(self):
"""获取音频时长"""
return round(self.wf.getnframes() / self.wf.getframerate(), 2)
def get_in_out_devices(self):
"""获取系统输入输出设备"""
self.input_dict = {}
self.output_dict = {}
for i in range(self.get_device_count()):
devinfo = self.get_device_info_by_index(i)
if not devinfo['hostApi'] and int(devinfo['defaultSampleRate']) == self.fps \
and '映射器' not in devinfo['name']:
if devinfo['maxInputChannels']:
self.input_dict[devinfo['name'].split(' ')[0]] = i
elif devinfo['maxOutputChannels']:
self.output_dict[devinfo['name'].split(' ')[0]] = i
def get_device_index(self, name, inp=True):
"""获取选定设备索引"""
if inp and self.input_dict:
return self.input_dict.get(name, -1)
elif not inp and self.output_dict:
return self.output_dict.get(name, -1)
def save_audio_file(self, filename):
"""音频文件保存"""
wf = wave.open(filename, 'wb')
wf.setnchannels(self.channels)
wf.setsampwidth(self.get_sample_size(self.format_sample))
wf.setframerate(self.fps)
return wf
def terminate_run(self):
"""结束流录制或流播放"""
if self.stream is not None:
self.stream.stop_stream()
self.stream.close()
self.stream = None
self.wf = None
self.terminate()

@ -1,39 +0,0 @@
# 使用python动手做一个屏幕录制工具
*更新日期2022-04-20*
> windows 10
> python 3.7
- Screeshot_Gui.exe [58.5M] [下载地址](https://github.com/lk-itween/FunnyCodeRepository/releases/download/Screenshot_Gui/Screenshot_Gui.exe)
先将ffmpeg.exe压缩成7z格式再一并打包进入exe文件中且使用pyinstaller打包生成文件较大
- Screenshot_Gui.py
pyqt5制作的屏幕录制窗口窗口程序入口
- Screenshot_record.py
使用ffmpeg工具利用python调用ffmpeg命令行来录制屏幕
- Audio_record.py
pyaudio读取可录制音频设备设定需要使用的设备进行录制音频
- Screenshot_record_with_cv2.py
使用opencv-python录制视频并保存
- Screenshot_test.py
无窗口化测试联动屏幕录制及音频录制,保持音视频同时录制和同时停止录制
- resource
保存程序运行过程中需要使用到的程序或图片
- requirements.txt
已将所需模块放在requirements.txt中可直接pip install -r requirements.txt下载安装。
- PyAudio-0.2.11-cp37-cp37m-win_amd64.whl
python3.7环境的PyAudio的轮子
**ps:**
*1. ffmpeg.exe 请至此https://www.gyan.dev/ffmpeg/builds/#release-builds 下载符合的版本并解压将bin下的ffmpeg.exe移动到resource文件夹下*
*2. 如果PyAudio包不能使用pip在线安装python3.7环境请下载PyAudio-0.2.11-cp37-cp37m-win_amd64.whl至本地在该路径下打开cmd窗口执行pip install 该whl文件*
*3. 其他python环境可在https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio 下自行安装对应版本的whl文件*

@ -1,359 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'Screenshot_GUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../PaddleClas-release-2.3")
import shutil
from datetime import datetime
from pathlib import Path
from PIL import ImageGrab
from PyQt5.QtCore import QRect, Qt, QPoint, QMetaObject, QThread
from PyQt5.QtGui import QIcon, QFont, QColor, QImage, QPixmap, QPen, QPainter
from PyQt5.QtWidgets import (QMainWindow, QApplication, QDesktopWidget, QWidget, QFrame,
QLabel, QPushButton, QComboBox, QCheckBox, QSpinBox, QDialog, QFileDialog, QMessageBox)
from Screenshot.Audio_record import AudioRecord
from Screenshot.Screenshot_record import Screenshot, file_path
def unpack_7zip_to_exe(filename):
file_basename = Path(file_path(f'{filename}.exe'))
if file_basename.exists():
return True
elif file_basename.with_suffix('.7z').exists():
shutil.unpack_archive(file_basename.with_suffix('.7z'), file_basename.parent)
return unpack_7zip_to_exe(file_basename.stem)
else:
return False
class Thread_screenshot(QThread):
def __init__(self):
super().__init__()
self.offset_x = 0
self.offset_y = 0
self.draw_mouse = 1
self.filename = ''
self.screen = Screenshot()
def config_modify(self, **kwargs):
self.screen.width = kwargs.get('width', self.screen.width)
self.screen.height = kwargs.get('height', self.screen.height)
self.screen.fps = kwargs.get('fps', self.screen.fps)
self.offset_x = kwargs.get('offset_x', self.offset_x)
self.offset_y = kwargs.get('offset_y', self.offset_y)
self.draw_mouse = kwargs.get('draw_mouse', self.draw_mouse)
self.filename = kwargs.get('filename', self.filename)
def compose(self):
video = self.filename + '.mp4'
audio = self.filename + '.mp3'
self.screen.compose_audio(video, audio, video.replace('缓存', ''))
QThread.sleep(1)
self.stop()
Path(audio).unlink()
Path(video).unlink()
def run(self):
if not self.filename:
raise Exception('请输入filename!')
filename = self.filename + '.mp4'
self.screen.record(filename, self.offset_x, self.offset_y, self.draw_mouse)
def stop(self):
self.screen.terminate()
class Ui_MainWindow2(QMainWindow):
"""主体窗口设置"""
def __init__(self):
super().__init__()
self.setObjectName("MainWindow")
self.setWindowTitle('屏幕录制')
windowico = file_path('gui_svg.svg')
self.setWindowIcon(QIcon(windowico))
self.resize(512, 352)
self.setFixedSize(512, 352)
self._dir = '.'
self.fname = ''
self.offset_x, self.offset_y = 0, 0
self.screen_x, self.screen_y = ImageGrab.grab().size
self.audio = AudioRecord()
self.audio.get_in_out_devices()
self.device_name = None
self.record_audio = True if self.audio.input_dict else False
self.hotkey_start = False
self.screenshot = Thread_screenshot()
self.setupUi()
ffmpeg_exists = unpack_7zip_to_exe('ffmpeg')
if not ffmpeg_exists:
QMessageBox.warning(self, '请下载ffmpeg', '请将ffmpeg.exe放在resource目录下再录制')
self.show()
def setupUi(self):
self.centralwidget = QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.label0 = QLabel(self.centralwidget)
self.label0.setGeometry(QRect(0, 0, 421, 351))
self.label0.setObjectName("scrollArea")
# 添加边框
self.label0.setFrameShape(QFrame.Box)
self.label0.setFrameShadow(QFrame.Sunken)
self.get_screen_grab((0, 0, self.screen_x, self.screen_y))
self.label0.setAlignment(Qt.AlignCenter)
self.label_1 = QLabel(self.centralwidget)
font = QFont()
font.setBold(True)
self.label_1.setGeometry(QRect(430, 0, 85, 31))
self.label_1.setTextFormat(Qt.AutoText)
self.label_1.setFont(font)
self.label_1.setScaledContents(True)
self.label_1.setObjectName("屏幕录制器")
self.label_6 = QLabel(self.centralwidget)
self.label_6.setGeometry(QRect(430, 20, 85, 31))
self.label_6.setTextFormat(Qt.AutoText)
self.label_6.setScaledContents(True)
self.label_6.setObjectName("注释1")
self.checkbox = QCheckBox(self.centralwidget)
self.checkbox.setGeometry(QRect(430, 100, 85, 20))
self.checkbox.setChecked(True)
self.checkbox.stateChanged.connect(self.mouse_draw)
self.checkbox.setObjectName("录制鼠标")
self.comboBox = QComboBox(self.centralwidget)
self.comboBox.setGeometry(QRect(430, 200, 85, 20))
self.comboBox.setEditable(False)
self.comboBox.addItems(self.audio.input_dict.keys())
self.comboBox.addItems([''])
self.comboBox.setMaxVisibleItems(9)
self.comboBox.currentTextChanged.connect(self.change_combox)
self.comboBox.setObjectName("音频来源")
self.comboBox_3 = QComboBox(self.centralwidget)
self.comboBox_3.setGeometry(QRect(430, 260, 80, 20))
self.comboBox_3.addItems(['双声道', '单声道'])
self.comboBox_3.currentIndexChanged.connect(self.change_combox)
self.comboBox_3.setObjectName("声道设置")
self.pushButton = QPushButton(self.centralwidget)
self.pushButton.setGeometry(QRect(470, 320, 41, 31))
self.pushButton.clicked.connect(self.stop)
self.pushButton.setObjectName("停止")
self.pushButton_2 = QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QRect(430, 320, 41, 31))
self.pushButton_2.setObjectName("开始")
self.pushButton_2.clicked.connect(self.start)
self.pushButton_3 = QPushButton(self.centralwidget)
self.pushButton_3.setGeometry(QRect(430, 290, 80, 23))
self.pushButton_3.setObjectName("保存目录")
self.pushButton_3.clicked.connect(self.open_dirpath)
self.pushButton_4 = QPushButton(self.centralwidget)
self.pushButton_4.setGeometry(QRect(430, 70, 80, 23))
self.pushButton_4.setCheckable(True)
self.pushButton_4.setObjectName("Size")
self.pushButton_4.clicked.connect(self.get_screen_area)
self.spinBox = QSpinBox(self.centralwidget)
self.spinBox.setValue(15)
self.spinBox.setRange(10, 60)
self.spinBox.setGeometry(QRect(430, 145, 80, 20))
self.spinBox.valueChanged.connect(self.screen_fps_mofify)
self.spinBox.setObjectName("spinBox")
self.label_2 = QLabel(self.centralwidget)
self.label_2.setGeometry(QRect(430, 50, 54, 16))
self.label_2.setTextFormat(Qt.AutoText)
self.label_2.setObjectName("选择区域")
self.label_3 = QLabel(self.centralwidget)
self.label_3.setGeometry(QRect(430, 125, 54, 16))
self.label_3.setObjectName("帧率")
self.label_4 = QLabel(self.centralwidget)
self.label_4.setGeometry(QRect(430, 240, 54, 16))
self.label_4.setObjectName("声道设置")
self.label_5 = QLabel(self.centralwidget)
self.label_5.setGeometry(QRect(430, 180, 54, 16))
self.label_5.setObjectName("音频来源")
self.setCentralWidget(self.centralwidget)
self.retranslateUi()
QMetaObject.connectSlotsByName(self)
def retranslateUi(self):
self.pushButton.setText("停止")
self.pushButton_2.setText("开始")
self.pushButton_3.setText("保存目录")
self.pushButton_4.setText("Size*Size")
self.checkbox.setText("录制鼠标")
self.label_1.setText("屏幕录制器")
self.label_2.setText("选择区域:")
self.label_3.setText("帧率:")
self.label_4.setText("声道设置:")
self.label_5.setText("音频来源:")
self.label_6.setText("F7开始/停止")
def keyPressEvent(self, event):
"""监测键盘是否按下F3如果已开始录制则停止否则开始录制"""
if event.key() == Qt.Key_F7:
if not self.hotkey_start:
self.start()
else:
self.stop()
def change_combox(self, event):
sendername = self.sender().objectName()
if sendername == '声道设置':
self.channels = 2 if event == 0 else 1
elif sendername == '音频来源':
if event == '':
self.record_audio = False
else:
self.record_audio = True
self.device_name = event
def get_screen_grab(self, crop_size):
# 获取屏幕截图并更新至self.label0中
screen = ImageGrab.grab().convert('RGBA')
screen = screen.crop(box=crop_size) # box=(left, upper, right, lower)
data = screen.tobytes("raw", "RGBA")
qim = QImage(data, screen.size[0], screen.size[1], QImage.Format_RGBA8888)
pix = QPixmap.fromImage(qim).scaled(self.label0.size(), aspectRatioMode=Qt.KeepAspectRatio)
pix.detach() # QPixmap为另开线程操作的避免程序崩溃使用detach方法使用一个QImage对象直到QPixmap对象销毁
self.label0.setPixmap(pix)
self.label0.repaint()
return screen.size
def get_screen_area(self):
# 获取截图区间
SizeScreen = MousePaint()
SizeScreen.exec_()
self.offset_x, self.offset_y = SizeScreen.lastpoint
end_x, end_y = SizeScreen.endpoint
self.screen_x, self.screen_y = self.get_screen_grab((self.offset_x, self.offset_y, end_x, end_y))
self.screenshot.config_modify(offset_x=self.offset_x, offset_y=self.offset_y,
width=self.screen_x, height=self.screen_y)
self.pushButton_4.setText(f'{self.screen_x}*{self.screen_y}')
def screen_fps_mofify(self, fps):
self.screenshot.config_modify(fps=fps)
def mouse_draw(self, checked):
draw_mouse = 1 if checked else 0
self.screenshot.config_modify(draw_mouse=draw_mouse)
def open_dirpath(self):
fdir = QFileDialog.getExistingDirectory(self, '选择目录', self._dir)
if not fdir:
return None
self._dir = fdir
self.set_filename()
def set_filename(self):
self.fname = f'{self._dir}/屏幕录制缓存_{int(datetime.now().replace(microsecond=0).timestamp())}'
self.screenshot.config_modify(filename=self.fname)
self.audio = AudioRecord()
self.setWindowTitle(f'屏幕录制 {self.fname}')
def start(self):
if self.hotkey_start:
return
if not self.fname:
QMessageBox.warning(self, '请选择保存目录!', '请选择保存目录!')
return
self.screenshot.start()
if self.record_audio:
self.audio.stop_flag = False
self.audio.run(filename=self.fname + '.mp3', name=self.device_name)
self.hotkey_start = True
def stop(self):
if not self.hotkey_start:
return
self.audio.stop_flag = True
self.hotkey_start = False
self.screenshot.stop()
if self.record_audio:
self.screenshot.compose()
else:
filename = self.fname + '.mp4'
target = filename.replace('缓存', '')
Path(filename).replace(target)
self.set_filename()
class MousePaint(QDialog):
"""移动鼠标获取屏幕捕获范围"""
def __init__(self):
super().__init__()
self.setMouseTracking(True)
# 设置窗口布满整个屏幕
self.showFullScreen()
# 设置窗体无边框
self.setWindowFlags(Qt.FramelessWindowHint) # 窗口置顶,无边框
# 设置背景透明
self.setWindowOpacity(0.5)
self.initUI()
self.setFocus()
def initUI(self):
self.setGeometry(*(QDesktopWidget().screenGeometry()).getRect())
self.pix = QPixmap()
self.lastpoint = QPoint()
self.endpoint = QPoint()
self.pos = None
self.bline = 0
def mousePressEvent(self, event):
# 监听鼠标按压事件
if event.button() == Qt.LeftButton:
self.lastpoint = event.x(), event.y()
self.bline = 1
elif event.button() == Qt.RightButton:
self.close()
event.accept()
def mouseReleaseEvent(self, event):
# 监听鼠标释放事件
self.endpoint = event.x(), event.y()
self.bline = 0
event.accept()
self.close()
def mouseMoveEvent(self, event):
# 监听鼠标移动事件
if self.bline == 1:
self.pos = event.x(), event.y()
event.accept()
self.update()
def paintEvent(self, event):
# 绘画事件
if self.bline == 1:
pp = QPainter(self)
pen = QPen() # 定义笔格式对象
pen.setWidth(5) # 设置笔的宽度
pen.setColor(QColor(255, 0, 0))
pp.setPen(pen)
lpx, lpy = self.lastpoint
pp.drawRect(lpx, lpy, self.pos[0] - lpx, self.pos[1] - lpy)
event.accept()
# def main():
# """运行函数"""
# # 将7zip压缩格式添加到shutil中
# shutil.register_archive_format('7zip', pack_7zarchive, description='7zip archive')
# shutil.register_unpack_format('7zip', ['.7z'], unpack_7zarchive, description='7zip archive')
# app = QApplication(sys.argv)
# app.setAttribute(Qt.AA_UseHighDpiPixmaps)
# ui = Ui_MainWindow1()
# sys.exit(app.exec_())
#if __name__ == '__main__':
# main()

@ -1,67 +0,0 @@
"""
使用ffmpeg录制屏幕并结合Pyaudio同步录制的音频合成带有声音的视频文件
"""
import sys
from pathlib import Path
from time import sleep
import ffmpeg
def file_path(filename):
relative_path = Path('resource').joinpath(filename)
if getattr(sys, 'frozen', False):
base_path = Path(sys.MEIPASS)
else:
base_path = Path('.').absolute()
return Path(base_path).joinpath(relative_path).as_posix()
class Screenshot:
def __init__(self, width=1920, height=1080, fps=15):
self.width = width
self.height = height
self.fps = fps
self.process = None
self.ffmpeg_path = file_path('ffmpeg.exe')
def __call__(self, width, height, fps=None):
self.width = width
self.height = height
self.fps = fps if fps else self.fps
@staticmethod
def unlink(filename):
Path(filename).unlink()
def record(self, filename, offset_x=0, offset_y=0, draw_mouse=0):
self.process = (
ffmpeg.output(
ffmpeg.input(
filename='desktop', format='gdigrab', framerate=self.fps, offset_x=offset_x, offset_y=offset_y,
draw_mouse=draw_mouse, s=f'{self.width}x{self.height}'),
filename=filename, pix_fmt='yuv420p'
).overwrite_output()
)
self.ffmpeg_async()
def compose_audio(self, video_path, audio_path, output_path):
self.process = (
ffmpeg.output(
ffmpeg.input(filename=video_path),
ffmpeg.input(filename=audio_path),
filename=output_path, vcodec='copy', acodec='aac', strict='experimental', pix_fmt='yuv420p'
).overwrite_output()
)
sleep(1)
self.ffmpeg_async()
def ffmpeg_async(self):
self.process = self.process.run_async(cmd=self.ffmpeg_path, pipe_stdin=True, pipe_stdout=False,
pipe_stderr=False)
def terminate(self):
if self.process is not None:
self.process.communicate(str.encode("q"))
self.process.terminate()
self.process = None

@ -1,151 +0,0 @@
import time
from pathlib import Path
from threading import Thread
import cv2
import numpy as np
from PIL import ImageGrab
from numba import jit
from pynput import keyboard
@jit(nopython=True)
def average_n(x, y):
"""Numpy计算趋近值"""
return ((x + y + y) // 3).astype(x.dtype)
class ScreenshotVideo(Thread):
def __init__(self, width, high, path='', fps=15):
"""初始化参数"""
super().__init__()
self.save_file = path
self.best_fps = fps
self.fps = fps
self.width = width
self.high = high
self.spend_time = 1
self.flag = False
self.kill = False
self.video = None
def __call__(self, path):
"""重载视频路径,便于类的二次调用"""
self.save_file = Path(path)
self.video = self.init_videowriter(self.save_file)
@staticmethod
def screenshot():
"""静态方法屏幕截图并转换为np.array数组"""
return np.array(ImageGrab.grab())
@staticmethod
def get_fourcc(name):
"""视频编码字典"""
fourcc_maps = {'.avi': 'I420',
'.m4v': 'mp4v',
'.mp4': 'avc1',
'.ogv': 'THEO',
'.flv': 'FLV1',
}
return fourcc_maps.get(name)
def init_videowriter(self, path):
"""获取视频编码并新建视频文件"""
if not path:
raise Exception('视频路径未设置,请设置\nvideo = ScreenshotVideo(fps,width,high)\nvideo = video(video_path)')
path = Path(path) if isinstance(path, str) else path
fourcc = cv2.VideoWriter_fourcc(*self.get_fourcc(path.suffix))
return cv2.VideoWriter(path.as_posix(), fourcc, self.fps, (self.width, self.high))
def video_record_doing(self, img):
"""将BGR数组转换为RGB数组"""
im_cv = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
self.video.write(im_cv)
def video_record_end(self):
"""录制结束,根据条件判断文件是否保存"""
self.video.release()
cv2.destroyAllWindows()
if self.save_file and self.kill:
Path(self.save_file).unlink()
def video_best_fps(self, path):
"""获取电脑录制视频的最优帧率"""
video = cv2.VideoCapture(path)
fps = video.get(cv2.CAP_PROP_FPS)
count = video.get(cv2.CAP_PROP_FRAME_COUNT)
self.best_fps = int(fps * ((int(count) / fps) / self.spend_time))
video.release()
def pre_video_record(self):
"""预录制以获取最佳fps值"""
self.video = self.init_videowriter('test.mp4')
start_time = time.time()
for _ in range(10):
im = self.screenshot()
self.video_record_doing(im)
self.spend_time = round(time.time() - start_time, 4)
self.video_record_end()
time.sleep(2)
self.video_best_fps('test.mp4')
Path('test.mp4').unlink()
def insert_frame_array(self, frame_list):
"""Numpy增强截图信息"""
fps_n = round(self.fps / self.best_fps)
if fps_n <= 0:
return frame_list
times = int(np.log2(fps_n)) # 倍率
for _ in range(times):
frame_list2 = map(average_n, [frame_list[0]] + frame_list[:-1], frame_list)
frame_list = [[x, y] for x, y in zip(frame_list2, frame_list)]
frame_list = [j for i in frame_list for j in i]
return frame_list
def frame2video_run(self):
"""使用opencv将连续型截图转换为视频"""
self.video = self.init_videowriter(self.save_file)
start_time = time.time()
frame_list = []
while True:
frame_list.append(self.screenshot())
if self.flag:
break
self.spend_time = round(time.time() - start_time, 4)
if not self.kill: # 视频录制不被终止将逐帧处理图像
frame_list = self.insert_frame_array(frame_list)
for im in frame_list:
self.video_record_doing(im)
self.video_record_end()
def hotkey(self):
"""热键监听"""
with keyboard.Listener(on_press=self.on_press) as listener:
listener.join()
def on_press(self, key):
try:
if key.char == 't': # 录屏结束,保存视频
self.flag = True
elif key.char == 'k': # 录屏中止,删除文件
self.flag = True
self.kill = True
except Exception as e:
print(e)
def run(self):
# 运行函数
# 设置守护线程
Thread(target=self.hotkey, daemon=True).start()
# 运行截图函数
self.frame2video_run()
screen = ImageGrab.grab()
width, high = screen.size
video = ScreenshotVideo(width, high, fps=60)
video.pre_video_record() # 预录制获取最优fps
video('test1.mp4')
video.run()

@ -1,32 +0,0 @@
from threading import Thread
from pynput import keyboard # pip install pynput
from Audio_record import AudioRecord
from Screenshot_record import Screenshot
def hotkey():
"""热键监听"""
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
def on_press(key):
try:
video.terminate()
if key.char == 't': # t键录制结束保存音视频
audio.stop_flag = True
elif key.char == 'k': # k键录制中止删除文件
audio.stop_flag = True
audio.kill = True
video.unlink('test.mp4')
except Exception as e:
print(e)
key_thread = Thread(target=hotkey, daemon=True)
audio = AudioRecord()
video = Screenshot()
key_thread.start()
video.record('test.mp4')

@ -1,9 +0,0 @@
ffmpeg-python==0.2.0
numba==0.54.1
numpy==1.20.3
opencv-python==4.5.4.60
Pillow==8.4.0
PyAudio
pynput==1.7.5
PyQt5==5.15.4
py7zr==0.18.4

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

@ -0,0 +1,171 @@
# _*_ coding:utf-8 _*_
import shutil
from PIL import ImageGrab
from PyQt5.QtCore import QCoreApplication, Qt, qAbs, QRect, QPoint
from PyQt5.QtGui import QPen, QPainter, QIcon, QFont, QColor, QPixmap
from PyQt5.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout, QCheckBox, QMessageBox, QFileDialog, QDialog, \
QApplication, QWidget, QDesktopWidget
from base import Base
from config import ABSOLUTE_PATH
from screen_record_thread import ScreenRecordThread, SaveMp4Thread
class ScreenBar(Base):
def __init__(self):
super(ScreenBar, self).__init__()
self.box = QVBoxLayout()
self.btn_box = QHBoxLayout()
self.tip_label = QLabel(' 屏幕录制 ')
self.is_record_full = QCheckBox()
self.open_btn = QPushButton()
self.start_btn = QPushButton()
self.end_btn = QPushButton()
self.close_btn = QPushButton()
self.recording_thread = ScreenRecordThread()
self.offset_x = 0
self.offset_y = 0
self.end_x = 0
self.end_y = 0
self.bind()
self.set_style()
def set_style(self):
self.start_btn.setEnabled(False)
self.end_btn.setEnabled(False)
self.start_btn.setIcon(QIcon('screenshot_png/start.png'))
self.end_btn.setIcon(QIcon('screenshot_png/stop.png'))
self.close_btn.setIcon(QIcon('screenshot_png/close.png'))
self.is_record_full.setIcon(QIcon('screenshot_png/full.png'))
self.open_btn.setIcon(QIcon('screenshot_png/rect.png'))
self.btn_box.addWidget(self.is_record_full, 0)
self.btn_box.addWidget(self.open_btn, 0)
self.btn_box.addWidget(self.start_btn, 0)
self.btn_box.addWidget(self.end_btn, 0)
self.btn_box.addWidget(self.close_btn, 0)
self.box.addWidget(self.tip_label)
self.box.addLayout(self.btn_box)
self.box.setContentsMargins(0, 0, 0, 0)
self.setWindowOpacity(0.7)
self.frameGeometry()
self.move(QApplication.desktop().frameGeometry().width() - 300,
QApplication.desktop().frameGeometry().height() - 150)
self.setLayout(self.box)
def thread_trigger_signal(self, list, fps, gif_list):
def show_success_message(str):
QMessageBox.information(self, "提示", str, QMessageBox.Yes)
self.end_btn.setEnabled(False)
self.open_btn.setEnabled(True)
self.is_record_full.setEnabled(True)
path, tmp = QFileDialog.getSaveFileName(self,
"文件保存",
'%s/*.mp4' % ABSOLUTE_PATH,
"All Files (*);")
if not '' == path:
if not path.endswith(".mp4"):
path = path + ".mp4"
self.save_mp4_thread = SaveMp4Thread(list, fps, path, gif_list)
self.save_mp4_thread.trigger.connect(lambda: show_success_message("保存成功"))
self.save_mp4_thread.start()
else:
show_success_message("取消保存")
shutil.rmtree('temp')
def bind(self):
def open_signal():
SizeScreen = MousePaint()
SizeScreen.exec_()
self.offset_x, self.offset_y = SizeScreen.lastpoint
self.end_x, self.end_y = SizeScreen.endpoint
self.start_btn.setEnabled(True)
self.is_record_full.setEnabled(False)
def start_signal():
self.open_btn.setEnabled(False)
self.start_btn.setEnabled(False)
self.end_btn.setEnabled(True)
self.recording_thread.recording = True
if self.is_record_full.isChecked():
self.recording_thread.area = None
self.recording_thread.start()
else:
self.recording_thread.area = (self.offset_x, self.offset_y, self.end_x, self.end_y)
self.recording_thread.start()
def end_signal():
self.recording_thread.recording = False
def record_full_signal():
if self.is_record_full.isChecked():
self.open_btn.setEnabled(False)
self.start_btn.setEnabled(True)
else:
self.open_btn.setEnabled(True)
self.start_btn.setEnabled(False)
self.open_btn.clicked.connect(open_signal)
self.start_btn.clicked.connect(start_signal)
self.end_btn.clicked.connect(end_signal)
self.close_btn.clicked.connect(lambda: self.close())
self.is_record_full.stateChanged.connect(record_full_signal)
self.recording_thread.trigger.connect(self.thread_trigger_signal)
class MousePaint(QDialog):
"""移动鼠标获取屏幕捕获范围"""
def __init__(self):
super().__init__()
self.setMouseTracking(True)
# 设置窗口布满整个屏幕
self.showFullScreen()
# 设置窗体无边框
self.setWindowFlags(Qt.FramelessWindowHint) # 窗口置顶,无边框
# 设置背景透明
self.setWindowOpacity(0.5)
self.initUI()
self.setFocus()
def initUI(self):
self.setGeometry(*(QDesktopWidget().screenGeometry()).getRect())
self.pix = QPixmap()
self.lastpoint = QPoint()
self.endpoint = QPoint()
self.pos = None
self.bline = 0
def mousePressEvent(self, event):
# 监听鼠标按压事件
if event.button() == Qt.LeftButton:
self.lastpoint = event.x(), event.y()
self.bline = 1
elif event.button() == Qt.RightButton:
self.close()
event.accept()
def mouseReleaseEvent(self, event):
# 监听鼠标释放事件
self.endpoint = event.x(), event.y()
self.bline = 0
event.accept()
self.close()
def mouseMoveEvent(self, event):
# 监听鼠标移动事件
if self.bline == 1:
self.pos = event.x(), event.y()
event.accept()
self.update()
def paintEvent(self, event):
# 绘画事件
if self.bline == 1:
pp = QPainter(self)
pen = QPen() # 定义笔格式对象
pen.setWidth(5) # 设置笔的宽度
pen.setColor(QColor(255, 0, 0))
pp.setPen(pen)
lpx, lpy = self.lastpoint
pp.drawRect(lpx, lpy, self.pos[0] - lpx, self.pos[1] - lpy)
event.accept()

@ -0,0 +1,23 @@
# _*_ coding:utf-8 _*_
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication
class Base(QWidget):
def __init__(self):
super(Base, self).__init__()
self.mMoveing = True
self.mMovePosition = self.pos()
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
def mousePressEvent(self, QMouseEvent):
self.mMoveing = True
self.mMovePosition = QMouseEvent.globalPos() - self.pos()
return super().mousePressEvent(QMouseEvent)
def mouseMoveEvent(self, QMouseEvent):
if self.mMoveing and (QMouseEvent.buttons() and Qt.LeftButton) and (
QMouseEvent.globalPos() - self.mMovePosition).manhattanLength() > QApplication.startDragDistance():
self.move(QMouseEvent.globalPos() - self.mMovePosition)
self.mMovePosition = QMouseEvent.globalPos() - self.pos()
return super().mouseMoveEvent(QMouseEvent)

@ -0,0 +1,6 @@
# _*_ coding:utf-8 _*_
import os
ABSOLUTE_PATH = os.path.dirname(os.path.realpath(__file__)).replace('\\', '/')

@ -0,0 +1,71 @@
# _*_ coding:utf-8 _*_
import time
import os
import shutil
import win32gui
from PIL import ImageGrab, Image
from PyQt5.QtCore import QThread, pyqtSignal
from moviepy.video.io.ImageSequenceClip import ImageSequenceClip
class ScreenRecordThread(QThread):
trigger = pyqtSignal(list, int, list)
def __init__(self):
super(ScreenRecordThread, self).__init__()
self.recording = True
self.area = None
self.save_path = ''
self.is_pause_for_save = True
self.recordTime = 0
self.image_list = [] # mp4 list
self.image_gif_list = [] # gif list
def run(self):
if not os.path.exists('temp'):
os.mkdir('temp')
else:
shutil.rmtree('temp')
os.mkdir('temp')
self.is_pause_for_save = True
self.recording = True
self.recordTime = 0
self.image_list = []
t = time.time()
imCursor = Image.open('screenshot_png/cursor.png')
self.image_gif_list = []
while self.recording:
curX, curY = win32gui.GetCursorPos()
if self.area is None:
image = ImageGrab.grab()
image.paste(imCursor, box=(curX, curY), mask=imCursor)
else:
image = ImageGrab.grab(self.area)
image.paste(imCursor, box=(curX - self.area[0], curY - self.area[1]), mask=imCursor)
imageName = os.path.join('temp', '%s.jpg' % int(time.time() * 1e3))
image.save(imageName)
self.image_list.append(imageName)
self.recordTime = time.time() - t
fps = len(self.image_list) / self.recordTime
self.trigger.emit(self.image_list, fps, self.image_gif_list)
class SaveMp4Thread(QThread):
trigger = pyqtSignal()
def __init__(self, list, fps, path, gif_list):
super(SaveMp4Thread, self).__init__()
self.fps = fps
self.path = path
self.list = list
self.gif_list = gif_list
def run(self):
clip = ImageSequenceClip(self.list, fps=self.fps)
clip.write_videofile(self.path) # to video
self.trigger.emit()
shutil.rmtree('temp')

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Loading…
Cancel
Save