diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index b87cfc9..0000000 --- a/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -E:\181115\Tello-Python -$ cat ./.gitattributes -*.bat -text \ No newline at end of file diff --git a/.gitignore b/.gitignore index d6bb898..b1246cc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.DS_Store *.dat *.caffemodel +.venv/ +__pycache__/ \ No newline at end of file diff --git a/Single_Tello_Test/stats.py b/Single_Tello_Test/stats.py index ca2ab4e..0a198c3 100644 --- a/Single_Tello_Test/stats.py +++ b/Single_Tello_Test/stats.py @@ -21,12 +21,12 @@ def get_duration(self): return diff.total_seconds() def print_stats(self): - print '\nid: %s' % self.id - print 'command: %s' % self.command - print 'response: %s' % self.response - print 'start time: %s' % self.start_time - print 'end_time: %s' % self.end_time - print 'duration: %s\n' % self.duration + print('\nid: %s' % self.id) + print('command: %s' % self.command) + print('response: %s' % self.response) + print('start time: %s' % self.start_time) + print('end_time: %s' % self.end_time) + print('duration: %s\n' % self.duration) def got_response(self): if self.response is None: diff --git a/Single_Tello_Test/tello.py b/Single_Tello_Test/tello.py index b69563e..a38c056 100644 --- a/Single_Tello_Test/tello.py +++ b/Single_Tello_Test/tello.py @@ -35,18 +35,18 @@ def send_command(self, command): self.log.append(Stats(command, len(self.log))) self.socket.sendto(command.encode('utf-8'), self.tello_adderss) - print 'sending command: %s to %s' % (command, self.tello_ip) + print('sending command: %s to %s' % (command, self.tello_ip)) start = time.time() while not self.log[-1].got_response(): now = time.time() diff = now - start if diff > self.MAX_TIME_OUT: - print 'Max timeout exceeded... command %s' % command + print('Max timeout exceeded... command %s' % command) # TODO: is timeout considered failure or next command still get executed # now, next one got executed return - print 'Done!!! sent command: %s to %s' % (command, self.tello_ip) + print('Done!!! sent command: %s to %s' % (command, self.tello_ip)) def _receive_thread(self): """Listen to responses from the Tello. @@ -57,11 +57,11 @@ def _receive_thread(self): while True: try: self.response, ip = self.socket.recvfrom(1024) - print('from %s: %s' % (ip, self.response)) + print(('from %s: %s' % (ip, self.response))) self.log[-1].add_response(self.response) - except socket.error, exc: - print "Caught exception socket.error : %s" % exc + except socket.error as exc: + print("Caught exception socket.error : %s" % exc) def on_close(self): pass diff --git a/Single_Tello_Test/tello_test.py b/Single_Tello_Test/tello_test.py index 1692ccc..d7e7265 100644 --- a/Single_Tello_Test/tello_test.py +++ b/Single_Tello_Test/tello_test.py @@ -17,7 +17,7 @@ if command.find('delay') != -1: sec = float(command.partition('delay')[2]) - print 'delay %s' % sec + print('delay %s' % sec) time.sleep(sec) pass else: diff --git a/TelloPython_FAQ(CH).txt b/TelloPython_FAQ(CH).txt deleted file mode 100644 index e09a3c8..0000000 --- a/TelloPython_FAQ(CH).txt +++ /dev/null @@ -1,96 +0,0 @@ -FAQ about Tello-Python (常见问题解答) - -Q1:在windows上,运行tello_video需要哪些依赖项/怎么手动? - -A1:在windows上,运行tello_video,首先,需要下载python2.7, - a.python2.7下载链接: - win64: https://www.python.org/ftp/python/2.7.15/python-2.7.15.amd64.msi - win32: https://www.python.org/ftp/python/2.7.15/python-2.7.15.msi - - b.然后可以通过pip安装四个依赖项,分别是: (pip的安装方式请自行google) - numpy,opencv-python,pillow,matplotib。安装指令分别为: - numpy: python -m pip install numpy - matplotlib: python -m pip install matplotlib - opencv-python: python -m pip install -v opencv-python==3.4.2.17 - pillow: python -m pip install pillow - - c.接下来,需要下载并安装boost,目的是获得一个叫boost_python27-vc120-mt-x**-1_68.dll的动态链接库 - boost下载链接: - win64: https://nchc.dl.sourceforge.net/project/boost/boost-binaries/1.68.0/boost_1_68_0-msvc-12.0-64.exe - win32: https://excellmedia.dl.sourceforge.net/project/boost/boost-binaries/1.68.0/boost_1_68_0-msvc-12.0-32.exe - 下载完毕之后双击安装包进行安装,安装完成后,找到boost_python27-vc120-mt-x**-1_68.dll文件('**'部分取决你的电脑位数),放置到.\Python27\Lib\site-packages路径下。 - - d.接下来,需要下载ffmpeg,目的是得到包括avcodec-58.dll在内的多个dll文件。 - ffmpeg下载链接: - win64: https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-20180825-844ff49-win64-shared.zip - win32: https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-20180825-844ff49-win32-shared.zip - 下载完毕之后,解压文件,并将./bin 路径下的所有.dll文件都放置到.\Python27\Lib\site-packages路径下。 - - e.接下来,需要下载vs2013编译器, - vs2013编译器下载链接: - win64: https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe - win32: https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x86.exe - 下载完毕后,运行安装包,安装程序。 - - f.将Tello-Python程序包中的libh264decoder复制到.\Python27\Lib\site-packages路径下。 - h264decoder.pyd路径: - win64: .\Tello-Python\Tello_Video\h264decoder\Windows\x64\libh264decoder.pyd - win32: .\Tello-Python\Tello_Video\h264decoder\Windows\x86\libh264decoder.pyd - - 最后,在最新提交的commit中,提供了一个tello_video_dll.zip的压缩包(仅适用于win64),里面包括所需的与boost,ffmpeg,libh264decoder,vs2013编译器相关的文件。 - 首先,双击运行vcredist_x64.exe(vs2013编译器的安装包)进行安装。其次,将文件夹中的剩余文件都复制到.\Python27\Lib\site-packages路径下。 - (通过该zip,你可以省去在网上下载和部分安装的环节,但注意这个zip里面的文件仅适用于win64!另外,像numpy,matplotlib,pillow和opencv-python你仍需要用pip进行安装。) - - -Q2:windows上的一键安装脚本(install.bat)在我的电脑上无法正常运行。 - -A2:目前提供的install.bat由于包含MD5校验等环节,对于部分用户电脑不是很兼容,后续会提供一个稳定安装的版本。如果无法正常运行自动安装脚本,请参考 - Q1中的手动安装依赖项的方式。 - - -Q3:install_manual.bat有什么作用? - -A3:该脚本与install.bat在完成的任务上并无区别,区别主要在于会在每一个安装项目前进行中断,并提示用户是否安装。 - 目前该脚本并不稳定,建议您适用install.bat或者采用Q1中的手动安装的方式。 - - -Q4:在windows/linux/mac中,我的所有依赖项均已经全部安装完成,运行python main.py之后也没有发生报错,但是运行程序后GUI并没有弹出视频流的画面,也无法使用 - Snapshot按钮进行拍照。 - -A4:正常情况下,在已经连接tello wifi的情况下,运行程序后GUI会弹出视频流的播放窗口,并且可以正常使用snapshot按钮进行拍照,如果没有正常运行(实际效果参照https://www.youtube.com/watch?v=kcXN7CYgQ0g), - 请考虑并检查以下几种情况: - a.你的PC设备是否已经正常连接到你的tello 的wifi; - b.检查你的PC的网络防火墙,可能防火墙会对视频流数据进行拦截; - c.进一步检查你的网络设置,因为tello是对PC的11111端口进行视频流数据的推送的,所以请检查你的网络设置中是否禁用了11111UDP端口,或者对11111端口的UDP数据进行了拦截。 - 如果有,请取消禁用; - d.极少数情况下,可能是PC发送的'streamon'指令发生丢包,没有被tello接收,针对这种情况,请尝试重新运行程序。 - - -Q5:运行程序后,无法正常使用command panel中的按键响应功能。 - -A5:请确认鼠标不要点击command panel外的地方,如果点击了,请用鼠标重新点击command panel界面,再尝试按键响应功能。 - - -Q6:tello EDU版本是否提供python 版本的多机编队例程? - -A6:请参考https://github.com/TelloSDK/Multi-Tello-Formation,并阅读其README.md文档,此外,该例程内提供了可直接执行文件版本(.exe), - 请参照Tello-Swarm中的userguide使用。 - -Q7:libh264decoder的作用是什么? - -A7:libh264decoder是根据.\Tello-Python\Tello_Video\h264decoder文件夹中的h264decoder.cpp,h264decoder.hpp和h264decoder_python.cpp生成的 - 动态链接库,其作用是对tello向PC设备传输的h264编码格式的视频流进行解码。在windows上是以libh264decoder.pyd的形式体现,在linux和macos - 上以libh264decoder.so的形式体现。 - Windows/Linux/Mac上的libh264decoder动态链接库均提供在.\Tello-Python\Tello_Video\h264decoder路径下。 - 在Linux和Mac的一键脚本安装过程中,libh264decoder.so通过源码实时编译生成。 - 在Windows的一键脚本安装过程中,libh264decoder.pyd直接从\Tello_Video\h264decoder\Windows中复制,并被放置到.\Python27\Lib\site-packages中。 - -Q8:Tello EDU能否在station模式下对外传输视频流(对Tello EDU 使用了ap 'ssid' 'password'指令)? - -A8:目前该功能并不被支持。 - -Q9:tello SDK接口所支持的UDP通信的频段和信道? - -A9:目前只支持2.4GHz,支持的信道包括0-11。 - -如果有其他问题,欢迎提问或补充! \ No newline at end of file diff --git a/TelloPython_FAQ.txt b/TelloPython_FAQ.txt deleted file mode 100644 index 98d30cc..0000000 --- a/TelloPython_FAQ.txt +++ /dev/null @@ -1,104 +0,0 @@ -FAQ about Tello-Python - -Q1:On windows, what dependencies are needed to run tello_video/how to install it manually? - -A1:On windows, to run tello_video, first, you need to download python2.7, - a.Python2.7 download link: - win64: https://www.python.org/ftp/python/2.7.15/python-2.7.15.amd64.msi - win32: https://www.python.org/ftp/python/2.7.15/python-2.7.15.msi - - b.Then you can install these four dependencies through pip, ('way to install pip' please google by yourself) - numpy,opencv-python,pillow,matplotib。The installation instructions are: - numpy: python -m pip install numpy - matplotlib: python -m pip install matplotlib - opencv-python: python -m pip install -v opencv-python==3.4.2.17 - pillow: python -m pip install pillow - - c.Next, you need to download and install boost in order to get a dynamic link library called boost_python27-vc120-mt-x**-1_68.dll - boost download link: - win64: https://nchc.dl.sourceforge.net/project/boost/boost-binaries/1.68.0/boost_1_68_0-msvc-12.0-64.exe - win32: https://excellmedia.dl.sourceforge.net/project/boost/boost-binaries/1.68.0/boost_1_68_0-msvc-12.0-32.exe - After the download is complete, double-click the installation package to install. After the installation is complete, find the - boost_python27-vc120-mt-x**-1_68.dll file (the '**' depends on the number of your windows system) and place it in - .\Python27\Lib\site-packages path. - - d.Next, you need to download ffmpeg, the purpose is to get multiple dll files including avcodec-58.dll,etc. - ffmpeg download link: - win64: https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-20180825-844ff49-win64-shared.zip - win32: https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-20180825-844ff49-win32-shared.zip - After the download is complete, extract the files,enter ./bin directory and place all the .dll files under the .\Python27\Lib\site-packages path. - - e.Next, you need to download the vs2013 compiler. - vs2013 compiler download link: - win64: https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe - win32: https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x86.exe - After the download is complete, run the installation package and install. - - f.Finally Copy the libh264decoder from the Tello-Python package to the .\Python27\Lib\site-packages directory. - h264decoder.pyd path: - win64: .\Tello-Python\Tello_Video\h264decoder\Windows\x64\libh264decoder.pyd - win32: .\Tello-Python\Tello_Video\h264decoder\Windows\x86\libh264decoder.pyd - - In the latest commit, a zip named 'tello_video_dll.zip' is provided (for win64 only), which includes the required files related - to boost, ffmpeg, libh264decoder, vs2013 compiler.If you want to install it: - First, double-click to run vcredist_x64.exe (vs2013 compiler installation package) to install.Second, copy the remaining files in the folder - to the C:\Python27\Lib\site-packages path. - (With this zip, you can avoid the online download and partial installation, but note that the files in this zip are only available for win64! - Also, like numpy, matplotlib, pillow and opencv-python you still need to install with pip by yourself.) - - -Q2:The one-click installation script (install.bat) on windows does not work properly on my computer. - -A2:At present, the install.bat that we provide contains steps such as MD5 checksum, which is not very compatible with some users' computers. A stable installation version will be provided later. - If the one-click installation script does not work properly, please refer to the manual installation of dependencies in Q1&A1. - - -Q3:What does install_manual.bat do? What is the difference with install.bat? - -A3:This script is no different from install.bat in the completed tasks. The main difference is that it will be interrupted before each installation project, - and prompt the user to install. - Currently the script is not stable. It is recommended that you apply install.bat or use the manual installation method in Q1&A1. - - -Q4:In windows/linux/mac, all my dependencies have been installed, and no error has occurred after running python main.py.However, after running - the program, the GUI does not pop up the video stream, and I cannot use the Snapshot button to take a photo. - -A4:Under normal circumstances, when tello wifi is connected, the GUI will pop up the playback window of the video stream after running the program, and the snapshot button can - be used to take pictures.If it is not working properly (see https://www.youtube.com/watch?v=kcXN7CYgQ0g for actual results), - Please consider and check the following situations: - a.Whether your PC device is properly connected to your tello wifi; - b.Check your PC's network firewall, which may block video stream data; - c.Further check your network settings, because tello is pushing the video stream data to the 11111 port of your PC, so please check if the 11111 UDP port is disabled in - your PC network settings, or the UDP data of port 11111 is blocked. If there is, please cancel it; - d.In rare cases, it is possible that the 'streamon' command sent by the PC has been lost and not received by the tello. In this case, try to run the program again. - - -Q5:After running the program, the keyboard control function in the command panel cannot be used normally. - -A5:Please make sure that the mouse does not click outside the command panel. If you click it, please click the command panel again with the mouse and - retry the keyboard control function. - - -Q6:Will you provide a python version sample code of the multi-tello swarm for tello EDU version? - -A6:Please refer to https://github.com/TelloSDK/Multi-Tello-Formation and read its README.md documentation.In addition, the program provides a direct executable file version (.exe). - Please refer to the userguide in Tello-Swarm.zip for use. - -Q7:What is the role of libh264decoder? - -A7:Libh264decoder is a dynamic link library (dll)compiled from h264decoder.cpp, h264decoder.hpp and h264decoder_python.cpp in the .\Tello-Python\Tello_Video\h264decoder folder. - ,its function is to decode the video stream of the h264 encoded format transmitted by the tello to the PC device.On windows, it exists in the form of libh264decoder.pyd, - and in linux and macos,it exists in the form of libh264decoder.so.The libh264decoder dynamic link library on Windows,Linux and Mac is available in the - .\Tello-Python\Tello_Video\h264decoder path. In the one-click script installation process for Linux and Mac, libh264decoder.so is compiled and generated in real time through - source code.During the one-click script installation process for Windows, libh264decoder.pyd is copied directly from \Tello_Video\h264decoder\Windows and placed in - .\Python27\Lib\site-packages. - -Q8:Can Tello EDU transmit video streams externally in station mode (send ap 'ssid' 'password' command for Tello EDU)? - -A8:This feature is currently not supported. - -Q9:What are the bands and channels of wifi UDP communication supported by the tello SDK interface? - -A9:Currently only supports 2.4GHz, and the supported channels include 0-11. - -If you have other questions, please ask questions or add! \ No newline at end of file diff --git a/Tello_Video/h264decoder/CMakeLists.txt b/Tello_Video/h264decoder/CMakeLists.txt deleted file mode 100644 index 98a924d..0000000 --- a/Tello_Video/h264decoder/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -project(python_h264decoder) - -set(Python_ADDITIONAL_VERSIONS 2.7) - -if(UNIX AND NOT APPLE) - set(LINUX TRUE) -endif() - -if(APPLE) - set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") - find_package(Boost REQUIRED COMPONENTS python27) -elseif(LINUX) - find_package(Boost REQUIRED COMPONENTS "python") -endif(APPLE) - - - -find_package(PythonInterp 2.7 REQUIRED) -find_package(PythonLibs 2.7 REQUIRED ) - - -include_directories(${PYTHON_INCLUDE_DIRS}) -include_directories(${Boost_INCLUDE_DIRS}) -link_directories(${Boost_LIBRARY_DIRS}) - -add_compile_options ("-std=c++0x") - -add_library(h264decoder SHARED h264decoder.cpp h264decoder_python.cpp) - -if(APPLE) - target_link_libraries(h264decoder avcodec swscale avutil ${Boost_LIBRARIES} ${Boost_PYTHON_LIBRARY_RELEASE} ${PYTHON_LIBRARIES}) -elseif(LINUX) - target_link_libraries(h264decoder avcodec swscale avutil ${Boost_PYTHON_LIBRARY_RELEASE} ${PYTHON_LIBRARIES}) -endif(APPLE) - -add_custom_command(TARGET h264decoder POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_BINARY_DIR}/libh264decoder.so ${CMAKE_SOURCE_DIR}/libh264decoder.so) -install(TARGETS h264decoder LIBRARY DESTINATION .) diff --git a/Tello_Video/h264decoder/Linux/libh264decoder.so b/Tello_Video/h264decoder/Linux/libh264decoder.so deleted file mode 100755 index 5c51abd..0000000 Binary files a/Tello_Video/h264decoder/Linux/libh264decoder.so and /dev/null differ diff --git a/Tello_Video/h264decoder/Mac/libh264decoder.so b/Tello_Video/h264decoder/Mac/libh264decoder.so deleted file mode 100644 index e9d5d30..0000000 Binary files a/Tello_Video/h264decoder/Mac/libh264decoder.so and /dev/null differ diff --git a/Tello_Video/h264decoder/Windows/x64/libh264decoder.pyd b/Tello_Video/h264decoder/Windows/x64/libh264decoder.pyd deleted file mode 100644 index 9772c49..0000000 Binary files a/Tello_Video/h264decoder/Windows/x64/libh264decoder.pyd and /dev/null differ diff --git a/Tello_Video/h264decoder/Windows/x86/libh264decoder.pyd b/Tello_Video/h264decoder/Windows/x86/libh264decoder.pyd deleted file mode 100644 index c153cad..0000000 Binary files a/Tello_Video/h264decoder/Windows/x86/libh264decoder.pyd and /dev/null differ diff --git a/Tello_Video/h264decoder/h264decoder.cpp b/Tello_Video/h264decoder/h264decoder.cpp deleted file mode 100644 index fac80ad..0000000 --- a/Tello_Video/h264decoder/h264decoder.cpp +++ /dev/null @@ -1,173 +0,0 @@ -extern "C" { -#include -#include -#include -#include -} - -#ifndef PIX_FMT_RGB24 -#define PIX_FMT_RGB24 AV_PIX_FMT_RGB24 -#endif - -#ifndef CODEC_CAP_TRUNCATED -#define CODEC_CAP_TRUNCATED AV_CODEC_CAP_TRUNCATED -#endif - -#ifndef CODEC_FLAG_TRUNCATED -#define CODEC_FLAG_TRUNCATED AV_CODEC_FLAG_TRUNCATED -#endif - -#include "h264decoder.hpp" -#include - -typedef unsigned char ubyte; - -/* For backward compatibility with release 9 or so of libav */ -#if (LIBAVCODEC_VERSION_MAJOR <= 54) -# define av_frame_alloc avcodec_alloc_frame -# define av_frame_free avcodec_free_frame -#endif - - -H264Decoder::H264Decoder() -{ - avcodec_register_all(); - - codec = avcodec_find_decoder(AV_CODEC_ID_H264); - if (!codec) - throw H264InitFailure("cannot find decoder"); - - context = avcodec_alloc_context3(codec); - if (!context) - throw H264InitFailure("cannot allocate context"); - - if(codec->capabilities & CODEC_CAP_TRUNCATED) { - context->flags |= CODEC_FLAG_TRUNCATED; - } - - int err = avcodec_open2(context, codec, nullptr); - if (err < 0) - throw H264InitFailure("cannot open context"); - - parser = av_parser_init(AV_CODEC_ID_H264); - if (!parser) - throw H264InitFailure("cannot init parser"); - - frame = av_frame_alloc(); - if (!frame) - throw H264InitFailure("cannot allocate frame"); - -#if 1 - pkt = new AVPacket; - if (!pkt) - throw H264InitFailure("cannot allocate packet"); - av_init_packet(pkt); -#endif -} - - -H264Decoder::~H264Decoder() -{ - av_parser_close(parser); - avcodec_close(context); - av_free(context); - av_frame_free(&frame); -#if 1 - delete pkt; -#endif -} - - -ssize_t H264Decoder::parse(const ubyte* in_data, ssize_t in_size) -{ - auto nread = av_parser_parse2(parser, context, &pkt->data, &pkt->size, - in_data, in_size, - 0, 0, AV_NOPTS_VALUE); - return nread; -} - - -bool H264Decoder::is_frame_available() const -{ - return pkt->size > 0; -} - - -const AVFrame& H264Decoder::decode_frame() -{ - int got_picture = 0; - int nread = avcodec_decode_video2(context, frame, &got_picture, pkt); - if (nread < 0 || got_picture == 0) - throw H264DecodeFailure("error decoding frame\n"); - return *frame; -} - - -ConverterRGB24::ConverterRGB24() -{ - framergb = av_frame_alloc(); - if (!framergb) - throw H264DecodeFailure("cannot allocate frame"); - context = nullptr; -} - -ConverterRGB24::~ConverterRGB24() -{ - sws_freeContext(context); - av_frame_free(&framergb); -} - - -const AVFrame& ConverterRGB24::convert(const AVFrame &frame, ubyte* out_rgb) -{ - int w = frame.width; - int h = frame.height; - int pix_fmt = frame.format; - - context = sws_getCachedContext(context, - w, h, (AVPixelFormat)pix_fmt, - w, h, PIX_FMT_RGB24, SWS_BILINEAR, - nullptr, nullptr, nullptr); - if (!context) - throw H264DecodeFailure("cannot allocate context"); - - // Setup framergb with out_rgb as external buffer. Also say that we want RGB24 output. - avpicture_fill((AVPicture*)framergb, out_rgb, PIX_FMT_RGB24, w, h); - // Do the conversion. - sws_scale(context, frame.data, frame.linesize, 0, h, - framergb->data, framergb->linesize); - framergb->width = w; - framergb->height = h; - return *framergb; -} - -/* -Determine required size of framebuffer. - -avpicture_get_size is used in http://dranger.com/ffmpeg/tutorial01.html -to do this. However, avpicture_get_size returns the size of a compact -representation, without padding bytes. Since we use avpicture_fill to -fill the buffer we should also use it to determine the required size. -*/ -int ConverterRGB24::predict_size(int w, int h) -{ - return avpicture_fill((AVPicture*)framergb, nullptr, PIX_FMT_RGB24, w, h); -} - - - -std::pair width_height(const AVFrame& f) -{ - return std::make_pair(f.width, f.height); -} - -int row_size(const AVFrame& f) -{ - return f.linesize[0]; -} - - -void disable_logging() -{ - av_log_set_level(AV_LOG_QUIET); -} diff --git a/Tello_Video/h264decoder/h264decoder.hpp b/Tello_Video/h264decoder/h264decoder.hpp deleted file mode 100644 index 9dd5c7a..0000000 --- a/Tello_Video/h264decoder/h264decoder.hpp +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once -/* -This h264 decoder class is just a thin wrapper around libav -functions to decode h264 videos. It would have been easy to use -libav directly in the python module code but I like to keep these -things separate. - -It is mostly based on roxlu's code. See -http://roxlu.com/2014/039/decoding-h264-and-yuv420p-playback - -However, in contrast to roxlu's code the color space conversion is -done by libav functions - so on the CPU, I suppose. - -Most functions/members throw exceptions. This way, error states are -conveniently forwarded to python via the exception translation -mechanisms of boost::python. -*/ - -// for ssize_t (signed int type as large as pointer type) -#include -#include -#include - -struct AVCodecContext; -struct AVFrame; -struct AVCodec; -struct AVCodecParserContext; -struct SwsContext; -struct AVPacket; - - -class H264Exception : public std::runtime_error -{ -public: - H264Exception(const char* s) : std::runtime_error(s) {} -}; - -class H264InitFailure : public H264Exception -{ -public: - H264InitFailure(const char* s) : H264Exception(s) {} -}; - -class H264DecodeFailure : public H264Exception -{ -public: - H264DecodeFailure(const char* s) : H264Exception(s) {} -}; - - -class H264Decoder -{ - /* Persistent things here, using RAII for cleanup. */ - AVCodecContext *context; - AVFrame *frame; - AVCodec *codec; - AVCodecParserContext *parser; - /* In the documentation example on the github master branch, the -packet is put on the heap. This is done here to store the pointers -to the encoded data, which must be kept around between calls to -parse- and decode frame. In release 11 it is put on the stack, too. - */ - AVPacket *pkt; -public: - H264Decoder(); - ~H264Decoder(); - /* First, parse a continuous data stream, dividing it into -packets. When there is enough data to form a new frame, decode -the data and return the frame. parse returns the number -of consumed bytes of the input stream. It stops consuming -bytes at frame boundaries. - */ - ssize_t parse(const unsigned char* in_data, ssize_t in_size); - bool is_frame_available() const; - const AVFrame& decode_frame(); -}; - -// TODO: Rename to OutputStage or so?! -class ConverterRGB24 -{ - SwsContext *context; - AVFrame *framergb; - -public: - ConverterRGB24(); - ~ConverterRGB24(); - - /* Returns, given a width and height, - how many bytes the frame buffer is going to need. */ - int predict_size(int w, int h); - /* Given a decoded frame, convert it to RGB format and fill -out_rgb with the result. Returns a AVFrame structure holding -additional information about the RGB frame, such as the number of -bytes in a row and so on. */ - const AVFrame& convert(const AVFrame &frame, unsigned char* out_rgb); -}; - -void disable_logging(); - -/* Wrappers, so we don't have to include libav headers. */ -std::pair width_height(const AVFrame&); -int row_size(const AVFrame&); - -/* all the documentation links - * My version of libav on ubuntu 16 appears to be from the release/11 branch on github - * Video decoding example: https://libav.org/documentation/doxygen/release/11/avcodec_8c_source.html#l00455 - * https://libav.org/documentation/doxygen/release/9/group__lavc__decoding.html - * https://libav.org/documentation/doxygen/release/11/group__lavc__parsing.html - * https://libav.org/documentation/doxygen/release/9/swscale_8h.html - * https://libav.org/documentation/doxygen/release/9/group__lavu.html - * https://libav.org/documentation/doxygen/release/9/group__lavc__picture.html - * http://dranger.com/ffmpeg/tutorial01.html - */ \ No newline at end of file diff --git a/Tello_Video/h264decoder/h264decoder_python.cpp b/Tello_Video/h264decoder/h264decoder_python.cpp deleted file mode 100644 index 6c553a1..0000000 --- a/Tello_Video/h264decoder/h264decoder_python.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include -#include - -// python string api, see -// https://docs.python.org/2/c-api/string.html -extern "C" { - #include -} - -#include -#include -#include -#include -#include -namespace py = boost::python; - -#include "h264decoder.hpp" - -using ubyte = unsigned char; - - -class GILScopedReverseLock -{ - // see https://docs.python.org/2/c-api/init.html (Releasing the GIL ...) -public: - GILScopedReverseLock() - : state(nullptr) - { - unlock(); - } - - ~GILScopedReverseLock() - { - lock(); - } - - void lock() - { - // Allow successive calls to lock. - // E.g. lock() followed by destructor. - if (state != nullptr) - { - PyEval_RestoreThread(state); - state = nullptr; - } - } - - void unlock() - { - assert (state == nullptr); - state = PyEval_SaveThread(); - } - - GILScopedReverseLock(const GILScopedReverseLock &) = delete; - GILScopedReverseLock(const GILScopedReverseLock &&) = delete; - GILScopedReverseLock operator=(const GILScopedReverseLock &) = delete; - GILScopedReverseLock operator=(const GILScopedReverseLock &&) = delete; -private: - PyThreadState *state; -}; - - -/* The class wrapped in python via boost::python */ -class PyH264Decoder -{ - H264Decoder decoder; - ConverterRGB24 converter; - - /* Extract frames from input stream. Stops at frame boundaries and returns the number of consumed bytes - * in num_consumed. - * - * If a frame is completed, is_frame_available is set to true, and the returned python tuple contains - * formation about the frame as well as the frame buffer memory. - * - * Else, i.e. all data in the buffer is consumed, is_frame_available is set to false. The returned tuple - * contains dummy data. - */ - py::tuple decode_frame_impl(const ubyte *data, ssize_t num, ssize_t &num_consumed, bool &is_frame_available); - -public: - /* Decoding style analogous to c/c++ way. Stop at frame boundaries. - * Return tuple containing frame data as above as nested tuple, and an integer telling how many bytes were consumed. */ - py::tuple decode_frame(const py::str &data_in_str); - /* Process all the input data and return a list of all contained frames. */ - py::list decode(const py::str &data_in_str); -}; - - -py::tuple PyH264Decoder::decode_frame_impl(const ubyte *data_in, ssize_t len, ssize_t &num_consumed, bool &is_frame_available) -{ - GILScopedReverseLock gilguard; - num_consumed = decoder.parse((ubyte*)data_in, len); - - if (is_frame_available = decoder.is_frame_available()) - { - const auto &frame = decoder.decode_frame(); - int w, h; std::tie(w,h) = width_height(frame); - Py_ssize_t out_size = converter.predict_size(w,h); - - gilguard.lock(); - // Construction of py::handle causes ... TODO: WHAT? No increase of ref count ?! - py::object py_out_str(py::handle<>(PyString_FromStringAndSize(NULL, out_size))); - char* out_buffer = PyString_AsString(py_out_str.ptr()); - - gilguard.unlock(); - const auto &rgbframe = converter.convert(frame, (ubyte*)out_buffer); - - gilguard.lock(); - return py::make_tuple(py_out_str, w, h, row_size(rgbframe)); - } - else - { - gilguard.lock(); - return py::make_tuple(py::object(), 0, 0, 0); - } -} - - -py::tuple PyH264Decoder::decode_frame(const py::str &data_in_str) -{ - ssize_t len = PyString_Size(data_in_str.ptr()); - const ubyte* data_in = (const ubyte*)(PyString_AsString(data_in_str.ptr())); - - ssize_t num_consumed = 0; - bool is_frame_available = false; - auto frame = decode_frame_impl(data_in, len, num_consumed, is_frame_available); - - return py::make_tuple(frame, num_consumed); -} - - -py::list PyH264Decoder::decode(const py::str &data_in_str) -{ - ssize_t len = PyString_Size(data_in_str.ptr()); - const ubyte* data_in = (const ubyte*)(PyString_AsString(data_in_str.ptr())); - - py::list out; - - try - { - while (len > 0) - { - ssize_t num_consumed = 0; - bool is_frame_available = false; - - try - { - auto frame = decode_frame_impl(data_in, len, num_consumed, is_frame_available); - if (is_frame_available) - { - out.append(frame); - } - } - catch (const H264DecodeFailure &e) - { - if (num_consumed <= 0) - // This case is fatal because we cannot continue to move ahead in the stream. - throw e; - } - - len -= num_consumed; - data_in += num_consumed; - } - } - catch (const H264DecodeFailure &e) - { - } - - return out; -} - - -BOOST_PYTHON_MODULE(libh264decoder) -{ - PyEval_InitThreads(); // need for release of the GIL (http://stackoverflow.com/questions/8009613/boost-python-not-supporting-parallelism) - py::class_("H264Decoder") - .def("decode_frame", &PyH264Decoder::decode_frame) - .def("decode", &PyH264Decoder::decode); - py::def("disable_logging", disable_logging); -} \ No newline at end of file diff --git a/Tello_Video/h264decoder/readme.md b/Tello_Video/h264decoder/readme.md deleted file mode 100644 index 5aeba30..0000000 --- a/Tello_Video/h264decoder/readme.md +++ /dev/null @@ -1,35 +0,0 @@ -H264 Decoder Python Module -========================== - -The aim of this project is to provide a simple decoder for video -captured by a Raspberry Pi camera. At the time of this writing I only -need H264 decoding, since a H264 stream is what the RPi software -delivers. Furthermore flexibility to incorporate the decoder in larger -python programs in various ways is desirable. - -The code might also serve as example for libav and boost python usage. - - -Files ------ -* `h264decoder.hpp`, `h264decoder.cpp` and `h264decoder_python.cpp` contain the module code. - -* Other source files are tests and demos. - - -Requirements ------------- -* cmake for building -* libav -* boost python - - -Todo ----- - -* Add a video clip for testing and remove hard coded file names in demos/tests. - - -License -------- -The code is published under the Mozilla Public License v. 2.0. diff --git a/Tello_Video/install/Linux/linux_install.sh b/Tello_Video/install/Linux/linux_install.sh deleted file mode 100644 index 93f62f8..0000000 --- a/Tello_Video/install/Linux/linux_install.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh - -echo 'Compiling and Installing the Tello Video Stream module' -echo 'You might need to enter your password' - -cd .. -cd .. -sudo apt-get update -y - -# install python 2.7 -sudo apt-get install python2.7 python-pip -y -sudo pip install --upgrade pip - -#switch to python2.7 -sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 150 -sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 100 - -sudo apt-get update -y - -# install cmake -#sudo apt-get install cmake -y -sudo pip install cmake - -# install dependencies -sudo apt-get install libboost-all-dev -y -sudo apt-get install libavcodec-dev -y -sudo apt-get install libswscale-dev -y -sudo apt-get install python-numpy -y -sudo apt-get install python-matplotlib -y -sudo pip install opencv-python -sudo apt-get install python-imaging-tk - -# pull and build h264 decoder library -cd h264decoder -mkdir build -cd build -cmake .. -make - -# copy source .so file to tello.py directory -cp libh264decoder.so ../../ - -echo 'Compilation and Installation Done!' diff --git a/Tello_Video/install/Linux/linux_uninstall.sh b/Tello_Video/install/Linux/linux_uninstall.sh deleted file mode 100644 index c2d4a3f..0000000 --- a/Tello_Video/install/Linux/linux_uninstall.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -echo 'Uninstalling the Tello Video Stream module' - -cd .. -cd .. -sudo pip uninstall opencv-python -y -sudo pip uninstall cmake -y -sudo pip uninstall pip -y - -sudo apt-get remove libboost-all-dev -y -sudo apt-get remove libavcodec-dev -y -sudo apt-get remove libswscale-dev -y -sudo apt-get remove python-numpy -y -sudo apt-get remove python-matplotlib -y -sudo apt-get remove python-pil.imagetk -y - -sudo apt-get remove python-pip -y -sudo apt-get remove python2.7 -y - -sudo apt-get update -y - -rm ./libh264decoder.so -rm -r ./h264decoder/build -echo 'Uninstallation Done!' diff --git a/Tello_Video/install/Mac/mac_install.sh b/Tello_Video/install/Mac/mac_install.sh deleted file mode 100644 index 2b773ad..0000000 --- a/Tello_Video/install/Mac/mac_install.sh +++ /dev/null @@ -1,76 +0,0 @@ - -#!/bin/sh - - - -echo 'Compiling and Installing the Tello Video Stream module' - -echo 'You might need to enter your password' - -# go to /sample_code folder -cd .. -cd .. - -# install Homebrew - -/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" - -brew update - - - -# install pip - -#sudo easy_install pip - -curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py -sudo python get-pip.py - -# install cmake - -brew install cmake - - - -# install dependencies - -brew install boost - -brew install boost-python - -brew install ffmpeg - -brew install tcl-tk - -sudo pip install numpy --ignore-installed - -sudo pip install matplotlib --ignore-installed - -sudo pip install pillow --ignore-installed - -sudo pip install opencv-python --ignore-installed - - - -# pull and build h264 decoder library - -cd h264decoder - - -mkdir build - -cd build - -cmake .. - -make - - - -# copy source .so file to tello.py directory - -cp libh264decoder.so ../../ - - - -echo 'Compilation and Installation Done!' diff --git a/Tello_Video/install/Mac/mac_uninstall.sh b/Tello_Video/install/Mac/mac_uninstall.sh deleted file mode 100644 index 579d482..0000000 --- a/Tello_Video/install/Mac/mac_uninstall.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -echo 'Uninstalling the Tello Video Stream module' - -echo 'You might need to enter your password' - -# go to /sample_code folder -cd .. -cd .. - -brew update - -sudo pip uninstall matplotlib -y -sudo pip uninstall numpy -y -sudo pip install pillow -y -sudo pip install opencv-python -y -sudo pip uninstall pip -y - -brew uninstall tcl-tk -brew uninstall ffmpeg -brew uninstall boost-python -brew uninstall boost -brew uninstall cmake - -rm -f ./libh264decoder.so -rm -rf ./h264decoder/build - -echo 'Uninstallation Done!' diff --git a/Tello_Video/install/Windows/install.bat b/Tello_Video/install/Windows/install.bat deleted file mode 100644 index fe973e2..0000000 --- a/Tello_Video/install/Windows/install.bat +++ /dev/null @@ -1,440 +0,0 @@ -echo=1/*>nul&@cls -@echo off -setlocal enableDelayedExpansion -::runas administrator -%1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit -cls -::setlocal -call :setdir -call :configx86orx64 -set extract=extract -set pythonLib="C:\Python27\Lib\site-packages\" -set /a maxRetry=3 -set /a retryCount=0 -echo ------------------------------------------------------ - -::-------------------down python2.7 and install------------------- -echo ------------------------------------------------------ -echo Downloading python2.7 -echo ------------------------------------------------------ -::עڿssltls汾֧֣ڽpythonܾʵ -REG ADD "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v SecureProtocols /t REG_DWORD /d 2728 /f >nul -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downpython - ) -) -if exist %pythonPackage% goto :downpythoncheck -:downpython -call :down %pythonDown% %pythonPackage% -:downpythoncheck -call :checkMD5 %pythonPackage% %pythonMD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downpythonend - ) else ( - echo Download %pythonPackage% failed. Retrying... !retryCount! of %maxRetry% - goto downpython - ) -) -call :installmsiPackage %pythonPackage% -::python2.7 -::wmicἴʱЧԽset -echo %PATH%|findstr "c:\python27" >nul -if %errorlevel% neq 0 ( - wmic ENVIRONMENT where "name='PATH' and username=''" set VariableValue="%PATH%;c:\python27" - set "path=%path%;c:\python27;" -) -echo %PATHEXT%|findstr ".PY;.PYM" >nul -if %errorlevel% neq 0 ( - wmic ENVIRONMENT where "name='PATHEXT' and username=''" set VariableValue="%PATHEXT%;.PY;.PYM" - set "pathext=%pathext%;.PY;.PYM;" -) -:downpythonend -::-------------------python pipİװ------------------- -echo ------------------------------------------------------ -echo Downloading pip -echo ------------------------------------------------------ -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downpip - ) -) -if exist %pipPackage% goto :downpipcheck -:downpip -call :down %pipDown% %pipPackage% -:downpipcheck -call :checkMD5 %pipPackage% %pipMD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downpipend - ) else ( - echo Download %pipPackage% failed. Retrying... !retryCount! of %maxRetry% - goto downpip - ) -) -python %pipPackage% -python -m pip install -U pip -:downpipend -::-------------------libboost-all-dev İװ------------------- -echo ------------------------------------------------------ -echo Downloading libboost -echo ------------------------------------------------------ -echo Please wait a few minutes... -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downlibboost - ) -) -if exist %libboostPackage% goto :downlibboostcheck -:downlibboost -call :down %libboostDown% %libboostPackage% -:downlibboostcheck -call :checkMD5 %libboostPackage% %libboostMD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downlibboostend - ) else ( - echo Download %libboostPackage% failed. Retrying... !retryCount! of %maxRetry% - goto downlibboost - ) -) -call %libboostPackage% /SILENT /NORESTART -:downlibboostend -::-------------------ffmpegİװ Ӧlinuxlibavcodec-dev libswscale-dev2------------------- -echo ------------------------------------------------------ -echo Downloading ffmpeg -echo ------------------------------------------------------ -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downffmpeg - ) -) -if exist %ffmpegPackage% goto :downffmpegcheck -:downffmpeg -call :down %ffmpegDown% %ffmpegPackage% -:downffmpegcheck -call :checkMD5 %ffmpegPackage% %ffmpegMD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downffmpegend - ) else ( - echo Download %ffmpegPackage% failed. Retrying... !retryCount! of %maxRetry% - goto downffmpeg - ) -) -call :unpack %ffmpegPackage% %extract% -:downffmpegend -echo ------------------------------------------------------ -echo Downloading VS2013 runtime -echo ------------------------------------------------------ -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downvs2013 - ) -) -if exist %vs2013package% goto :downvs2013check -:downvs2013 -call :down %vs2013depend% %vs2013package% -:downvs2013check -call :checkMD5 %vs2013package% %vs2013MD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downvs2013end - ) else ( - echo Download %vs2013package% failed. Retrying... !retryCount! of %maxRetry% - goto downvs2013 - ) -) -call %vs2013package% /passive /NORESTART -:downvs2013end -::-------------------python-numpy python-matplotlib opencv-pythonİװpipʽ------------------- -echo ------------------------------------------------------ -echo Downloading numpy -echo ------------------------------------------------------ -python -m pip install numpy -echo ------------------------------------------------------ -echo Downloading matplotlib -echo ------------------------------------------------------ -python -m pip install matplotlib -echo ------------------------------------------------------ -echo Downloading opencv-python -echo ------------------------------------------------------ -python -m pip install -v opencv-python==3.4.2.17 -echo ------------------------------------------------------ -echo Downloading pillow -echo ------------------------------------------------------ -python -m pip install pillow -:copydependencies -::-------------------еdllȥc:\python27\lib\site-packages------------------- -echo ------------------------------------------------------ -echo Copying dependencies -echo ------------------------------------------------------ -echo %extract%\%ffmpegPackage:~0,-4%\bin\ -echo %libboostPackageCopy% -echo %libh264% -xcopy /Y /E /I %extract%\%ffmpegPackage:~0,-4%\bin\*.dll %pythonLib% -xcopy /Y /E /I %libboostPackageCopy% %pythonLib% -xcopy /Y /E /I %libh264%\*.pyd %pythonLib% -endlocal -echo ------------------------------------------------------ -echo Installation done. -echo ------------------------------------------------------ -pause -goto :eof - -::-------------------------------------------------------------------------------------------- - -::-----------------MD5Աȶ------------------ -:checkMD5 -set file=%~1 -call :MD5get %file% MD5 -if "%MD5%" equ "%~2" ( - echo MD5 identical. - set "%~3=YES" -) else ( - if "%MD5%" equ "skip" ( - echo MD5 check skipped. - set "%~3=YES" - ) else ( - echo Warning: MD5 does not match! - set "%~3=NO" - ) -) -goto :eof - -::-----------------MD5ȡ------------------ -:MD5get -echo %~1 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - echo certutil.exe not found - echo for Windows XP professional and Windows 2003 - echo you need Windows Server 2003 Administration Tools Pack - echo https://www.microsoft.com/en-us/download/details.aspx?id=16770 - echo now skip the MD5 check - if "%~2" neq "" ( - set "%~2=skip" - ) - ::exit /b 4 - goto :eof - ) -) - -set "md5=" -for /f "skip=1 tokens=* delims=" %%# in ('certutil -hashfile "%~f1" MD5') do ( - if not defined md5 ( - for %%Z in (%%#) do set "md5=!md5!%%Z" - ) -) - -if "%~2" neq "" ( - set "%~2=%md5%" -) else ( - echo %md5% -) - -goto :eof - -::-----------------Ŀ¼л------------------ -::ڹԱģʽִʱĬ·˴Ŀ¼л -:setdir -set char=%~dp0% -%char:~0,2% -cd %~dp0% -goto :eof - -::-----------------ǰ汾------------------ -:configx86orx64 -IF %PROCESSOR_ARCHITECTURE% == AMD64 ( - set versionFlag=win64 -) else ( - set versionFlag=win32 -) - -echo Windows Version: %versionFlag% -if %versionFlag%==win64 ( - set pythonDown="https://www.python.org/ftp/python/2.7.15/python-2.7.15.amd64.msi" - set pythonPackage=python-2.7.15.amd64.msi - set pythonMD5="0ffa44a86522f9a37b916b361eebc552" - - set pipDown="https://bootstrap.pypa.io/get-pip.py" - set pipPackage=get-pip.py - set pipMD5="da117d21d1fd79c0445a6af027f13580" - - set ffmpegDown="https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-20180825-844ff49-win64-shared.zip" - set ffmpegPackage=ffmpeg-20180825-844ff49-win64-shared.zip - set ffmpegMD5="37de63b02196cf9e09c7adf2924c143a" - - set libboostDown="https://nchc.dl.sourceforge.net/project/boost/boost-binaries/1.68.0/boost_1_68_0-msvc-12.0-64.exe" - set libboostPackage="boost_1_68_0-msvc-12.0-64.exe" - set libboostPackageCopy="c:\local\boost_1_68_0\lib64-msvc-12.0\boost_python27-vc120-mt-x64-1_68.dll" - set libboostMD5="4e6b11a971502639ba5cc564c7f2d568" - - set libh264=..\..\h264decoder\windows\x64 - - set vs2013depend="https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe" - set vs2013package=vcredist_x64.exe - set vs2013MD5="96b61b8e069832e6b809f24ea74567ba" - -) else ( - set pythonDown="https://www.python.org/ftp/python/2.7.15/python-2.7.15.msi" - set pythonPackage=python-2.7.15.msi - set pythonMD5="023e49c9fba54914ebc05c4662a93ffe" - - set pipDown="https://bootstrap.pypa.io/get-pip.py" - set pipPackage=get-pip.py - set pipMD5="da117d21d1fd79c0445a6af027f13580" - - set ffmpegDown="https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-20180825-844ff49-win32-shared.zip" - set ffmpegPackage=ffmpeg-20180825-844ff49-win32-shared.zip - set ffmpegMD5="8e70ae7338083a311c055aa44aa5407d" - - set libboostDown="https://excellmedia.dl.sourceforge.net/project/boost/boost-binaries/1.68.0/boost_1_68_0-msvc-12.0-32.exe" - set libboostPackage="boost_1_68_0-msvc-12.0-32.exe" - set libboostPackageCopy="c:\local\boost_1_68_0\lib32-msvc-12.0\boost_python27-vc120-mt-x32-1_68.dll" - set libboostMD5="d5d5ee205c87078245eb7df72789f407" - - set libh264=..\..\h264decoder\windows\x86 - - set vs2013depend="https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x86.exe" - set vs2013package=vcredist_x86.exe - set vs2013MD5="0fc525b6b7b96a87523daa7a0013c69d" -) - -goto :eof - -::-----------------h264------------------ -:h264install -cd h264decoder -if exist build ( - rd /s /q build - mkdir build -) else ( - mkdir build -) -cd build -cmake .. -make -goto :eof - -::-----------------غ------------------ -:down -echo Source: "%~1" -echo Destination: "%~f2" -echo Start downloading "%~2"... -cscript -nologo -e:jscript "%~f0" "download" "%~1" "%~2" -::echo Download "%~2" OK! -echo ------------------------------------------------------ -goto :eof - -::-----------------ǽѹ------------------ -:unpack -echo Source: "%~f1" -echo Destination: "%~f2" -echo Start unpacking "%~1"... -cscript -nologo -e:jscript "%~f0" "unpack" "%~1" "%~2" "%~dp0" -echo Unpack "%~1" OK! -echo ------------------------------------------------------ -goto :eof -::-----------------ǰװ------------------ -:installmsiPackage -echo Source: "%~f1" -echo Strat installing "%~f1"... -msiexec /i "%~f1" /passive -echo install "%~1" OK! -echo ------------------------------------------------------ -goto :eof -*/ - -function download(DownSource, DownDestination) -{ - var DownPost - ,DownGet; - - DownDestination=DownDestination.toLowerCase(); - DownSource=DownSource.toLowerCase(); - //DownPost = new ActiveXObject("Msxml2"+String.fromCharCode(0x2e)+"ServerXMLHTTP"); - //DownPost = new ActiveXObject("Microsoft"+String.fromCharCode(0x2e)+"XMLHTTP"); - //DownPost.setOption(2, 13056); - var DownPost=null; - try{ - DownPost=new XMLHttpRequest(); - }catch(e){ - try{ - DownPost=new ActiveXObject("Msxml2.XMLHTTP"); - DownPost.setOption(2, 13056); - }catch(ex){ - try{ - DownPost=new ActiveXObject("Microsoft.XMLHTTP"); - }catch(e3){ - DownPost=null; - } - } - } - DownPost.open("GET",DownSource,0); - DownPost.send(); - DownGet = new ActiveXObject("ADODB"+String.fromCharCode(0x2e)+"Stream"); - DownGet.Mode = 3; - DownGet.Type = 1; - DownGet.Open(); - DownGet.Write(DownPost.responseBody); - DownGet.SaveToFile(DownDestination,2); -} - -function unpack(PackedFileSource, UnpackFileDestination, ParentFolder) -{ - var FileSysObject = new Object - ,ShellObject = new ActiveXObject("Shell.Application") - ,intOptions = 4 + 16 - ,DestinationObj - ,SourceObj; - - if (!UnpackFileDestination) UnpackFileDestination = '.'; - var FolderTest = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); - FileSysObject = ShellObject.NameSpace(ParentFolder); - while (!FolderTest) - { - WSH.Echo ('Unpack Destination Folder Not Exist, Creating...'); - FileSysObject.NewFolder(UnpackFileDestination); - FolderTest = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); - if (FolderTest) - WSH.Echo('Unpack Destination Folder Created.'); - } - DestinationObj = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); - SourceObj = ShellObject.NameSpace(ParentFolder + PackedFileSource); - for (var i = 0; i < SourceObj.Items().Count; i++) - { - try { - if (SourceObj) { - WSH.Echo('Unpacking ' + SourceObj.Items().Item(i) + '... '); - DestinationObj.CopyHere(SourceObj.Items().Item(i), intOptions); - WSH.Echo('Unpack ' + SourceObj.Items().Item(i) + ' Done.'); - } - } - catch(e) { - WSH.Echo('Failed: ' + e); - } - } -} - -switch (WScript.Arguments(0)){ - case "download": - download(WScript.Arguments(1), WScript.Arguments(2)); - break; - case "unpack": - unpack(WScript.Arguments(1), WScript.Arguments(2), WScript.Arguments(3)); - break; - default: -} - diff --git a/Tello_Video/install/Windows/uninstall.bat b/Tello_Video/install/Windows/uninstall.bat deleted file mode 100644 index 12e4b81..0000000 --- a/Tello_Video/install/Windows/uninstall.bat +++ /dev/null @@ -1,92 +0,0 @@ -@echo off -::runas administrator -%1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit -setlocal enabledelayedexpansion -call :setdir -call :configx86orx64 -set "pythondir=c:\python27" -set "libboostdir=c:\local\boost_1_68_0" -set "logfile=%~dp0\UnInstallLog.txt" -set extract=extract -echo ------------------------------------------------------ -echo Uninstalling python27 -echo ------------------------------------------------------ -call :uninstallmsiPackage "%python27Name%" "%python27Name%" -if exist %pythondir% rmdir /s /q %pythondir% -if exist %pythondir% rd /s /q %pythondir% -set delPythonPath=%PATH:;c:\python27=% -set delPythonExt=%PATHEXT:;.PY;.PYM=% -wmic ENVIRONMENT where "name='PATH' and username=''" set VariableValue="%delPythonPath%" -wmic ENVIRONMENT where "name='PATHEXT' and username=''" set VariableValue="%delPythonExt%" -echo ------------------------------------------------------ -echo Uninstalling libboost -echo ------------------------------------------------------ -if exist %libboostdir% rd /s /q %libboostdir% -echo ------------------------------------------------------ -echo Uninstalling ffmpeg -echo ------------------------------------------------------ -if exist %extract% rd /s /q %extract% -echo ------------------------------------------------------ -echo Cleaning downloaded files -echo ------------------------------------------------------ -del /f /q %pythonPackage% -del /f /q %pipPackage% -del /f /q %ffmpegPackage% -del /f /q %libboostPackage% -echo ------------------------------------------------------ -echo Uninstallation done. -echo ------------------------------------------------------ -pause -goto :eof - -::-----------------Ŀ¼л----------------- -::ڹԱģʽִʱĬ·˴Ŀ¼л -:setdir -set char=%~dp0% -%char:~0,2% -cd %~dp0% -goto :eof - -::-----------------ǰ汾----------------- -:configx86orx64 -IF %PROCESSOR_ARCHITECTURE% == AMD64 ( - set versionFlag=win64 -) else ( - set versionFlag=win32 -) - -echo Windows Version: %versionFlag% -if %versionFlag%==win64 ( - set "python27Name=python 2.7.15 (64-bit)" - set "python27ProductCode=20C31435-2A0A-4580-BE8B-AC06FC243CA5" - set pythonPackage=python-2.7.15.amd64.msi - set pipPackage=get-pip.py - set ffmpegPackage=ffmpeg-20180825-844ff49-win64-shared.zip - set libboostPackage="boost_1_68_0-msvc-12.0-64.exe" -) else ( - set "python27Name=Python 2.7.15" - set "python27ProductCode=20C31435-2A0A-4580-BE8B-AC06FC243CA4" - set pythonPackage=python-2.7.15.msi - set pipPackage=get-pip.py - set ffmpegPackage=ffmpeg-20180825-844ff49-win32-shared.zip - set libboostPackage="boost_1_68_0-msvc-12.0-32.exe" -) -endlocal -goto :eof - -::-----------------жغ----------------- -:uninstallmsiPackage -echo Strat uninstalling "%~2"... -echo "%~2" Start...%date% %time%>>%logfile% -FOR /f "tokens=1" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /e /s /d /f "%~2"^|FINDSTR /i "CurrentVersion"') do ( - FOR /f "tokens=1-2,*" %%j in ('reg query "%%i" /f "UninstallString"^|FINDSTR /i "UninstallString"') do ( - set correct=%%l && set correct=!correct:/I{=/passive /norestart /x{! && CMD /q /c "!correct!" - ) -) - -IF %ERRORLEVEL% EQU 0 echo Uninstall %~2 Successfull -IF %ERRORLEVEL% NEQ 0 IF %ERRORLEVEL% NEQ 1605 echo Uninstall %~2 Failed, ERRORLEVEL:%ERRORLEVEL%. -echo ERRORLEVEL:%ERRORLEVEL%>>%logfile% -echo %~2 End %date% %time%>>%logfile% -echo ----------------------------------------------------- -goto :eof \ No newline at end of file diff --git a/Tello_Video/requirements.txt b/Tello_Video/requirements.txt new file mode 100644 index 0000000..bea982f --- /dev/null +++ b/Tello_Video/requirements.txt @@ -0,0 +1,4 @@ +matplotlib +numpy +Pillow +git+https://github.com/DaWelter/h264decoder.git \ No newline at end of file diff --git a/Tello_Video/tello.py b/Tello_Video/tello.py index 0b94952..856c3c5 100644 --- a/Tello_Video/tello.py +++ b/Tello_Video/tello.py @@ -2,7 +2,7 @@ import threading import time import numpy as np -import libh264decoder +import h264decoder class Tello: """Wrapper class to interact with the Tello drone.""" @@ -20,9 +20,9 @@ def __init__(self, local_ip, local_port, imperial=False, command_timeout=.3, tel :param tello_ip (str): Tello IP. :param tello_port (int): Tello port. """ - + self.abort_flag = False - self.decoder = libh264decoder.H264Decoder() + self.decoder = h264decoder.H264Decoder() self.command_timeout = command_timeout self.imperial = imperial self.response = None @@ -95,7 +95,7 @@ def _receive_video_thread(self): Runs as a thread, sets self.frame to the most recent frame Tello captured. """ - packet_data = "" + packet_data = b"" while True: try: res_string, ip = self.socket_video.recvfrom(2048) @@ -104,7 +104,7 @@ def _receive_video_thread(self): if len(res_string) != 1460: for frame in self._h264_decode(packet_data): self.frame = frame - packet_data = "" + packet_data = b"" except socket.error as exc: print ("Caught exception socket.error : %s" % exc) @@ -125,7 +125,7 @@ def _h264_decode(self, packet_data): # print 'frame size %i bytes, w %i, h %i, linesize %i' % (len(frame), w, h, ls) frame = np.fromstring(frame, dtype=np.ubyte, count=len(frame), sep='') - frame = (frame.reshape((h, ls / 3, 3))) + frame = (frame.reshape((h, ls // 3, 3))) frame = frame[:, :w, :] res_frame_list.append(frame) @@ -371,7 +371,7 @@ def move(self, direction, distance): distance = int(round(distance * 30.48)) else: distance = int(round(distance * 100)) - + return self.send_command('%s %s' % (direction, distance)) def move_backward(self, distance): diff --git a/Tello_Video/tello_control_ui.py b/Tello_Video/tello_control_ui.py index 4cd1c91..1e1f133 100644 --- a/Tello_Video/tello_control_ui.py +++ b/Tello_Video/tello_control_ui.py @@ -1,7 +1,7 @@ from PIL import Image from PIL import ImageTk -import Tkinter as tki -from Tkinter import Toplevel, Scale +import tkinter as tki +from tkinter import Toplevel, Scale import threading import datetime import cv2 @@ -97,7 +97,7 @@ def videoLoop(self): thread_tmp = threading.Thread(target=self._updateGUIImage,args=(image,)) thread_tmp.start() time.sleep(0.03) - except RuntimeError, e: + except RuntimeError as e: print("[INFO] caught a RuntimeError") @@ -304,42 +304,42 @@ def updateTrackBar(self): def updateDistancebar(self): self.distance = self.distance_bar.get() - print 'reset distance to %.1f' % self.distance + print('reset distance to %.1f' % self.distance) def updateDegreebar(self): self.degree = self.degree_bar.get() - print 'reset distance to %d' % self.degree + print('reset distance to %d' % self.degree) def on_keypress_w(self, event): - print "up %d m" % self.distance + print("up %d m" % self.distance) self.telloUp(self.distance) def on_keypress_s(self, event): - print "down %d m" % self.distance + print("down %d m" % self.distance) self.telloDown(self.distance) def on_keypress_a(self, event): - print "ccw %d degree" % self.degree + print("ccw %d degree" % self.degree) self.tello.rotate_ccw(self.degree) def on_keypress_d(self, event): - print "cw %d m" % self.degree + print("cw %d m" % self.degree) self.tello.rotate_cw(self.degree) def on_keypress_up(self, event): - print "forward %d m" % self.distance + print("forward %d m" % self.distance) self.telloMoveForward(self.distance) def on_keypress_down(self, event): - print "backward %d m" % self.distance + print("backward %d m" % self.distance) self.telloMoveBackward(self.distance) def on_keypress_left(self, event): - print "left %d m" % self.distance + print("left %d m" % self.distance) self.telloMoveLeft(self.distance) def on_keypress_right(self, event): - print "right %d m" % self.distance + print("right %d m" % self.distance) self.telloMoveRight(self.distance) def on_keypress_enter(self, event): diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/CMakeLists.txt b/Tello_Video_With_Pose_Recognition/h264decoder/CMakeLists.txt deleted file mode 100644 index 98a924d..0000000 --- a/Tello_Video_With_Pose_Recognition/h264decoder/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -project(python_h264decoder) - -set(Python_ADDITIONAL_VERSIONS 2.7) - -if(UNIX AND NOT APPLE) - set(LINUX TRUE) -endif() - -if(APPLE) - set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") - find_package(Boost REQUIRED COMPONENTS python27) -elseif(LINUX) - find_package(Boost REQUIRED COMPONENTS "python") -endif(APPLE) - - - -find_package(PythonInterp 2.7 REQUIRED) -find_package(PythonLibs 2.7 REQUIRED ) - - -include_directories(${PYTHON_INCLUDE_DIRS}) -include_directories(${Boost_INCLUDE_DIRS}) -link_directories(${Boost_LIBRARY_DIRS}) - -add_compile_options ("-std=c++0x") - -add_library(h264decoder SHARED h264decoder.cpp h264decoder_python.cpp) - -if(APPLE) - target_link_libraries(h264decoder avcodec swscale avutil ${Boost_LIBRARIES} ${Boost_PYTHON_LIBRARY_RELEASE} ${PYTHON_LIBRARIES}) -elseif(LINUX) - target_link_libraries(h264decoder avcodec swscale avutil ${Boost_PYTHON_LIBRARY_RELEASE} ${PYTHON_LIBRARIES}) -endif(APPLE) - -add_custom_command(TARGET h264decoder POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_BINARY_DIR}/libh264decoder.so ${CMAKE_SOURCE_DIR}/libh264decoder.so) -install(TARGETS h264decoder LIBRARY DESTINATION .) diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/Linux/libh264decoder.so b/Tello_Video_With_Pose_Recognition/h264decoder/Linux/libh264decoder.so deleted file mode 100644 index 5c51abd..0000000 Binary files a/Tello_Video_With_Pose_Recognition/h264decoder/Linux/libh264decoder.so and /dev/null differ diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/Mac/libh264decoder.so b/Tello_Video_With_Pose_Recognition/h264decoder/Mac/libh264decoder.so deleted file mode 100644 index e9d5d30..0000000 Binary files a/Tello_Video_With_Pose_Recognition/h264decoder/Mac/libh264decoder.so and /dev/null differ diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/Windows/x64/libh264decoder.pyd b/Tello_Video_With_Pose_Recognition/h264decoder/Windows/x64/libh264decoder.pyd deleted file mode 100644 index 9772c49..0000000 Binary files a/Tello_Video_With_Pose_Recognition/h264decoder/Windows/x64/libh264decoder.pyd and /dev/null differ diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/Windows/x86/libh264decoder.pyd b/Tello_Video_With_Pose_Recognition/h264decoder/Windows/x86/libh264decoder.pyd deleted file mode 100644 index c153cad..0000000 Binary files a/Tello_Video_With_Pose_Recognition/h264decoder/Windows/x86/libh264decoder.pyd and /dev/null differ diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder.cpp b/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder.cpp deleted file mode 100644 index fac80ad..0000000 --- a/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder.cpp +++ /dev/null @@ -1,173 +0,0 @@ -extern "C" { -#include -#include -#include -#include -} - -#ifndef PIX_FMT_RGB24 -#define PIX_FMT_RGB24 AV_PIX_FMT_RGB24 -#endif - -#ifndef CODEC_CAP_TRUNCATED -#define CODEC_CAP_TRUNCATED AV_CODEC_CAP_TRUNCATED -#endif - -#ifndef CODEC_FLAG_TRUNCATED -#define CODEC_FLAG_TRUNCATED AV_CODEC_FLAG_TRUNCATED -#endif - -#include "h264decoder.hpp" -#include - -typedef unsigned char ubyte; - -/* For backward compatibility with release 9 or so of libav */ -#if (LIBAVCODEC_VERSION_MAJOR <= 54) -# define av_frame_alloc avcodec_alloc_frame -# define av_frame_free avcodec_free_frame -#endif - - -H264Decoder::H264Decoder() -{ - avcodec_register_all(); - - codec = avcodec_find_decoder(AV_CODEC_ID_H264); - if (!codec) - throw H264InitFailure("cannot find decoder"); - - context = avcodec_alloc_context3(codec); - if (!context) - throw H264InitFailure("cannot allocate context"); - - if(codec->capabilities & CODEC_CAP_TRUNCATED) { - context->flags |= CODEC_FLAG_TRUNCATED; - } - - int err = avcodec_open2(context, codec, nullptr); - if (err < 0) - throw H264InitFailure("cannot open context"); - - parser = av_parser_init(AV_CODEC_ID_H264); - if (!parser) - throw H264InitFailure("cannot init parser"); - - frame = av_frame_alloc(); - if (!frame) - throw H264InitFailure("cannot allocate frame"); - -#if 1 - pkt = new AVPacket; - if (!pkt) - throw H264InitFailure("cannot allocate packet"); - av_init_packet(pkt); -#endif -} - - -H264Decoder::~H264Decoder() -{ - av_parser_close(parser); - avcodec_close(context); - av_free(context); - av_frame_free(&frame); -#if 1 - delete pkt; -#endif -} - - -ssize_t H264Decoder::parse(const ubyte* in_data, ssize_t in_size) -{ - auto nread = av_parser_parse2(parser, context, &pkt->data, &pkt->size, - in_data, in_size, - 0, 0, AV_NOPTS_VALUE); - return nread; -} - - -bool H264Decoder::is_frame_available() const -{ - return pkt->size > 0; -} - - -const AVFrame& H264Decoder::decode_frame() -{ - int got_picture = 0; - int nread = avcodec_decode_video2(context, frame, &got_picture, pkt); - if (nread < 0 || got_picture == 0) - throw H264DecodeFailure("error decoding frame\n"); - return *frame; -} - - -ConverterRGB24::ConverterRGB24() -{ - framergb = av_frame_alloc(); - if (!framergb) - throw H264DecodeFailure("cannot allocate frame"); - context = nullptr; -} - -ConverterRGB24::~ConverterRGB24() -{ - sws_freeContext(context); - av_frame_free(&framergb); -} - - -const AVFrame& ConverterRGB24::convert(const AVFrame &frame, ubyte* out_rgb) -{ - int w = frame.width; - int h = frame.height; - int pix_fmt = frame.format; - - context = sws_getCachedContext(context, - w, h, (AVPixelFormat)pix_fmt, - w, h, PIX_FMT_RGB24, SWS_BILINEAR, - nullptr, nullptr, nullptr); - if (!context) - throw H264DecodeFailure("cannot allocate context"); - - // Setup framergb with out_rgb as external buffer. Also say that we want RGB24 output. - avpicture_fill((AVPicture*)framergb, out_rgb, PIX_FMT_RGB24, w, h); - // Do the conversion. - sws_scale(context, frame.data, frame.linesize, 0, h, - framergb->data, framergb->linesize); - framergb->width = w; - framergb->height = h; - return *framergb; -} - -/* -Determine required size of framebuffer. - -avpicture_get_size is used in http://dranger.com/ffmpeg/tutorial01.html -to do this. However, avpicture_get_size returns the size of a compact -representation, without padding bytes. Since we use avpicture_fill to -fill the buffer we should also use it to determine the required size. -*/ -int ConverterRGB24::predict_size(int w, int h) -{ - return avpicture_fill((AVPicture*)framergb, nullptr, PIX_FMT_RGB24, w, h); -} - - - -std::pair width_height(const AVFrame& f) -{ - return std::make_pair(f.width, f.height); -} - -int row_size(const AVFrame& f) -{ - return f.linesize[0]; -} - - -void disable_logging() -{ - av_log_set_level(AV_LOG_QUIET); -} diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder.hpp b/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder.hpp deleted file mode 100644 index 404d163..0000000 --- a/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once -/* -This h264 decoder class is just a thin wrapper around libav -functions to decode h264 videos. It would have been easy to use -libav directly in the python module code but I like to keep these -things separate. - -It is mostly based on roxlu's code. See -http://roxlu.com/2014/039/decoding-h264-and-yuv420p-playback - -However, in contrast to roxlu's code the color space conversion is -done by libav functions - so on the CPU, I suppose. - -Most functions/members throw exceptions. This way, error states are -conveniently forwarded to python via the exception translation -mechanisms of boost::python. -*/ - -// for ssize_t (signed int type as large as pointer type) -#include -#include - -struct AVCodecContext; -struct AVFrame; -struct AVCodec; -struct AVCodecParserContext; -struct SwsContext; -struct AVPacket; - - -class H264Exception : public std::runtime_error -{ -public: - H264Exception(const char* s) : std::runtime_error(s) {} -}; - -class H264InitFailure : public H264Exception -{ -public: - H264InitFailure(const char* s) : H264Exception(s) {} -}; - -class H264DecodeFailure : public H264Exception -{ -public: - H264DecodeFailure(const char* s) : H264Exception(s) {} -}; - - -class H264Decoder -{ - /* Persistent things here, using RAII for cleanup. */ - AVCodecContext *context; - AVFrame *frame; - AVCodec *codec; - AVCodecParserContext *parser; - /* In the documentation example on the github master branch, the -packet is put on the heap. This is done here to store the pointers -to the encoded data, which must be kept around between calls to -parse- and decode frame. In release 11 it is put on the stack, too. - */ - AVPacket *pkt; -public: - H264Decoder(); - ~H264Decoder(); - /* First, parse a continuous data stream, dividing it into -packets. When there is enough data to form a new frame, decode -the data and return the frame. parse returns the number -of consumed bytes of the input stream. It stops consuming -bytes at frame boundaries. - */ - ssize_t parse(const unsigned char* in_data, ssize_t in_size); - bool is_frame_available() const; - const AVFrame& decode_frame(); -}; - -// TODO: Rename to OutputStage or so?! -class ConverterRGB24 -{ - SwsContext *context; - AVFrame *framergb; - -public: - ConverterRGB24(); - ~ConverterRGB24(); - - /* Returns, given a width and height, - how many bytes the frame buffer is going to need. */ - int predict_size(int w, int h); - /* Given a decoded frame, convert it to RGB format and fill -out_rgb with the result. Returns a AVFrame structure holding -additional information about the RGB frame, such as the number of -bytes in a row and so on. */ - const AVFrame& convert(const AVFrame &frame, unsigned char* out_rgb); -}; - -void disable_logging(); - -/* Wrappers, so we don't have to include libav headers. */ -std::pair width_height(const AVFrame&); -int row_size(const AVFrame&); - -/* all the documentation links - * My version of libav on ubuntu 16 appears to be from the release/11 branch on github - * Video decoding example: https://libav.org/documentation/doxygen/release/11/avcodec_8c_source.html#l00455 - * https://libav.org/documentation/doxygen/release/9/group__lavc__decoding.html - * https://libav.org/documentation/doxygen/release/11/group__lavc__parsing.html - * https://libav.org/documentation/doxygen/release/9/swscale_8h.html - * https://libav.org/documentation/doxygen/release/9/group__lavu.html - * https://libav.org/documentation/doxygen/release/9/group__lavc__picture.html - * http://dranger.com/ffmpeg/tutorial01.html - */ \ No newline at end of file diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder_python.cpp b/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder_python.cpp deleted file mode 100644 index 6c553a1..0000000 --- a/Tello_Video_With_Pose_Recognition/h264decoder/h264decoder_python.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include -#include - -// python string api, see -// https://docs.python.org/2/c-api/string.html -extern "C" { - #include -} - -#include -#include -#include -#include -#include -namespace py = boost::python; - -#include "h264decoder.hpp" - -using ubyte = unsigned char; - - -class GILScopedReverseLock -{ - // see https://docs.python.org/2/c-api/init.html (Releasing the GIL ...) -public: - GILScopedReverseLock() - : state(nullptr) - { - unlock(); - } - - ~GILScopedReverseLock() - { - lock(); - } - - void lock() - { - // Allow successive calls to lock. - // E.g. lock() followed by destructor. - if (state != nullptr) - { - PyEval_RestoreThread(state); - state = nullptr; - } - } - - void unlock() - { - assert (state == nullptr); - state = PyEval_SaveThread(); - } - - GILScopedReverseLock(const GILScopedReverseLock &) = delete; - GILScopedReverseLock(const GILScopedReverseLock &&) = delete; - GILScopedReverseLock operator=(const GILScopedReverseLock &) = delete; - GILScopedReverseLock operator=(const GILScopedReverseLock &&) = delete; -private: - PyThreadState *state; -}; - - -/* The class wrapped in python via boost::python */ -class PyH264Decoder -{ - H264Decoder decoder; - ConverterRGB24 converter; - - /* Extract frames from input stream. Stops at frame boundaries and returns the number of consumed bytes - * in num_consumed. - * - * If a frame is completed, is_frame_available is set to true, and the returned python tuple contains - * formation about the frame as well as the frame buffer memory. - * - * Else, i.e. all data in the buffer is consumed, is_frame_available is set to false. The returned tuple - * contains dummy data. - */ - py::tuple decode_frame_impl(const ubyte *data, ssize_t num, ssize_t &num_consumed, bool &is_frame_available); - -public: - /* Decoding style analogous to c/c++ way. Stop at frame boundaries. - * Return tuple containing frame data as above as nested tuple, and an integer telling how many bytes were consumed. */ - py::tuple decode_frame(const py::str &data_in_str); - /* Process all the input data and return a list of all contained frames. */ - py::list decode(const py::str &data_in_str); -}; - - -py::tuple PyH264Decoder::decode_frame_impl(const ubyte *data_in, ssize_t len, ssize_t &num_consumed, bool &is_frame_available) -{ - GILScopedReverseLock gilguard; - num_consumed = decoder.parse((ubyte*)data_in, len); - - if (is_frame_available = decoder.is_frame_available()) - { - const auto &frame = decoder.decode_frame(); - int w, h; std::tie(w,h) = width_height(frame); - Py_ssize_t out_size = converter.predict_size(w,h); - - gilguard.lock(); - // Construction of py::handle causes ... TODO: WHAT? No increase of ref count ?! - py::object py_out_str(py::handle<>(PyString_FromStringAndSize(NULL, out_size))); - char* out_buffer = PyString_AsString(py_out_str.ptr()); - - gilguard.unlock(); - const auto &rgbframe = converter.convert(frame, (ubyte*)out_buffer); - - gilguard.lock(); - return py::make_tuple(py_out_str, w, h, row_size(rgbframe)); - } - else - { - gilguard.lock(); - return py::make_tuple(py::object(), 0, 0, 0); - } -} - - -py::tuple PyH264Decoder::decode_frame(const py::str &data_in_str) -{ - ssize_t len = PyString_Size(data_in_str.ptr()); - const ubyte* data_in = (const ubyte*)(PyString_AsString(data_in_str.ptr())); - - ssize_t num_consumed = 0; - bool is_frame_available = false; - auto frame = decode_frame_impl(data_in, len, num_consumed, is_frame_available); - - return py::make_tuple(frame, num_consumed); -} - - -py::list PyH264Decoder::decode(const py::str &data_in_str) -{ - ssize_t len = PyString_Size(data_in_str.ptr()); - const ubyte* data_in = (const ubyte*)(PyString_AsString(data_in_str.ptr())); - - py::list out; - - try - { - while (len > 0) - { - ssize_t num_consumed = 0; - bool is_frame_available = false; - - try - { - auto frame = decode_frame_impl(data_in, len, num_consumed, is_frame_available); - if (is_frame_available) - { - out.append(frame); - } - } - catch (const H264DecodeFailure &e) - { - if (num_consumed <= 0) - // This case is fatal because we cannot continue to move ahead in the stream. - throw e; - } - - len -= num_consumed; - data_in += num_consumed; - } - } - catch (const H264DecodeFailure &e) - { - } - - return out; -} - - -BOOST_PYTHON_MODULE(libh264decoder) -{ - PyEval_InitThreads(); // need for release of the GIL (http://stackoverflow.com/questions/8009613/boost-python-not-supporting-parallelism) - py::class_("H264Decoder") - .def("decode_frame", &PyH264Decoder::decode_frame) - .def("decode", &PyH264Decoder::decode); - py::def("disable_logging", disable_logging); -} \ No newline at end of file diff --git a/Tello_Video_With_Pose_Recognition/h264decoder/readme.md b/Tello_Video_With_Pose_Recognition/h264decoder/readme.md deleted file mode 100644 index 2d912b1..0000000 --- a/Tello_Video_With_Pose_Recognition/h264decoder/readme.md +++ /dev/null @@ -1,35 +0,0 @@ -H264 Decoder Python Module -========================== - -The aim of this project is to provide a simple decoder for video -captured by a Raspberry Pi camera. At the time of this writing I only -need H264 decoding, since a H264 stream is what the RPi software -delivers. Furthermore flexibility to incorporate the decoder in larger -python programs in various ways is desirable. - -The code might also serve as example for libav and boost python usage. - - -Files ------ -* `h264decoder.hpp`, `h264decoder.cpp` and `h264decoder_python.cpp` contain the module code. - -* Other source files are tests and demos. - - -Requirements ------------- -* cmake for building -* libav -* boost python - - -Todo ----- - -* Add a video clip for testing and remove hard coded file names in demos/tests. - - -License -------- -The code is published under the Mozilla Public License v. 2.0. diff --git a/Tello_Video_With_Pose_Recognition/install/Linux/linux_install.sh b/Tello_Video_With_Pose_Recognition/install/Linux/linux_install.sh deleted file mode 100644 index 93f62f8..0000000 --- a/Tello_Video_With_Pose_Recognition/install/Linux/linux_install.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh - -echo 'Compiling and Installing the Tello Video Stream module' -echo 'You might need to enter your password' - -cd .. -cd .. -sudo apt-get update -y - -# install python 2.7 -sudo apt-get install python2.7 python-pip -y -sudo pip install --upgrade pip - -#switch to python2.7 -sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 150 -sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 100 - -sudo apt-get update -y - -# install cmake -#sudo apt-get install cmake -y -sudo pip install cmake - -# install dependencies -sudo apt-get install libboost-all-dev -y -sudo apt-get install libavcodec-dev -y -sudo apt-get install libswscale-dev -y -sudo apt-get install python-numpy -y -sudo apt-get install python-matplotlib -y -sudo pip install opencv-python -sudo apt-get install python-imaging-tk - -# pull and build h264 decoder library -cd h264decoder -mkdir build -cd build -cmake .. -make - -# copy source .so file to tello.py directory -cp libh264decoder.so ../../ - -echo 'Compilation and Installation Done!' diff --git a/Tello_Video_With_Pose_Recognition/install/Linux/linux_uninstall.sh b/Tello_Video_With_Pose_Recognition/install/Linux/linux_uninstall.sh deleted file mode 100644 index c2d4a3f..0000000 --- a/Tello_Video_With_Pose_Recognition/install/Linux/linux_uninstall.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -echo 'Uninstalling the Tello Video Stream module' - -cd .. -cd .. -sudo pip uninstall opencv-python -y -sudo pip uninstall cmake -y -sudo pip uninstall pip -y - -sudo apt-get remove libboost-all-dev -y -sudo apt-get remove libavcodec-dev -y -sudo apt-get remove libswscale-dev -y -sudo apt-get remove python-numpy -y -sudo apt-get remove python-matplotlib -y -sudo apt-get remove python-pil.imagetk -y - -sudo apt-get remove python-pip -y -sudo apt-get remove python2.7 -y - -sudo apt-get update -y - -rm ./libh264decoder.so -rm -r ./h264decoder/build -echo 'Uninstallation Done!' diff --git a/Tello_Video_With_Pose_Recognition/install/Mac/mac_install.sh b/Tello_Video_With_Pose_Recognition/install/Mac/mac_install.sh deleted file mode 100644 index 2b773ad..0000000 --- a/Tello_Video_With_Pose_Recognition/install/Mac/mac_install.sh +++ /dev/null @@ -1,76 +0,0 @@ - -#!/bin/sh - - - -echo 'Compiling and Installing the Tello Video Stream module' - -echo 'You might need to enter your password' - -# go to /sample_code folder -cd .. -cd .. - -# install Homebrew - -/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" - -brew update - - - -# install pip - -#sudo easy_install pip - -curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py -sudo python get-pip.py - -# install cmake - -brew install cmake - - - -# install dependencies - -brew install boost - -brew install boost-python - -brew install ffmpeg - -brew install tcl-tk - -sudo pip install numpy --ignore-installed - -sudo pip install matplotlib --ignore-installed - -sudo pip install pillow --ignore-installed - -sudo pip install opencv-python --ignore-installed - - - -# pull and build h264 decoder library - -cd h264decoder - - -mkdir build - -cd build - -cmake .. - -make - - - -# copy source .so file to tello.py directory - -cp libh264decoder.so ../../ - - - -echo 'Compilation and Installation Done!' diff --git a/Tello_Video_With_Pose_Recognition/install/Mac/mac_uninstall.sh b/Tello_Video_With_Pose_Recognition/install/Mac/mac_uninstall.sh deleted file mode 100644 index 579d482..0000000 --- a/Tello_Video_With_Pose_Recognition/install/Mac/mac_uninstall.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -echo 'Uninstalling the Tello Video Stream module' - -echo 'You might need to enter your password' - -# go to /sample_code folder -cd .. -cd .. - -brew update - -sudo pip uninstall matplotlib -y -sudo pip uninstall numpy -y -sudo pip install pillow -y -sudo pip install opencv-python -y -sudo pip uninstall pip -y - -brew uninstall tcl-tk -brew uninstall ffmpeg -brew uninstall boost-python -brew uninstall boost -brew uninstall cmake - -rm -f ./libh264decoder.so -rm -rf ./h264decoder/build - -echo 'Uninstallation Done!' diff --git a/Tello_Video_With_Pose_Recognition/install/Windows/install.bat b/Tello_Video_With_Pose_Recognition/install/Windows/install.bat deleted file mode 100644 index fe973e2..0000000 --- a/Tello_Video_With_Pose_Recognition/install/Windows/install.bat +++ /dev/null @@ -1,440 +0,0 @@ -echo=1/*>nul&@cls -@echo off -setlocal enableDelayedExpansion -::runas administrator -%1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit -cls -::setlocal -call :setdir -call :configx86orx64 -set extract=extract -set pythonLib="C:\Python27\Lib\site-packages\" -set /a maxRetry=3 -set /a retryCount=0 -echo ------------------------------------------------------ - -::-------------------down python2.7 and install------------------- -echo ------------------------------------------------------ -echo Downloading python2.7 -echo ------------------------------------------------------ -::עڿssltls汾֧֣ڽpythonܾʵ -REG ADD "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v SecureProtocols /t REG_DWORD /d 2728 /f >nul -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downpython - ) -) -if exist %pythonPackage% goto :downpythoncheck -:downpython -call :down %pythonDown% %pythonPackage% -:downpythoncheck -call :checkMD5 %pythonPackage% %pythonMD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downpythonend - ) else ( - echo Download %pythonPackage% failed. Retrying... !retryCount! of %maxRetry% - goto downpython - ) -) -call :installmsiPackage %pythonPackage% -::python2.7 -::wmicἴʱЧԽset -echo %PATH%|findstr "c:\python27" >nul -if %errorlevel% neq 0 ( - wmic ENVIRONMENT where "name='PATH' and username=''" set VariableValue="%PATH%;c:\python27" - set "path=%path%;c:\python27;" -) -echo %PATHEXT%|findstr ".PY;.PYM" >nul -if %errorlevel% neq 0 ( - wmic ENVIRONMENT where "name='PATHEXT' and username=''" set VariableValue="%PATHEXT%;.PY;.PYM" - set "pathext=%pathext%;.PY;.PYM;" -) -:downpythonend -::-------------------python pipİװ------------------- -echo ------------------------------------------------------ -echo Downloading pip -echo ------------------------------------------------------ -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downpip - ) -) -if exist %pipPackage% goto :downpipcheck -:downpip -call :down %pipDown% %pipPackage% -:downpipcheck -call :checkMD5 %pipPackage% %pipMD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downpipend - ) else ( - echo Download %pipPackage% failed. Retrying... !retryCount! of %maxRetry% - goto downpip - ) -) -python %pipPackage% -python -m pip install -U pip -:downpipend -::-------------------libboost-all-dev İװ------------------- -echo ------------------------------------------------------ -echo Downloading libboost -echo ------------------------------------------------------ -echo Please wait a few minutes... -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downlibboost - ) -) -if exist %libboostPackage% goto :downlibboostcheck -:downlibboost -call :down %libboostDown% %libboostPackage% -:downlibboostcheck -call :checkMD5 %libboostPackage% %libboostMD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downlibboostend - ) else ( - echo Download %libboostPackage% failed. Retrying... !retryCount! of %maxRetry% - goto downlibboost - ) -) -call %libboostPackage% /SILENT /NORESTART -:downlibboostend -::-------------------ffmpegİװ Ӧlinuxlibavcodec-dev libswscale-dev2------------------- -echo ------------------------------------------------------ -echo Downloading ffmpeg -echo ------------------------------------------------------ -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downffmpeg - ) -) -if exist %ffmpegPackage% goto :downffmpegcheck -:downffmpeg -call :down %ffmpegDown% %ffmpegPackage% -:downffmpegcheck -call :checkMD5 %ffmpegPackage% %ffmpegMD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downffmpegend - ) else ( - echo Download %ffmpegPackage% failed. Retrying... !retryCount! of %maxRetry% - goto downffmpeg - ) -) -call :unpack %ffmpegPackage% %extract% -:downffmpegend -echo ------------------------------------------------------ -echo Downloading VS2013 runtime -echo ------------------------------------------------------ -set /a retryCount=0 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - goto downvs2013 - ) -) -if exist %vs2013package% goto :downvs2013check -:downvs2013 -call :down %vs2013depend% %vs2013package% -:downvs2013check -call :checkMD5 %vs2013package% %vs2013MD5% MD5pass -if "%MD5pass%" == "NO" ( - set /a retryCount=!retryCount!+1 && if !retryCount! == %maxRetry% ( - echo Retried %maxRetry% times, all failed. Skip. - goto downvs2013end - ) else ( - echo Download %vs2013package% failed. Retrying... !retryCount! of %maxRetry% - goto downvs2013 - ) -) -call %vs2013package% /passive /NORESTART -:downvs2013end -::-------------------python-numpy python-matplotlib opencv-pythonİװpipʽ------------------- -echo ------------------------------------------------------ -echo Downloading numpy -echo ------------------------------------------------------ -python -m pip install numpy -echo ------------------------------------------------------ -echo Downloading matplotlib -echo ------------------------------------------------------ -python -m pip install matplotlib -echo ------------------------------------------------------ -echo Downloading opencv-python -echo ------------------------------------------------------ -python -m pip install -v opencv-python==3.4.2.17 -echo ------------------------------------------------------ -echo Downloading pillow -echo ------------------------------------------------------ -python -m pip install pillow -:copydependencies -::-------------------еdllȥc:\python27\lib\site-packages------------------- -echo ------------------------------------------------------ -echo Copying dependencies -echo ------------------------------------------------------ -echo %extract%\%ffmpegPackage:~0,-4%\bin\ -echo %libboostPackageCopy% -echo %libh264% -xcopy /Y /E /I %extract%\%ffmpegPackage:~0,-4%\bin\*.dll %pythonLib% -xcopy /Y /E /I %libboostPackageCopy% %pythonLib% -xcopy /Y /E /I %libh264%\*.pyd %pythonLib% -endlocal -echo ------------------------------------------------------ -echo Installation done. -echo ------------------------------------------------------ -pause -goto :eof - -::-------------------------------------------------------------------------------------------- - -::-----------------MD5Աȶ------------------ -:checkMD5 -set file=%~1 -call :MD5get %file% MD5 -if "%MD5%" equ "%~2" ( - echo MD5 identical. - set "%~3=YES" -) else ( - if "%MD5%" equ "skip" ( - echo MD5 check skipped. - set "%~3=YES" - ) else ( - echo Warning: MD5 does not match! - set "%~3=NO" - ) -) -goto :eof - -::-----------------MD5ȡ------------------ -:MD5get -echo %~1 -for %%# in (certutil.exe) do ( - if not exist "%%~f$PATH:#" ( - echo certutil.exe not found - echo for Windows XP professional and Windows 2003 - echo you need Windows Server 2003 Administration Tools Pack - echo https://www.microsoft.com/en-us/download/details.aspx?id=16770 - echo now skip the MD5 check - if "%~2" neq "" ( - set "%~2=skip" - ) - ::exit /b 4 - goto :eof - ) -) - -set "md5=" -for /f "skip=1 tokens=* delims=" %%# in ('certutil -hashfile "%~f1" MD5') do ( - if not defined md5 ( - for %%Z in (%%#) do set "md5=!md5!%%Z" - ) -) - -if "%~2" neq "" ( - set "%~2=%md5%" -) else ( - echo %md5% -) - -goto :eof - -::-----------------Ŀ¼л------------------ -::ڹԱģʽִʱĬ·˴Ŀ¼л -:setdir -set char=%~dp0% -%char:~0,2% -cd %~dp0% -goto :eof - -::-----------------ǰ汾------------------ -:configx86orx64 -IF %PROCESSOR_ARCHITECTURE% == AMD64 ( - set versionFlag=win64 -) else ( - set versionFlag=win32 -) - -echo Windows Version: %versionFlag% -if %versionFlag%==win64 ( - set pythonDown="https://www.python.org/ftp/python/2.7.15/python-2.7.15.amd64.msi" - set pythonPackage=python-2.7.15.amd64.msi - set pythonMD5="0ffa44a86522f9a37b916b361eebc552" - - set pipDown="https://bootstrap.pypa.io/get-pip.py" - set pipPackage=get-pip.py - set pipMD5="da117d21d1fd79c0445a6af027f13580" - - set ffmpegDown="https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-20180825-844ff49-win64-shared.zip" - set ffmpegPackage=ffmpeg-20180825-844ff49-win64-shared.zip - set ffmpegMD5="37de63b02196cf9e09c7adf2924c143a" - - set libboostDown="https://nchc.dl.sourceforge.net/project/boost/boost-binaries/1.68.0/boost_1_68_0-msvc-12.0-64.exe" - set libboostPackage="boost_1_68_0-msvc-12.0-64.exe" - set libboostPackageCopy="c:\local\boost_1_68_0\lib64-msvc-12.0\boost_python27-vc120-mt-x64-1_68.dll" - set libboostMD5="4e6b11a971502639ba5cc564c7f2d568" - - set libh264=..\..\h264decoder\windows\x64 - - set vs2013depend="https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe" - set vs2013package=vcredist_x64.exe - set vs2013MD5="96b61b8e069832e6b809f24ea74567ba" - -) else ( - set pythonDown="https://www.python.org/ftp/python/2.7.15/python-2.7.15.msi" - set pythonPackage=python-2.7.15.msi - set pythonMD5="023e49c9fba54914ebc05c4662a93ffe" - - set pipDown="https://bootstrap.pypa.io/get-pip.py" - set pipPackage=get-pip.py - set pipMD5="da117d21d1fd79c0445a6af027f13580" - - set ffmpegDown="https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-20180825-844ff49-win32-shared.zip" - set ffmpegPackage=ffmpeg-20180825-844ff49-win32-shared.zip - set ffmpegMD5="8e70ae7338083a311c055aa44aa5407d" - - set libboostDown="https://excellmedia.dl.sourceforge.net/project/boost/boost-binaries/1.68.0/boost_1_68_0-msvc-12.0-32.exe" - set libboostPackage="boost_1_68_0-msvc-12.0-32.exe" - set libboostPackageCopy="c:\local\boost_1_68_0\lib32-msvc-12.0\boost_python27-vc120-mt-x32-1_68.dll" - set libboostMD5="d5d5ee205c87078245eb7df72789f407" - - set libh264=..\..\h264decoder\windows\x86 - - set vs2013depend="https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x86.exe" - set vs2013package=vcredist_x86.exe - set vs2013MD5="0fc525b6b7b96a87523daa7a0013c69d" -) - -goto :eof - -::-----------------h264------------------ -:h264install -cd h264decoder -if exist build ( - rd /s /q build - mkdir build -) else ( - mkdir build -) -cd build -cmake .. -make -goto :eof - -::-----------------غ------------------ -:down -echo Source: "%~1" -echo Destination: "%~f2" -echo Start downloading "%~2"... -cscript -nologo -e:jscript "%~f0" "download" "%~1" "%~2" -::echo Download "%~2" OK! -echo ------------------------------------------------------ -goto :eof - -::-----------------ǽѹ------------------ -:unpack -echo Source: "%~f1" -echo Destination: "%~f2" -echo Start unpacking "%~1"... -cscript -nologo -e:jscript "%~f0" "unpack" "%~1" "%~2" "%~dp0" -echo Unpack "%~1" OK! -echo ------------------------------------------------------ -goto :eof -::-----------------ǰװ------------------ -:installmsiPackage -echo Source: "%~f1" -echo Strat installing "%~f1"... -msiexec /i "%~f1" /passive -echo install "%~1" OK! -echo ------------------------------------------------------ -goto :eof -*/ - -function download(DownSource, DownDestination) -{ - var DownPost - ,DownGet; - - DownDestination=DownDestination.toLowerCase(); - DownSource=DownSource.toLowerCase(); - //DownPost = new ActiveXObject("Msxml2"+String.fromCharCode(0x2e)+"ServerXMLHTTP"); - //DownPost = new ActiveXObject("Microsoft"+String.fromCharCode(0x2e)+"XMLHTTP"); - //DownPost.setOption(2, 13056); - var DownPost=null; - try{ - DownPost=new XMLHttpRequest(); - }catch(e){ - try{ - DownPost=new ActiveXObject("Msxml2.XMLHTTP"); - DownPost.setOption(2, 13056); - }catch(ex){ - try{ - DownPost=new ActiveXObject("Microsoft.XMLHTTP"); - }catch(e3){ - DownPost=null; - } - } - } - DownPost.open("GET",DownSource,0); - DownPost.send(); - DownGet = new ActiveXObject("ADODB"+String.fromCharCode(0x2e)+"Stream"); - DownGet.Mode = 3; - DownGet.Type = 1; - DownGet.Open(); - DownGet.Write(DownPost.responseBody); - DownGet.SaveToFile(DownDestination,2); -} - -function unpack(PackedFileSource, UnpackFileDestination, ParentFolder) -{ - var FileSysObject = new Object - ,ShellObject = new ActiveXObject("Shell.Application") - ,intOptions = 4 + 16 - ,DestinationObj - ,SourceObj; - - if (!UnpackFileDestination) UnpackFileDestination = '.'; - var FolderTest = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); - FileSysObject = ShellObject.NameSpace(ParentFolder); - while (!FolderTest) - { - WSH.Echo ('Unpack Destination Folder Not Exist, Creating...'); - FileSysObject.NewFolder(UnpackFileDestination); - FolderTest = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); - if (FolderTest) - WSH.Echo('Unpack Destination Folder Created.'); - } - DestinationObj = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); - SourceObj = ShellObject.NameSpace(ParentFolder + PackedFileSource); - for (var i = 0; i < SourceObj.Items().Count; i++) - { - try { - if (SourceObj) { - WSH.Echo('Unpacking ' + SourceObj.Items().Item(i) + '... '); - DestinationObj.CopyHere(SourceObj.Items().Item(i), intOptions); - WSH.Echo('Unpack ' + SourceObj.Items().Item(i) + ' Done.'); - } - } - catch(e) { - WSH.Echo('Failed: ' + e); - } - } -} - -switch (WScript.Arguments(0)){ - case "download": - download(WScript.Arguments(1), WScript.Arguments(2)); - break; - case "unpack": - unpack(WScript.Arguments(1), WScript.Arguments(2), WScript.Arguments(3)); - break; - default: -} - diff --git a/Tello_Video_With_Pose_Recognition/install/Windows/uninstall.bat b/Tello_Video_With_Pose_Recognition/install/Windows/uninstall.bat deleted file mode 100644 index 12e4b81..0000000 --- a/Tello_Video_With_Pose_Recognition/install/Windows/uninstall.bat +++ /dev/null @@ -1,92 +0,0 @@ -@echo off -::runas administrator -%1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit -setlocal enabledelayedexpansion -call :setdir -call :configx86orx64 -set "pythondir=c:\python27" -set "libboostdir=c:\local\boost_1_68_0" -set "logfile=%~dp0\UnInstallLog.txt" -set extract=extract -echo ------------------------------------------------------ -echo Uninstalling python27 -echo ------------------------------------------------------ -call :uninstallmsiPackage "%python27Name%" "%python27Name%" -if exist %pythondir% rmdir /s /q %pythondir% -if exist %pythondir% rd /s /q %pythondir% -set delPythonPath=%PATH:;c:\python27=% -set delPythonExt=%PATHEXT:;.PY;.PYM=% -wmic ENVIRONMENT where "name='PATH' and username=''" set VariableValue="%delPythonPath%" -wmic ENVIRONMENT where "name='PATHEXT' and username=''" set VariableValue="%delPythonExt%" -echo ------------------------------------------------------ -echo Uninstalling libboost -echo ------------------------------------------------------ -if exist %libboostdir% rd /s /q %libboostdir% -echo ------------------------------------------------------ -echo Uninstalling ffmpeg -echo ------------------------------------------------------ -if exist %extract% rd /s /q %extract% -echo ------------------------------------------------------ -echo Cleaning downloaded files -echo ------------------------------------------------------ -del /f /q %pythonPackage% -del /f /q %pipPackage% -del /f /q %ffmpegPackage% -del /f /q %libboostPackage% -echo ------------------------------------------------------ -echo Uninstallation done. -echo ------------------------------------------------------ -pause -goto :eof - -::-----------------Ŀ¼л----------------- -::ڹԱģʽִʱĬ·˴Ŀ¼л -:setdir -set char=%~dp0% -%char:~0,2% -cd %~dp0% -goto :eof - -::-----------------ǰ汾----------------- -:configx86orx64 -IF %PROCESSOR_ARCHITECTURE% == AMD64 ( - set versionFlag=win64 -) else ( - set versionFlag=win32 -) - -echo Windows Version: %versionFlag% -if %versionFlag%==win64 ( - set "python27Name=python 2.7.15 (64-bit)" - set "python27ProductCode=20C31435-2A0A-4580-BE8B-AC06FC243CA5" - set pythonPackage=python-2.7.15.amd64.msi - set pipPackage=get-pip.py - set ffmpegPackage=ffmpeg-20180825-844ff49-win64-shared.zip - set libboostPackage="boost_1_68_0-msvc-12.0-64.exe" -) else ( - set "python27Name=Python 2.7.15" - set "python27ProductCode=20C31435-2A0A-4580-BE8B-AC06FC243CA4" - set pythonPackage=python-2.7.15.msi - set pipPackage=get-pip.py - set ffmpegPackage=ffmpeg-20180825-844ff49-win32-shared.zip - set libboostPackage="boost_1_68_0-msvc-12.0-32.exe" -) -endlocal -goto :eof - -::-----------------жغ----------------- -:uninstallmsiPackage -echo Strat uninstalling "%~2"... -echo "%~2" Start...%date% %time%>>%logfile% -FOR /f "tokens=1" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /e /s /d /f "%~2"^|FINDSTR /i "CurrentVersion"') do ( - FOR /f "tokens=1-2,*" %%j in ('reg query "%%i" /f "UninstallString"^|FINDSTR /i "UninstallString"') do ( - set correct=%%l && set correct=!correct:/I{=/passive /norestart /x{! && CMD /q /c "!correct!" - ) -) - -IF %ERRORLEVEL% EQU 0 echo Uninstall %~2 Successfull -IF %ERRORLEVEL% NEQ 0 IF %ERRORLEVEL% NEQ 1605 echo Uninstall %~2 Failed, ERRORLEVEL:%ERRORLEVEL%. -echo ERRORLEVEL:%ERRORLEVEL%>>%logfile% -echo %~2 End %date% %time%>>%logfile% -echo ----------------------------------------------------- -goto :eof \ No newline at end of file diff --git a/Tello_Video_With_Pose_Recognition/requirements.txt b/Tello_Video_With_Pose_Recognition/requirements.txt new file mode 100644 index 0000000..9498a18 --- /dev/null +++ b/Tello_Video_With_Pose_Recognition/requirements.txt @@ -0,0 +1,5 @@ +matplotlib +numpy +Pillow +git+https://github.com/DaWelter/h264decoder.git +opencv-python \ No newline at end of file diff --git a/Tello_Video_With_Pose_Recognition/tello .py b/Tello_Video_With_Pose_Recognition/tello .py new file mode 100644 index 0000000..856c3c5 --- /dev/null +++ b/Tello_Video_With_Pose_Recognition/tello .py @@ -0,0 +1,459 @@ +import socket +import threading +import time +import numpy as np +import h264decoder + +class Tello: + """Wrapper class to interact with the Tello drone.""" + + def __init__(self, local_ip, local_port, imperial=False, command_timeout=.3, tello_ip='192.168.10.1', + tello_port=8889): + """ + Binds to the local IP/port and puts the Tello into command mode. + + :param local_ip (str): Local IP address to bind. + :param local_port (int): Local port to bind. + :param imperial (bool): If True, speed is MPH and distance is feet. + If False, speed is KPH and distance is meters. + :param command_timeout (int|float): Number of seconds to wait for a response to a command. + :param tello_ip (str): Tello IP. + :param tello_port (int): Tello port. + """ + + self.abort_flag = False + self.decoder = h264decoder.H264Decoder() + self.command_timeout = command_timeout + self.imperial = imperial + self.response = None + self.frame = None # numpy array BGR -- current camera output frame + self.is_freeze = False # freeze current camera output + self.last_frame = None + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for sending cmd + self.socket_video = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for receiving video stream + self.tello_address = (tello_ip, tello_port) + self.local_video_port = 11111 # port for receiving video stream + self.last_height = 0 + self.socket.bind((local_ip, local_port)) + + # thread for receiving cmd ack + self.receive_thread = threading.Thread(target=self._receive_thread) + self.receive_thread.daemon = True + + self.receive_thread.start() + + # to receive video -- send cmd: command, streamon + self.socket.sendto(b'command', self.tello_address) + print ('sent: command') + self.socket.sendto(b'streamon', self.tello_address) + print ('sent: streamon') + + self.socket_video.bind((local_ip, self.local_video_port)) + + # thread for receiving video + self.receive_video_thread = threading.Thread(target=self._receive_video_thread) + self.receive_video_thread.daemon = True + + self.receive_video_thread.start() + + def __del__(self): + """Closes the local socket.""" + + self.socket.close() + self.socket_video.close() + + def read(self): + """Return the last frame from camera.""" + if self.is_freeze: + return self.last_frame + else: + return self.frame + + def video_freeze(self, is_freeze=True): + """Pause video output -- set is_freeze to True""" + self.is_freeze = is_freeze + if is_freeze: + self.last_frame = self.frame + + def _receive_thread(self): + """Listen to responses from the Tello. + + Runs as a thread, sets self.response to whatever the Tello last returned. + + """ + while True: + try: + self.response, ip = self.socket.recvfrom(3000) + #print(self.response) + except socket.error as exc: + print ("Caught exception socket.error : %s" % exc) + + def _receive_video_thread(self): + """ + Listens for video streaming (raw h264) from the Tello. + + Runs as a thread, sets self.frame to the most recent frame Tello captured. + + """ + packet_data = b"" + while True: + try: + res_string, ip = self.socket_video.recvfrom(2048) + packet_data += res_string + # end of frame + if len(res_string) != 1460: + for frame in self._h264_decode(packet_data): + self.frame = frame + packet_data = b"" + + except socket.error as exc: + print ("Caught exception socket.error : %s" % exc) + + def _h264_decode(self, packet_data): + """ + decode raw h264 format data from Tello + + :param packet_data: raw h264 data array + + :return: a list of decoded frame + """ + res_frame_list = [] + frames = self.decoder.decode(packet_data) + for framedata in frames: + (frame, w, h, ls) = framedata + if frame is not None: + # print 'frame size %i bytes, w %i, h %i, linesize %i' % (len(frame), w, h, ls) + + frame = np.fromstring(frame, dtype=np.ubyte, count=len(frame), sep='') + frame = (frame.reshape((h, ls // 3, 3))) + frame = frame[:, :w, :] + res_frame_list.append(frame) + + return res_frame_list + + def send_command(self, command): + """ + Send a command to the Tello and wait for a response. + + :param command: Command to send. + :return (str): Response from Tello. + + """ + + print (">> send cmd: {}".format(command)) + self.abort_flag = False + timer = threading.Timer(self.command_timeout, self.set_abort_flag) + + self.socket.sendto(command.encode('utf-8'), self.tello_address) + + timer.start() + while self.response is None: + if self.abort_flag is True: + break + timer.cancel() + + if self.response is None: + response = 'none_response' + else: + response = self.response.decode('utf-8') + + self.response = None + + return response + + def set_abort_flag(self): + """ + Sets self.abort_flag to True. + + Used by the timer in Tello.send_command() to indicate to that a response + + timeout has occurred. + + """ + + self.abort_flag = True + + def takeoff(self): + """ + Initiates take-off. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.send_command('takeoff') + + def set_speed(self, speed): + """ + Sets speed. + + This method expects KPH or MPH. The Tello API expects speeds from + 1 to 100 centimeters/second. + + Metric: .1 to 3.6 KPH + Imperial: .1 to 2.2 MPH + + Args: + speed (int|float): Speed. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + speed = float(speed) + + if self.imperial is True: + speed = int(round(speed * 44.704)) + else: + speed = int(round(speed * 27.7778)) + + return self.send_command('speed %s' % speed) + + def rotate_cw(self, degrees): + """ + Rotates clockwise. + + Args: + degrees (int): Degrees to rotate, 1 to 360. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.send_command('cw %s' % degrees) + + def rotate_ccw(self, degrees): + """ + Rotates counter-clockwise. + + Args: + degrees (int): Degrees to rotate, 1 to 360. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + return self.send_command('ccw %s' % degrees) + + def flip(self, direction): + """ + Flips. + + Args: + direction (str): Direction to flip, 'l', 'r', 'f', 'b'. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.send_command('flip %s' % direction) + + def get_response(self): + """ + Returns response of tello. + + Returns: + int: response of tello. + + """ + response = self.response + return response + + def get_height(self): + """Returns height(dm) of tello. + + Returns: + int: Height(dm) of tello. + + """ + height = self.send_command('height?') + height = str(height) + height = filter(str.isdigit, height) + try: + height = int(height) + self.last_height = height + except: + height = self.last_height + pass + return height + + def get_battery(self): + """Returns percent battery life remaining. + + Returns: + int: Percent battery life remaining. + + """ + + battery = self.send_command('battery?') + + try: + battery = int(battery) + except: + pass + + return battery + + def get_flight_time(self): + """Returns the number of seconds elapsed during flight. + + Returns: + int: Seconds elapsed during flight. + + """ + + flight_time = self.send_command('time?') + + try: + flight_time = int(flight_time) + except: + pass + + return flight_time + + def get_speed(self): + """Returns the current speed. + + Returns: + int: Current speed in KPH or MPH. + + """ + + speed = self.send_command('speed?') + + try: + speed = float(speed) + + if self.imperial is True: + speed = round((speed / 44.704), 1) + else: + speed = round((speed / 27.7778), 1) + except: + pass + + return speed + + def land(self): + """Initiates landing. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.send_command('land') + + def move(self, direction, distance): + """Moves in a direction for a distance. + + This method expects meters or feet. The Tello API expects distances + from 20 to 500 centimeters. + + Metric: .02 to 5 meters + Imperial: .7 to 16.4 feet + + Args: + direction (str): Direction to move, 'forward', 'back', 'right' or 'left'. + distance (int|float): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + distance = float(distance) + + if self.imperial is True: + distance = int(round(distance * 30.48)) + else: + distance = int(round(distance * 100)) + + return self.send_command('%s %s' % (direction, distance)) + + def move_backward(self, distance): + """Moves backward for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.move('back', distance) + + def move_down(self, distance): + """Moves down for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.move('down', distance) + + def move_forward(self, distance): + """Moves forward for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + return self.move('forward', distance) + + def move_left(self, distance): + """Moves left for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + return self.move('left', distance) + + def move_right(self, distance): + """Moves right for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + """ + return self.move('right', distance) + + def move_up(self, distance): + """Moves up for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.move('up', distance) diff --git a/Tello_Video_With_Pose_Recognition/tello_control_ui.py b/Tello_Video_With_Pose_Recognition/tello_control_ui.py index dd6af64..f1c18bf 100644 --- a/Tello_Video_With_Pose_Recognition/tello_control_ui.py +++ b/Tello_Video_With_Pose_Recognition/tello_control_ui.py @@ -1,7 +1,7 @@ from PIL import Image from PIL import ImageTk -import Tkinter as tki -from Tkinter import Toplevel, Scale +import tkinter as tki +from tkinter import Toplevel, Scale import threading import datetime import cv2 @@ -119,7 +119,7 @@ def videoLoop(self): elif cmd == 'land': self.telloLanding() - except RuntimeError, e: + except RuntimeError as e: print("[INFO] caught a RuntimeError") def _getGUIImage(self): @@ -207,7 +207,7 @@ def _autoTakeoff(self): if self.quit_waiting_flag is True: break response = self.tello.get_response() - print "ack:%s"%response + print("ack:%s"%response) timer.cancel() # receive the correct response @@ -221,7 +221,7 @@ def _autoTakeoff(self): try: height_val = height_val + height_tmp cnt = cnt + 1 - print height_tmp,cnt + print(height_tmp,cnt) except: height_val = height_val @@ -361,7 +361,7 @@ def takeSnapshot(self): # save the file cv2.imwrite(p, cv2.cvtColor(self.frame, cv2.COLOR_RGB2BGR)) - print("[INFO] saved {}".format(filename)) + print(("[INFO] saved {}".format(filename))) def setPoseMode(self): """ @@ -403,7 +403,7 @@ def telloTakeOff(self): if takeoff_response != 'error': self.auto_takeoff_thread.start() else: - print "battery low,please repalce with a new one" + print("battery low,please repalce with a new one") def telloLanding(self): return self.tello.land() @@ -449,42 +449,42 @@ def updateTrackBar(self): def updateDistancebar(self): self.distance = self.distance_bar.get() - print 'reset distance to %.1f' % self.distance + print('reset distance to %.1f' % self.distance) def updateDegreebar(self): self.degree = self.degree_bar.get() - print 'reset distance to %d' % self.degree + print('reset distance to %d' % self.degree) def on_keypress_w(self, event): - print "up %d m" % self.distance + print("up %d m" % self.distance) self.telloUp(self.distance) def on_keypress_s(self, event): - print "down %d m" % self.distance + print("down %d m" % self.distance) self.telloDown(self.distance) def on_keypress_a(self, event): - print "ccw %d degree" % self.degree + print("ccw %d degree" % self.degree) self.tello.rotate_ccw(self.degree) def on_keypress_d(self, event): - print "cw %d m" % self.degree + print("cw %d m" % self.degree) self.tello.rotate_cw(self.degree) def on_keypress_up(self, event): - print "forward %d m" % self.distance + print("forward %d m" % self.distance) self.telloMoveForward(self.distance) def on_keypress_down(self, event): - print "backward %d m" % self.distance + print("backward %d m" % self.distance) self.telloMoveBackward(self.distance) def on_keypress_left(self, event): - print "left %d m" % self.distance + print("left %d m" % self.distance) self.telloMoveLeft(self.distance) def on_keypress_right(self, event): - print "right %d m" % self.distance + print("right %d m" % self.distance) self.telloMoveRight(self.distance) def on_keypress_enter(self, event): diff --git a/Tello_Video_With_Pose_Recognition/tello_pose.py b/Tello_Video_With_Pose_Recognition/tello_pose.py index aa1de38..fd90c90 100644 --- a/Tello_Video_With_Pose_Recognition/tello_pose.py +++ b/Tello_Video_With_Pose_Recognition/tello_pose.py @@ -244,15 +244,15 @@ def detect(self, frame): # check the captured pose if self.is_arms_down_45(points): self.arm_down_45_cnt += 1 - print "%d:arm down captured"%self.frame_cnt + print("%d:arm down captured"%self.frame_cnt) if self.is_arms_flat(points): self.arm_flat_cnt += 1 - print "%d:arm up captured"%self.frame_cnt + print("%d:arm up captured"%self.frame_cnt) if self.is_arms_V(points): self.arm_V_cnt += 1 - print '%d:arm V captured'%self.frame_cnt + print('%d:arm V captured'%self.frame_cnt) self.frame_cnt += 1 @@ -273,13 +273,13 @@ def detect(self, frame): # certain times of pose recognition if self.frame_cnt == frame_cnt_threshold: if self.arm_down_45_cnt >= pose_captured_threshold: - print '!!!arm up,move back!!!' + print('!!!arm up,move back!!!') cmd = 'moveback' if self.arm_flat_cnt >= pose_captured_threshold: - print '!!!arm down,moveforward!!!' + print('!!!arm down,moveforward!!!') cmd = 'moveforward' if self.arm_V_cnt == self.frame_cnt : - print '!!!arm V,land!!!' + print('!!!arm V,land!!!') cmd = 'land' self.frame_cnt = 0 self.arm_down_45_cnt = 0 diff --git a/Tello_Video_With_QR_Detection/LICENSE.md b/Tello_Video_With_QR_Detection/LICENSE.md new file mode 100644 index 0000000..96bfbb7 --- /dev/null +++ b/Tello_Video_With_QR_Detection/LICENSE.md @@ -0,0 +1,21 @@ +Sample code is offered under MIT License (See below). + +Copyright 2018 Ryze + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Tello_Video_With_QR_Detection/README.md b/Tello_Video_With_QR_Detection/README.md new file mode 100644 index 0000000..41b3676 --- /dev/null +++ b/Tello_Video_With_QR_Detection/README.md @@ -0,0 +1,123 @@ +# Tello-Video + +This is an example using the Tello SDK v1.3.0.0 and above to receive video stream from Tello camera,decode the video stream and show the image by GUI. + + - Written in Python 2.7 + - Tello SDK v1.3.0.0 and above(with h.264 video streaming) + - This example includes a simple UI build with Tkinter to interact with Tello + - Interactive control of Tello based on human movement is achieved via body pose recognition module. + +## Prerequisites + +- Python2.7 +- pip +- Python OpenCV +- Numpy +- PIL +- libboost-python +- Tkinter +- homebrew(for mac) +- Python h264 decoder + - + +## Installation + +In order to facilitate you to install python2.7 and various dependencies, we have written a one-click installation script for windows, Linux and macos. You can choose to run this script for the one-click installation, or you can download python2.7 and related libraries and dependencies online. If you have questions about the actions that the script performs, you can open the script with an editor and look up the comments for each instruction in the script. In addition, we have additionally written an uninstall script that cleans and restores all downloaded and configured content from the one-click installation script. + +- **Windows** + + Go to the "install\Windows" folder,select and run the correct "windows_install.bat" according to your computer operating system bits. + +- **Linux (Ubuntu 14.04 and above)** + + Go to the "install\Linux" folder in command line, run + + ``` + chmod +x linux_install.sh + ./linux_install.sh + ``` + +- **Mac** + + 1. Make sure you have the latest Xcode command line tools installed. If not, you might need to update your OS X and XCode to the latest version in order to compile the h264 decoder module + 2. Go to the "install\Mac" folder folder in command line, run + + ``` + chmod a+x ./mac_install.sh + ./mac_install.sh + ``` + + If you see no errors during installation, you are good to go! + +## Run the project +- **Step1**. Turn on Tello and connect your computer device to Tello via wifi. + + +- **Step2**. Open project folder in terminal. Run: + + ``` + python main.py + ``` + +- **Step3**. A UI will show up, you can now: + + - Watch live video stream from the Tello camera; + - Take snapshot and save jpg to local folder; + - Open Command Panel, which allows you to: + - Take Off + - Land + - Flip (in forward, backward, left and right direction) + - Control Tello using keyboard inputs: + - **[key-Up]** move forward 20cm + - **[key-Down]** move backward 20cm + - **[key-Left]** move left 20 cm + - **[key-Right]** move right 20 cm + - **[key-w]** move up 20cm + - **[key-s]** move down 20cm + - **[key-a]** rotate counter-clockwise by 30 degree + - **[key-d]** rotate clockwise by 30 degree + - You can also adjust the **distance** and **degree** via the trackbar and hit the "reset distance" or "reset degree" button to customize your own control. + +## Project Description + +### tello.py - class Tello + +Wrapper class to interact with Tello drone. +Modified from + +The object starts 2 threads: + + 1. thread for receiving command response from Tello + 2. thread for receiving video stream + +You can use **read()** to read the last frame from Tello camera, and pause the video by setting **video_freeze(is_freeze=True)**. + +### tello_control_ui.py - class TelloUI + +Modified from: https://www.pyimagesearch.com/2016/05/30/displaying-a-video-feed-with-opencv-and-tkinter/ + +Build with Tkinter. Display video, control video play/pause and control Tello using buttons and arrow keys. + +### h264decoder - class libh264decoder + +From . + +A c++ based class that decodes raw h264 data. This module interacts with python language via python-libboost library, and its decoding functionality is based on ffmpeg library. + +After compilation, a libh264decoder.so or libh264decoder.pyd file will be placed in the working directory so that the main python file can reference it. + +If you have to compile it from source,with Linux or Mac,you can: + +``` +cd h264decoder +mkdir build +cd build +cmake .. +make +cp libh264decoder.so ../../ +``` +With Windows,you can create a project through visual studio, add files in h264decoder and dependencies such as ffmpeg and libboost, compile the project and generate a libh264decoder.pyd file.We have generated a libh264decoder.pyd and put it in the "\h264decoder\Windows"foleder so that you can copy put it to "python/site-package". + +##Contact Information + +If you have any questions about this sample code and the installation, please feel free to contact me. You can communicate with me by sending e-mail to sdk@ryzerobotics.com. diff --git a/Tello_Video_With_QR_Detection/img/readme.md b/Tello_Video_With_QR_Detection/img/readme.md new file mode 100644 index 0000000..d9023d8 --- /dev/null +++ b/Tello_Video_With_QR_Detection/img/readme.md @@ -0,0 +1 @@ +This folder mainly stores photos taken by the tello front view camera. \ No newline at end of file diff --git a/Tello_Video_With_QR_Detection/main.py b/Tello_Video_With_QR_Detection/main.py new file mode 100644 index 0000000..735bcf2 --- /dev/null +++ b/Tello_Video_With_QR_Detection/main.py @@ -0,0 +1,14 @@ +import tello, time + +video=True + +drone = tello.Tello('', 8889, video=video) +print("battery:",drone.get_battery(),"%") +drone.takeoff() + +"""ここから""" +time.sleep(10) +"""ここまで""" + +drone.land() +drone.close() \ No newline at end of file diff --git a/Tello_Video_With_QR_Detection/requirements.txt b/Tello_Video_With_QR_Detection/requirements.txt new file mode 100644 index 0000000..bea982f --- /dev/null +++ b/Tello_Video_With_QR_Detection/requirements.txt @@ -0,0 +1,4 @@ +matplotlib +numpy +Pillow +git+https://github.com/DaWelter/h264decoder.git \ No newline at end of file diff --git a/Tello_Video_With_Pose_Recognition/tello.py b/Tello_Video_With_QR_Detection/tello.py similarity index 83% rename from Tello_Video_With_Pose_Recognition/tello.py rename to Tello_Video_With_QR_Detection/tello.py index 530a2d7..565b736 100644 --- a/Tello_Video_With_Pose_Recognition/tello.py +++ b/Tello_Video_With_QR_Detection/tello.py @@ -1,459 +1,480 @@ -import socket -import threading -import time -import numpy as np -import libh264decoder - -class Tello: - """Wrapper class to interact with the Tello drone.""" - - def __init__(self, local_ip, local_port, imperial=False, command_timeout=.3, tello_ip='192.168.10.1', - tello_port=8889): - """ - Binds to the local IP/port and puts the Tello into command mode. - - :param local_ip (str): Local IP address to bind. - :param local_port (int): Local port to bind. - :param imperial (bool): If True, speed is MPH and distance is feet. - If False, speed is KPH and distance is meters. - :param command_timeout (int|float): Number of seconds to wait for a response to a command. - :param tello_ip (str): Tello IP. - :param tello_port (int): Tello port. - """ - - self.abort_flag = False - self.decoder = libh264decoder.H264Decoder() - self.command_timeout = command_timeout - self.imperial = imperial - self.response = None - self.frame = None # numpy array BGR -- current camera output frame - self.is_freeze = False # freeze current camera output - self.last_frame = None - self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for sending cmd - self.socket_video = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for receiving video stream - self.tello_address = (tello_ip, tello_port) - self.local_video_port = 11111 # port for receiving video stream - self.last_height = 0 - self.socket.bind((local_ip, local_port)) - - # thread for receiving cmd ack - self.receive_thread = threading.Thread(target=self._receive_thread) - self.receive_thread.daemon = True - - self.receive_thread.start() - - # to receive video -- send cmd: command, streamon - self.socket.sendto(b'command', self.tello_address) - print ('sent: command') - self.socket.sendto(b'streamon', self.tello_address) - print ('sent: streamon') - - self.socket_video.bind((local_ip, self.local_video_port)) - - # thread for receiving video - self.receive_video_thread = threading.Thread(target=self._receive_video_thread) - self.receive_video_thread.daemon = True - - self.receive_video_thread.start() - - def __del__(self): - """Closes the local socket.""" - - self.socket.close() - self.socket_video.close() - - def read(self): - """Return the last frame from camera.""" - if self.is_freeze: - return self.last_frame - else: - return self.frame - - def video_freeze(self, is_freeze=True): - """Pause video output -- set is_freeze to True""" - self.is_freeze = is_freeze - if is_freeze: - self.last_frame = self.frame - - def _receive_thread(self): - """Listen to responses from the Tello. - - Runs as a thread, sets self.response to whatever the Tello last returned. - - """ - while True: - try: - self.response, ip = self.socket.recvfrom(3000) - #print(self.response) - except socket.error as exc: - print ("Caught exception socket.error : %s" % exc) - - def _receive_video_thread(self): - """ - Listens for video streaming (raw h264) from the Tello. - - Runs as a thread, sets self.frame to the most recent frame Tello captured. - - """ - packet_data = "" - while True: - try: - res_string, ip = self.socket_video.recvfrom(2048) - packet_data += res_string - # end of frame - if len(res_string) != 1460: - for frame in self._h264_decode(packet_data): - self.frame = frame - packet_data = "" - - except socket.error as exc: - print ("Caught exception socket.error : %s" % exc) - - def _h264_decode(self, packet_data): - """ - decode raw h264 format data from Tello - - :param packet_data: raw h264 data array - - :return: a list of decoded frame - """ - res_frame_list = [] - frames = self.decoder.decode(packet_data) - for framedata in frames: - (frame, w, h, ls) = framedata - if frame is not None: - # print 'frame size %i bytes, w %i, h %i, linesize %i' % (len(frame), w, h, ls) - - frame = np.fromstring(frame, dtype=np.ubyte, count=len(frame), sep='') - frame = (frame.reshape((h, ls / 3, 3))) - frame = frame[:, :w, :] - res_frame_list.append(frame) - - return res_frame_list - - def send_command(self, command): - """ - Send a command to the Tello and wait for a response. - - :param command: Command to send. - :return (str): Response from Tello. - - """ - - print (">> send cmd: {}".format(command)) - self.abort_flag = False - timer = threading.Timer(self.command_timeout, self.set_abort_flag) - - self.socket.sendto(command.encode('utf-8'), self.tello_address) - - timer.start() - while self.response is None: - if self.abort_flag is True: - break - timer.cancel() - - if self.response is None: - response = 'none_response' - else: - response = self.response.decode('utf-8') - - self.response = None - - return response - - def set_abort_flag(self): - """ - Sets self.abort_flag to True. - - Used by the timer in Tello.send_command() to indicate to that a response - - timeout has occurred. - - """ - - self.abort_flag = True - - def takeoff(self): - """ - Initiates take-off. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - return self.send_command('takeoff') - - def set_speed(self, speed): - """ - Sets speed. - - This method expects KPH or MPH. The Tello API expects speeds from - 1 to 100 centimeters/second. - - Metric: .1 to 3.6 KPH - Imperial: .1 to 2.2 MPH - - Args: - speed (int|float): Speed. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - speed = float(speed) - - if self.imperial is True: - speed = int(round(speed * 44.704)) - else: - speed = int(round(speed * 27.7778)) - - return self.send_command('speed %s' % speed) - - def rotate_cw(self, degrees): - """ - Rotates clockwise. - - Args: - degrees (int): Degrees to rotate, 1 to 360. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - return self.send_command('cw %s' % degrees) - - def rotate_ccw(self, degrees): - """ - Rotates counter-clockwise. - - Args: - degrees (int): Degrees to rotate, 1 to 360. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - return self.send_command('ccw %s' % degrees) - - def flip(self, direction): - """ - Flips. - - Args: - direction (str): Direction to flip, 'l', 'r', 'f', 'b'. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - return self.send_command('flip %s' % direction) - - def get_response(self): - """ - Returns response of tello. - - Returns: - int: response of tello. - - """ - response = self.response - return response - - def get_height(self): - """Returns height(dm) of tello. - - Returns: - int: Height(dm) of tello. - - """ - height = self.send_command('height?') - height = str(height) - height = filter(str.isdigit, height) - try: - height = int(height) - self.last_height = height - except: - height = self.last_height - pass - return height - - def get_battery(self): - """Returns percent battery life remaining. - - Returns: - int: Percent battery life remaining. - - """ - - battery = self.send_command('battery?') - - try: - battery = int(battery) - except: - pass - - return battery - - def get_flight_time(self): - """Returns the number of seconds elapsed during flight. - - Returns: - int: Seconds elapsed during flight. - - """ - - flight_time = self.send_command('time?') - - try: - flight_time = int(flight_time) - except: - pass - - return flight_time - - def get_speed(self): - """Returns the current speed. - - Returns: - int: Current speed in KPH or MPH. - - """ - - speed = self.send_command('speed?') - - try: - speed = float(speed) - - if self.imperial is True: - speed = round((speed / 44.704), 1) - else: - speed = round((speed / 27.7778), 1) - except: - pass - - return speed - - def land(self): - """Initiates landing. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - return self.send_command('land') - - def move(self, direction, distance): - """Moves in a direction for a distance. - - This method expects meters or feet. The Tello API expects distances - from 20 to 500 centimeters. - - Metric: .02 to 5 meters - Imperial: .7 to 16.4 feet - - Args: - direction (str): Direction to move, 'forward', 'back', 'right' or 'left'. - distance (int|float): Distance to move. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - distance = float(distance) - - if self.imperial is True: - distance = int(round(distance * 30.48)) - else: - distance = int(round(distance * 100)) - - return self.send_command('%s %s' % (direction, distance)) - - def move_backward(self, distance): - """Moves backward for a distance. - - See comments for Tello.move(). - - Args: - distance (int): Distance to move. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - return self.move('back', distance) - - def move_down(self, distance): - """Moves down for a distance. - - See comments for Tello.move(). - - Args: - distance (int): Distance to move. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - return self.move('down', distance) - - def move_forward(self, distance): - """Moves forward for a distance. - - See comments for Tello.move(). - - Args: - distance (int): Distance to move. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - return self.move('forward', distance) - - def move_left(self, distance): - """Moves left for a distance. - - See comments for Tello.move(). - - Args: - distance (int): Distance to move. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - return self.move('left', distance) - - def move_right(self, distance): - """Moves right for a distance. - - See comments for Tello.move(). - - Args: - distance (int): Distance to move. - - """ - return self.move('right', distance) - - def move_up(self, distance): - """Moves up for a distance. - - See comments for Tello.move(). - - Args: - distance (int): Distance to move. - - Returns: - str: Response from Tello, 'OK' or 'FALSE'. - - """ - - return self.move('up', distance) +import socket +import threading +import numpy as np +import h264decoder +from tello_control_ui import TelloUI + +class Tello: + """Wrapper class to interact with the Tello drone.""" + + def __init__(self, local_ip, local_port, imperial=False, command_timeout=.3, tello_ip='192.168.10.1', + tello_port=8889, video=False, video_path="./img/"): + """ + Binds to the local IP/port and puts the Tello into command mode. + + :param local_ip (str): Local IP address to bind. + :param local_port (int): Local port to bind. + :param imperial (bool): If True, speed is MPH and distance is feet. + If False, speed is KPH and distance is meters. + :param command_timeout (int|float): Number of seconds to wait for a response to a command. + :param tello_ip (str): Tello IP. + :param tello_port (int): Tello port. + """ + + self.abort_flag = False + self.decoder = h264decoder.H264Decoder() + self.command_timeout = command_timeout + self.imperial = imperial + self.response = None + self.frame = None # numpy array BGR -- current camera output frame + self.is_freeze = False # freeze current camera output + self.last_frame = None + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for sending cmd + self.socket_video = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for receiving video stream + self.tello_address = (tello_ip, tello_port) + self.local_video_port = 11111 # port for receiving video stream + self.last_height = 0 + self.socket.bind((local_ip, local_port)) + + # thread for receiving cmd ack + self.receive_thread = threading.Thread(target=self._receive_thread) + self.receive_thread.daemon = True + + self.receive_thread.start() + + # to receive video -- send cmd: command, streamon + self.socket.sendto(b'command', self.tello_address) + print ('sent: command') + if video: + self.socket.sendto(b'streamon', self.tello_address) + print ('sent: streamon') + + self.socket_video.bind((local_ip, self.local_video_port)) + + # thread for receiving video + self.receive_video_thread = threading.Thread(target=self._receive_video_thread) + self.receive_video_thread.daemon = True + self.receive_video_thread.start() + # thread for ui + self.ui_thread=threading.Thread(target=self._tello_ui, args=(video_path,)) + self.ui_thread.start() + def _tello_ui(self, video_path): + self.vplayer = TelloUI(self, video_path) + self.vplayer.root.mainloop() + def __del__(self): + """Closes the local socket.""" + self.socket.sendto(b'streamoff', self.tello_address) + print ('sent: streamoff') + self.land() + self.socket.close() + self.socket_video.close() + + def read(self): + """Return the last frame from camera.""" + if self.is_freeze: + return self.last_frame + else: + return self.frame + + def video_freeze(self, is_freeze=True): + """Pause video output -- set is_freeze to True""" + self.is_freeze = is_freeze + if is_freeze: + self.last_frame = self.frame + + def _receive_thread(self): + """Listen to responses from the Tello. + + Runs as a thread, sets self.response to whatever the Tello last returned. + + """ + while True: + try: + self.response, ip = self.socket.recvfrom(3000) + #print(self.response) + except socket.error as exc: + print("Caught exception socket.error : %s" % exc) + + def _receive_video_thread(self): + """ + Listens for video streaming (raw h264) from the Tello. + + Runs as a thread, sets self.frame to the most recent frame Tello captured. + + """ + packet_data = b"" + while True: + try: + res_string, ip = self.socket_video.recvfrom(2048) + packet_data += res_string + # end of frame + if len(res_string) != 1460: + for frame in self._h264_decode(packet_data): + self.frame = frame + packet_data = b"" + + except socket.error as exc: + print ("Caught exception socket.error : %s" % exc) + + def _h264_decode(self, packet_data): + """ + decode raw h264 format data from Tello + + :param packet_data: raw h264 data array + + :return: a list of decoded frame + """ + res_frame_list = [] + frames = self.decoder.decode(packet_data) + for framedata in frames: + (frame, w, h, ls) = framedata + if frame is not None: + # print 'frame size %i bytes, w %i, h %i, linesize %i' % (len(frame), w, h, ls) + + frame = np.fromstring(frame, dtype=np.ubyte, count=len(frame), sep='') + frame = (frame.reshape((h, ls // 3, 3))) + frame = frame[:, :w, :] + res_frame_list.append(frame) + + return res_frame_list + + def send_command(self, command): + """ + Send a command to the Tello and wait for a response. + + :param command: Command to send. + :return (str): Response from Tello. + + """ + + print (">> send cmd: {}".format(command)) + self.abort_flag = False + timer = threading.Timer(self.command_timeout, self.set_abort_flag) + + self.socket.sendto(command.encode('utf-8'), self.tello_address) + + timer.start() + while self.response is None: + if self.abort_flag is True: + break + timer.cancel() + if self.response is None: + response = 'none_response' + else: + timer = threading.Timer(self.command_timeout, self.set_abort_flag) + timer.start() + while self.response == b'ok': + if self.abort_flag is True: + break + timer.cancel() + if self.response is None: + return "ok" + response = self.response.decode('utf-8') + + self.response = None + import time + time.sleep(5) + return response + + def set_abort_flag(self): + """ + Sets self.abort_flag to True. + + Used by the timer in Tello.send_command() to indicate to that a response + + timeout has occurred. + + """ + + self.abort_flag = True + + def takeoff(self): + """ + Initiates take-off. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.send_command('takeoff') + + def set_speed(self, speed): + """ + Sets speed. + + This method expects KPH or MPH. The Tello API expects speeds from + 1 to 100 centimeters/second. + + Metric: .1 to 3.6 KPH + Imperial: .1 to 2.2 MPH + + Args: + speed (int|float): Speed. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + speed = float(speed) + + if self.imperial is True: + speed = int(round(speed * 44.704)) + else: + speed = int(round(speed * 27.7778)) + + return self.send_command('speed %s' % speed) + + def rotate_cw(self, degrees): + """ + Rotates clockwise. + + Args: + degrees (int): Degrees to rotate, 1 to 360. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.send_command('cw %s' % degrees) + + def rotate_ccw(self, degrees): + """ + Rotates counter-clockwise. + + Args: + degrees (int): Degrees to rotate, 1 to 360. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + return self.send_command('ccw %s' % degrees) + + def flip(self, direction): + """ + Flips. + + Args: + direction (str): Direction to flip, 'l', 'r', 'f', 'b'. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.send_command('flip %s' % direction) + + def get_response(self): + """ + Returns response of tello. + + Returns: + int: response of tello. + + """ + response = self.response + return response + + def get_height(self): + """Returns height(dm) of tello. + + Returns: + int: Height(dm) of tello. + + """ + height = self.send_command('height?') + height = str(height) + height = filter(str.isdigit, height) + try: + height = int(height) + self.last_height = height + except: + height = self.last_height + pass + return height + + def get_battery(self): + """Returns percent battery life remaining. + + Returns: + int: Percent battery life remaining. + + """ + + battery = self.send_command('battery?') + + try: + battery = int(battery) + except: + pass + + return battery + + def get_flight_time(self): + """Returns the number of seconds elapsed during flight. + + Returns: + int: Seconds elapsed during flight. + + """ + + flight_time = self.send_command('time?') + + try: + flight_time = int(flight_time) + except: + pass + + return flight_time + + def get_speed(self): + """Returns the current speed. + + Returns: + int: Current speed in KPH or MPH. + + """ + + speed = self.send_command('speed?') + + try: + speed = float(speed) + + if self.imperial is True: + speed = round((speed / 44.704), 1) + else: + speed = round((speed / 27.7778), 1) + except: + pass + + return speed + + def land(self): + """Initiates landing. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.send_command('land') + + def move(self, direction, distance): + """Moves in a direction for a distance. + + This method expects meters or feet. The Tello API expects distances + from 20 to 500 centimeters. + + Metric: .02 to 5 meters + Imperial: .7 to 16.4 feet + + Args: + direction (str): Direction to move, 'forward', 'back', 'right' or 'left'. + distance (int|float): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + distance = float(distance) + + if self.imperial is True: + distance = int(round(distance * 30.48)) + else: + distance = int(round(distance * 100)) + + return self.send_command('%s %s' % (direction, distance)) + + def move_backward(self, distance): + """Moves backward for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.move('back', distance) + + def move_down(self, distance): + """Moves down for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.move('down', distance) + + def move_forward(self, distance): + """Moves forward for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + return self.move('forward', distance) + + def move_left(self, distance): + """Moves left for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + return self.move('left', distance) + + def move_right(self, distance): + """Moves right for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + """ + return self.move('right', distance) + + def move_up(self, distance): + """Moves up for a distance. + + See comments for Tello.move(). + + Args: + distance (int): Distance to move. + + Returns: + str: Response from Tello, 'OK' or 'FALSE'. + + """ + + return self.move('up', distance) + + def close(self): + try: + getattr(self, "vplayer").onClose() + except AttributeError: + print("[INFO] closing...") diff --git a/Tello_Video_With_QR_Detection/tello_control_ui.py b/Tello_Video_With_QR_Detection/tello_control_ui.py new file mode 100644 index 0000000..5edd959 --- /dev/null +++ b/Tello_Video_With_QR_Detection/tello_control_ui.py @@ -0,0 +1,396 @@ +from PIL import Image, ImageDraw, ImageFont +from PIL import ImageTk +import tkinter as tki +from tkinter import Toplevel, Scale +import threading +import datetime +import cv2 +import os +import time +import platform +import numpy + +font = cv2.FONT_HERSHEY_SIMPLEX + +class TelloUI: + """Wrapper class to enable the GUI.""" + + def __init__(self,tello,outputpath): + """ + Initial all the element of the GUI,support by Tkinter + + :param tello: class interacts with the Tello drone. + + Raises: + RuntimeError: If the Tello rejects the attempt to enter command mode. + """ + + self.tello = tello # videostream device + self.outputPath = outputpath # the path that save pictures created by clicking the takeSnapshot button + self.frame = None # frame read from h264decoder and used for pose recognition + self.thread = None # thread of the Tkinter mainloop + self.stopEvent = None + + # control variables + self.distance = 0.1 # default distance for 'move' cmd + self.degree = 30 # default degree for 'cw' or 'ccw' cmd + + # if the flag is TRUE,the auto-takeoff thread will stop waiting for the response from tello + self.quit_waiting_flag = False + + self.qrd=cv2.QRCodeDetector() + self.qrs=[] + + # initialize the root window and image panel + self.root = tki.Tk() + self.panel = None + + # create buttons + # self.btn_snapshot = tki.Button(self.root, text="Snapshot!", + # command=self.takeSnapshot) + # self.btn_snapshot.pack(side="bottom", fill="both", + # expand="yes", padx=10, pady=5) + + # self.btn_pause = tki.Button(self.root, text="Pause", relief="raised", command=self.pauseVideo) + # self.btn_pause.pack(side="bottom", fill="both", + # expand="yes", padx=10, pady=5) + + # self.btn_landing = tki.Button( + # self.root, text="Open Command Panel", relief="raised", command=self.openCmdWindow) + # self.btn_landing.pack(side="bottom", fill="both", + # expand="yes", padx=10, pady=5) + + # start a thread that constantly pools the video sensor for + # the most recently read frame + self.stopEvent = threading.Event() + self.thread = threading.Thread(target=self.videoLoop, args=()) + self.thread.start() + + # set a callback to handle when the window is closed + self.root.wm_title("TELLO Camera View") + self.root.wm_protocol("WM_DELETE_WINDOW", self.onClose) + + # the sending_command will send command to tello every 5 seconds + self.sending_command_thread = threading.Thread(target = self._sendingCommand) + def videoLoop(self): + """ + The mainloop thread of Tkinter + Raises: + RuntimeError: To get around a RunTime error that Tkinter throws due to threading. + """ + try: + # start the thread that get GUI image and drwa skeleton + time.sleep(0.5) + self.sending_command_thread.start() + while not self.stopEvent.is_set(): + system = platform.system() + + # read the frame for GUI show + self.frame = self.tello.read() + if self.frame is None or self.frame.size == 0: + continue + + # transfer the format from frame to image + image = Image.fromarray(self.frame) + retval, decoded_info, points, straight_qrcode = self.qrd.detectAndDecodeMulti(self._pil2cv(image)) + if retval: + points = points.astype(numpy.int32) + + for dec_inf, point in zip(decoded_info, points): + if dec_inf == '' and dec_inf.startswith("drone_"): + continue + + # QRコード座標取得 + x = point[0][0] + y = point[0][1] + + draw=ImageDraw.Draw(image) + # QRコードデータ + draw.text([x, y], dec_inf, 'red', font=ImageFont.load_default(size=12)) + + # 新規ならスクショ + if not dec_inf in self.qrs: + self.qrs.append(dec_inf) + print("detect:",dec_inf) + self.takeSnapshot() + + # we found compatibility problem between Tkinter,PIL and Macos,and it will + # sometimes result the very long preriod of the "ImageTk.PhotoImage" function, + # so for Macos,we start a new thread to execute the _updateGUIImage function. + if system =="Windows" or system =="Linux": + self._updateGUIImage(image) + + else: + thread_tmp = threading.Thread(target=self._updateGUIImage,args=(image,)) + thread_tmp.start() + time.sleep(0.03) + except RuntimeError as e: + print("[INFO] caught a RuntimeError") + # https://qiita.com/derodero24/items/f22c22b22451609908ee + def _pil2cv(self, image): + ''' PIL型 -> OpenCV型 ''' + new_image = numpy.array(image, dtype=numpy.uint8) + if new_image.ndim == 2: # モノクロ + pass + elif new_image.shape[2] == 3: # カラー + new_image = cv2.cvtColor(new_image, cv2.COLOR_RGB2BGR) + elif new_image.shape[2] == 4: # 透過 + new_image = cv2.cvtColor(new_image, cv2.COLOR_RGBA2BGRA) + return new_image + + def _updateGUIImage(self,image): + """ + Main operation to initial the object of image,and update the GUI panel + """ + if self.panel is None: + self.panel = tki.Label() + self.panel.pack(side="left", padx=10, pady=10) + image = ImageTk.PhotoImage(image, master=self.panel) + # if the panel none ,we need to initial it + # otherwise, simply update the panel + self.panel.configure(image=image) + self.panel.image = image + + def _sendingCommand(self): + """ + start a while loop that sends 'command' to tello every 5 second + """ + + while True: + try: + self.tello.send_command('command') + except AttributeError: + break + time.sleep(5) + + def _setQuitWaitingFlag(self): + """ + set the variable as TRUE,it will stop computer waiting for response from tello + """ + self.quit_waiting_flag = True + + def openCmdWindow(self): + """ + open the cmd window and initial all the button and text + """ + panel = Toplevel(self.root) + panel.wm_title("Command Panel") + + # create text input entry + text0 = tki.Label(panel, + text='This Controller map keyboard inputs to Tello control commands\n' + 'Adjust the trackbar to reset distance and degree parameter', + font='Helvetica 10 bold' + ) + text0.pack(side='top') + + text1 = tki.Label(panel, text= + 'W - Move Tello Up\t\t\tArrow Up - Move Tello Forward\n' + 'S - Move Tello Down\t\t\tArrow Down - Move Tello Backward\n' + 'A - Rotate Tello Counter-Clockwise\tArrow Left - Move Tello Left\n' + 'D - Rotate Tello Clockwise\t\tArrow Right - Move Tello Right', + justify="left") + text1.pack(side="top") + + self.btn_landing = tki.Button( + panel, text="Land", relief="raised", command=self.telloLanding) + self.btn_landing.pack(side="bottom", fill="both", + expand="yes", padx=10, pady=5) + + self.btn_takeoff = tki.Button( + panel, text="Takeoff", relief="raised", command=self.telloTakeOff) + self.btn_takeoff.pack(side="bottom", fill="both", + expand="yes", padx=10, pady=5) + + # binding arrow keys to drone control + self.tmp_f = tki.Frame(panel, width=100, height=2) + self.tmp_f.bind('', self.on_keypress_w) + self.tmp_f.bind('', self.on_keypress_s) + self.tmp_f.bind('', self.on_keypress_a) + self.tmp_f.bind('', self.on_keypress_d) + self.tmp_f.bind('', self.on_keypress_up) + self.tmp_f.bind('', self.on_keypress_down) + self.tmp_f.bind('', self.on_keypress_left) + self.tmp_f.bind('', self.on_keypress_right) + self.tmp_f.pack(side="bottom") + self.tmp_f.focus_set() + + self.btn_landing = tki.Button( + panel, text="Flip", relief="raised", command=self.openFlipWindow) + self.btn_landing.pack(side="bottom", fill="both", + expand="yes", padx=10, pady=5) + + self.distance_bar = Scale(panel, from_=0.02, to=5, tickinterval=0.01, digits=3, label='Distance(m)', + resolution=0.01) + self.distance_bar.set(0.2) + self.distance_bar.pack(side="left") + + self.btn_distance = tki.Button(panel, text="Reset Distance", relief="raised", + command=self.updateDistancebar, + ) + self.btn_distance.pack(side="left", fill="both", + expand="yes", padx=10, pady=5) + + self.degree_bar = Scale(panel, from_=1, to=360, tickinterval=10, label='Degree') + self.degree_bar.set(30) + self.degree_bar.pack(side="right") + + self.btn_distance = tki.Button(panel, text="Reset Degree", relief="raised", command=self.updateDegreebar) + self.btn_distance.pack(side="right", fill="both", + expand="yes", padx=10, pady=5) + + def openFlipWindow(self): + """ + open the flip window and initial all the button and text + """ + + panel = Toplevel(self.root) + panel.wm_title("Gesture Recognition") + + self.btn_flipl = tki.Button( + panel, text="Flip Left", relief="raised", command=self.telloFlip_l) + self.btn_flipl.pack(side="bottom", fill="both", + expand="yes", padx=10, pady=5) + + self.btn_flipr = tki.Button( + panel, text="Flip Right", relief="raised", command=self.telloFlip_r) + self.btn_flipr.pack(side="bottom", fill="both", + expand="yes", padx=10, pady=5) + + self.btn_flipf = tki.Button( + panel, text="Flip Forward", relief="raised", command=self.telloFlip_f) + self.btn_flipf.pack(side="bottom", fill="both", + expand="yes", padx=10, pady=5) + + self.btn_flipb = tki.Button( + panel, text="Flip Backward", relief="raised", command=self.telloFlip_b) + self.btn_flipb.pack(side="bottom", fill="both", + expand="yes", padx=10, pady=5) + + def takeSnapshot(self): + """ + save the current frame of the video as a jpg file and put it into outputpath + """ + + # grab the current timestamp and use it to construct the filename + ts = datetime.datetime.now() + filename = "{}.jpg".format(ts.strftime("%Y-%m-%d_%H-%M-%S")) + + p = os.path.sep.join((self.outputPath, filename)) + os.makedirs(os.path.dirname(p), exist_ok=True) + # save the file + cv2.imwrite(p, cv2.cvtColor(self.frame, cv2.COLOR_RGB2BGR)) + print("[INFO] saved {}".format(os.path.abspath(p))) + + + def pauseVideo(self): + """ + Toggle the freeze/unfreze of video + """ + if self.btn_pause.config('relief')[-1] == 'sunken': + self.btn_pause.config(relief="raised") + self.tello.video_freeze(False) + else: + self.btn_pause.config(relief="sunken") + self.tello.video_freeze(True) + + def telloTakeOff(self): + return self.tello.takeoff() + + def telloLanding(self): + return self.tello.land() + + def telloFlip_l(self): + return self.tello.flip('l') + + def telloFlip_r(self): + return self.tello.flip('r') + + def telloFlip_f(self): + return self.tello.flip('f') + + def telloFlip_b(self): + return self.tello.flip('b') + + def telloCW(self, degree): + return self.tello.rotate_cw(degree) + + def telloCCW(self, degree): + return self.tello.rotate_ccw(degree) + + def telloMoveForward(self, distance): + return self.tello.move_forward(distance) + + def telloMoveBackward(self, distance): + return self.tello.move_backward(distance) + + def telloMoveLeft(self, distance): + return self.tello.move_left(distance) + + def telloMoveRight(self, distance): + return self.tello.move_right(distance) + + def telloUp(self, dist): + return self.tello.move_up(dist) + + def telloDown(self, dist): + return self.tello.move_down(dist) + + def updateTrackBar(self): + self.my_tello_hand.setThr(self.hand_thr_bar.get()) + + def updateDistancebar(self): + self.distance = self.distance_bar.get() + print('reset distance to %.1f' % self.distance) + + def updateDegreebar(self): + self.degree = self.degree_bar.get() + print('reset distance to %d' % self.degree) + + def on_keypress_w(self, event): + print("up %d m" % self.distance) + self.telloUp(self.distance) + + def on_keypress_s(self, event): + print("down %d m" % self.distance) + self.telloDown(self.distance) + + def on_keypress_a(self, event): + print("ccw %d degree" % self.degree) + self.tello.rotate_ccw(self.degree) + + def on_keypress_d(self, event): + print("cw %d m" % self.degree) + self.tello.rotate_cw(self.degree) + + def on_keypress_up(self, event): + print("forward %d m" % self.distance) + self.telloMoveForward(self.distance) + + def on_keypress_down(self, event): + print("backward %d m" % self.distance) + self.telloMoveBackward(self.distance) + + def on_keypress_left(self, event): + print("left %d m" % self.distance) + self.telloMoveLeft(self.distance) + + def on_keypress_right(self, event): + print("right %d m" % self.distance) + self.telloMoveRight(self.distance) + + def on_keypress_enter(self, event): + if self.frame is not None: + self.registerFace() + self.tmp_f.focus_set() + + def onClose(self): + """ + set the stop event, cleanup the camera, and allow the rest of + + the quit process to continue + """ + print("[INFO] closing...") + self.stopEvent.set() + del self.tello + self.root.quit() diff --git a/doc/readme.pdf b/doc/readme.pdf deleted file mode 100644 index 9c12ed7..0000000 Binary files a/doc/readme.pdf and /dev/null differ diff --git "a/doc/\342\225\246\342\225\241\342\224\234\342\211\210.pdf" "b/doc/\342\225\246\342\225\241\342\224\234\342\211\210.pdf" deleted file mode 100644 index 733bec5..0000000 Binary files "a/doc/\342\225\246\342\225\241\342\224\234\342\211\210.pdf" and /dev/null differ diff --git a/tello_video_dll(ForWin64).zip b/tello_video_dll(ForWin64).zip deleted file mode 100644 index 0e42f27..0000000 Binary files a/tello_video_dll(ForWin64).zip and /dev/null differ