From d328bc9ce23817694c568b6577bbabd05b76daa3 Mon Sep 17 00:00:00 2001 From: mrbesen Date: Tue, 1 Mar 2022 01:28:30 +0100 Subject: [PATCH] initial --- .gitignore | 18 ++++++++ .gitmodules | 3 ++ .vscode/c_cpp_properties.json | 20 +++++++++ .vscode/launch.json | 50 +++++++++++++++++++++ .vscode/tasks.json | 45 +++++++++++++++++++ Makefile | 74 +++++++++++++++++++++++++++++++ include/screen.h | 31 +++++++++++++ src/main.cpp | 82 +++++++++++++++++++++++++++++++++++ src/screen.cpp | 59 +++++++++++++++++++++++++ tests/main.cpp | 43 ++++++++++++++++++ tests/sampletest.cpp | 6 +++ tests/test.h | 17 ++++++++ tests/tests.cpp | 14 ++++++ thirdparty/Log | 1 + 14 files changed, 463 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Makefile create mode 100644 include/screen.h create mode 100644 src/main.cpp create mode 100644 src/screen.cpp create mode 100644 tests/main.cpp create mode 100644 tests/sampletest.cpp create mode 100644 tests/test.h create mode 100644 tests/tests.cpp create mode 160000 thirdparty/Log diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfa90ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +*.bin +*.out +build/ +*.exe +.gdb_history + +*.so +*.bmp +*.d + +test +.vscode/settings.json + +lolautoaccept +lolautoaccept_strip +log.txt +*.png + diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5fd23c8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "thirdparty/Log"] + path = thirdparty/Log + url = https://git.okaestne.de/okaestne/Log.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..92e59b6 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/src/", + "${workspaceFolder}/thirdparty/Log/", + "${workspaceFolder}/include/**", + + "/usr/include/opencv4/**" + ], + "defines": [], + "compilerPath": "/usr/bin/g++", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-x64" + } + ], + "version": 4 +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b83dd92 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "test Debuggen (gdb)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/test", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "make test", + "miDebuggerPath": "/usr/bin/gdb" + }, + { + "name": "Debuggen (gdb)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/lolautoaccept", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "make all", + "miDebuggerPath": "/usr/bin/gdb" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d71d1c1 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,45 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "make all", + "type": "shell", + "command": "make -j all", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "make clean", + "type": "shell", + "command": "make clean", + "problemMatcher": [] + }, + { + "label": "leak check", + "type": "shell", + "command": "/usr/bin/valgrind --leak-check=full ${workspaceFolder}/lolautoaccept -s", + "problemMatcher": [], + "dependsOn": "make all" + }, + { + "label": "leak check ALL", + "type": "shell", + "command": "/usr/bin/valgrind --leak-check=full --show-leak-kinds=all ${workspaceFolder}/lolautoaccept -s", + "problemMatcher": [], + "dependsOn": "make all" + }, + { + "label": "make test", + "type": "shell", + "command": "make -j test", + "problemMatcher": ["$gcc"], + } + ] +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..961a5b0 --- /dev/null +++ b/Makefile @@ -0,0 +1,74 @@ +# Author Yannis Gerlach +# Hochschule Osnabrück +# 13.11.2020 + +# `make clean all` nicht mit -j verwenden! -> race condition im make file +# statdessen: `make clean; make all -j` verwenden + +NAME = lolautoaccept +NAMETEST = test +CFLAGS = -std=c++17 -O2 -g -pipe -Wall -Wextra -Wno-unused-parameter -Wpedantic -rdynamic -mfpmath=both -march=native -m64 -funroll-loops -mavx2 `pkg-config opencv4 --cflags` +CXX = g++ +SRCF = src/ +BUILDDIR = build/ +TESTF = tests/ +DEPF = $(BUILDDIR)deps/ +INCF = ./include/ +INCFS = $(shell find $(INCF) -type d) + +LOGF = ./thirdparty/Log/ +LOGO = $(LOGF)Log.o + +INCLUDES = -I$(LOGF) $(addprefix -I, $(INCFS)) +LDFLAGS = -lX11 -lXext `pkg-config opencv4 --libs` + +SRCFILES = $(shell find $(SRCF) -name "*.cpp") +OBJFILES = $(patsubst $(SRCF)%, $(BUILDDIR)%, $(patsubst %.cpp, %.o, $(SRCFILES))) $(LOGO) +DEPFILES = $(wildcard $(DEPF)*.d) + +SOURCEDIRS = $(shell find $(SRCF) -type d -printf "%p/\n") +BUILDDIRS = $(patsubst $(SRCF)%, $(BUILDDIR)%, $(SOURCEDIRS)) + +OBJFILESTEST = $(filter-out $(BUILDDIR)main.o, $(OBJFILES)) + +INCLUDES += $(addprefix -I, $(SOURCEDIRS)) + +all: $(NAME) runtest + +$(NAME): $(BUILDDIRS) $(DEPF) $(OBJFILES) + @echo "Linking $@" + @$(CXX) $(CFLAGS) -o $@ $(filter %.o, $^) $(LDFLAGS) + +$(BUILDDIR)%.o: $(SRCF)%.cpp + @echo "Compiling: $@" + @$(CXX) $(CFLAGS) $(INCLUDES) $< -MM -MT $@ > $(DEPF)$(subst /,_,$*).d + @$(CXX) -c -o $@ $(CFLAGS) $(INCLUDES) $< + +$(NAME)_strip: $(NAME) + @echo "Strip $<" + @strip -o $@ $< + +%/: + mkdir -p $@ + +clean-depends: + $(RM) -r $(DEPF) + +$(LOGO): + $(MAKE) -C $(LOGF) all + +clean: + $(RM) -r $(NAME) $(BUILDDIR) $(NAMETEST) $(NAME)_strip + $(MAKE) -C $(LOGF) $@ + +$(NAMETEST): $(BUILDDIRS) $(DEPF) $(TESTF)*.cpp $(OBJFILESTEST) + @echo "Compiling tests" + @$(CXX) -o $@ $(filter %.o, $^) $(filter %.cpp, $^) $(CFLAGS) -I$(SRCF) $(INCLUDES) $(LDFLAGS) + +runtest: $(NAMETEST) + @echo "Running tests" + ./$< + +.PHONY: clean all $(NAMETEST) clean-depends runtest + +include $(DEPFILES) diff --git a/include/screen.h b/include/screen.h new file mode 100644 index 0000000..52ba280 --- /dev/null +++ b/include/screen.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +class ScreenShot { +private: + Display* display = nullptr; + Window root; + XWindowAttributes window_attributes; + XImage* ximg = nullptr; + XShmSegmentInfo shminfo; + + int x, y, width, height; + + bool init; + +public: + + ScreenShot(); + ~ScreenShot(); + + void operator() (cv::Mat& cvimg); + +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..bc98c23 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,82 @@ + +#include "Log.h" + +#include "screen.h" + +std::string type2str(int type) { + std::string r; + + uchar depth = type & CV_MAT_DEPTH_MASK; + uchar chans = 1 + (type >> CV_CN_SHIFT); + + switch ( depth ) { + case CV_8U: r = "8U"; break; + case CV_8S: r = "8S"; break; + case CV_16U: r = "16U"; break; + case CV_16S: r = "16S"; break; + case CV_32S: r = "32S"; break; + case CV_32F: r = "32F"; break; + case CV_64F: r = "64F"; break; + default: r = "User"; break; + } + + r += "C"; + r += (chans+'0'); + + return r; +} + +int main(int argc, const char** argv) { + Log::init(); + Log::setConsoleLogLevel(Log::Level::TRACE); + Log::addLogfile("log.txt", Log::Level::TRACE); +#if __unix__ + Log::setColoredOutput(true); +#endif + + Log::info << "Hello, World!"; + + // load template + cv::Mat tem = cv::imread("imgs/Accept.png", cv::IMREAD_COLOR); + // cv::imshow("template", tem); + // cv::waitKey(0); + + ScreenShot screen; + + cv::Mat img; + screen(img); + // cv::imshow("img", img); + // cv::waitKey(0); + + Log::info << "img " << img.type(); + + cv::Mat out; + int result_cols = img.cols - tem.cols + 1; + int result_rows = img.rows - tem.rows + 1; + out.create(result_rows, result_cols, CV_32FC1); + + Log::info << "tem " << tem.type() << " " << type2str(tem.type()); + // img.convertTo(img, tem.type()); + + cv::cvtColor(img, img, cv::COLOR_RGBA2RGB); + Log::info << "converted img: " << img.type() << " " << type2str(img.type()); + + cv::matchTemplate(img, tem, out, cv::TM_SQDIFF); + cv::normalize(out, out, 0, 1, cv::NORM_MINMAX, -1, cv::Mat()); + + double minVal; double maxVal; + cv::Point minLoc; cv::Point maxLoc; cv::Point matchLoc; + cv::minMaxLoc( out, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat() ); + + matchLoc = minLoc; + cv::rectangle(img, matchLoc, cv::Point( matchLoc.x + tem.cols , matchLoc.y + tem.rows ), cv::Scalar(255, 0, 0, 0), 2); + + cv::imshow("result", img); + cv::waitKey(0); + + time_t t = time(0); + cv::imwrite("debugimages/" + std::to_string(t) + ".png", img); + + Log::stop(); + return 0; +} \ No newline at end of file diff --git a/src/screen.cpp b/src/screen.cpp new file mode 100644 index 0000000..8806c9a --- /dev/null +++ b/src/screen.cpp @@ -0,0 +1,59 @@ +// g++ screena.cpp -o screena -lX11 -lXext -Ofast -mfpmath=both -march=native -m64 -funroll-loops -mavx2 `pkg-config opencv --cflags --libs` && ./screena + + // This includes most headers! + +#include "screen.h" + +// https://stackoverflow.com/a/39781697/4399859 + +ScreenShot::ScreenShot() { + + display = XOpenDisplay(nullptr); + root = DefaultRootWindow(display); + + int count_screens = ScreenCount(display); + if(count_screens == 0) { + puts("no screen info!"); + } + for (int i = 0; i < count_screens; ++i) { + Screen* screen = ScreenOfDisplay(display, i); + printf("\tScreen %d: %dX%d\n", i + 1, screen->width, screen->height); + } + + XGetWindowAttributes(display, root, &window_attributes); + Screen* screen = window_attributes.screen; + width = screen->width; + height = screen->height; + x = 0; + y = 0; + + ximg = XShmCreateImage(display, DefaultVisualOfScreen(screen), DefaultDepthOfScreen(screen), ZPixmap, NULL, &shminfo, width, height); + + shminfo.shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT|0777); + shminfo.shmaddr = ximg->data = (char*)shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = false; + if(shminfo.shmid < 0) + puts("Fatal shminfo error!"); + Status s1 = XShmAttach(display, &shminfo); + printf("XShmAttach() %s\n", s1 ? "success!" : "failure!"); + + init = true; +} + +void ScreenShot::operator() (cv::Mat& cv_img){ + if(init) + init = false; + + XShmGetImage(display, root, ximg, 0, 0, 0x00ffffff); + cv_img = cv::Mat(height, width, CV_8UC4, ximg->data); +} + +ScreenShot::~ScreenShot(){ + if(!init) + XDestroyImage(ximg); + + XShmDetach(display, &shminfo); + shmdt(shminfo.shmaddr); + XCloseDisplay(display); +} + diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..4168da5 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,43 @@ +#include +#include "test.h" + +#include +#include +#include + +#define RED "\033[1;91m" +#define GREEN "\033[1;92m" +#define AQUA "\033[1;36m" +#define RESET "\033[;1m" + +extern std::map tests; + +void loadTests(); + +int main(int argc, char** argv) { + auto start = std::chrono::high_resolution_clock::now(); + + loadTests(); + + int failcount = 0; + int testcount = tests.size(); + int testnumber = 0; + for(std::map::iterator current = tests.begin(); current != tests.end(); ++current) { + printf("\033[1mRunning test: %d/%d " AQUA "%s " RESET, ++testnumber, testcount, current->first.c_str()); + if((current->second)()) { + printf(GREEN "succeeded" RESET "!\n"); + } else { + printf(RED "failed" RESET "\n"); + failcount++; + } + } + + const char* color = (failcount > 0 ? RED : GREEN); // red or green + printf("%s%d" RESET "/%d failed\n", color, failcount, testcount); + + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration t = end - start; + printf("Testing took: %fms\n", (t.count() * 1000)); + + return failcount > 0; +} diff --git a/tests/sampletest.cpp b/tests/sampletest.cpp new file mode 100644 index 0000000..0d6d5e0 --- /dev/null +++ b/tests/sampletest.cpp @@ -0,0 +1,6 @@ +#include "test.h" + +TEST(ABC) { + CMPASSERT(1, true); + +} TESTEND diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..1cad15b --- /dev/null +++ b/tests/test.h @@ -0,0 +1,17 @@ +#define TESTFAILED 0 +#define TESTGOOD 1 + +#include + +#define TESTDATA "./tests/data/" + +#define TESTNAME(NAME) test_##NAME +#define TESTFUNC(NAME) bool TESTNAME(NAME)() +#define TEST(NAME) TESTFUNC(NAME) { +#define TESTEND return TESTGOOD; } + +#define ASSERT(BED, ERR) if(!(BED)) { std::cout << __FILE__ << ":" << __LINE__ << " " << ERR << std::endl; return TESTFAILED; } +#define CMPASSERTE(A, B, ERR) if( !((A) == (B))) { std::cout << __FILE__ << ":" << __LINE__ << " is: \"" << (A) << "\" should: \"" << (B) << "\""<< std::endl; return TESTFAILED; } +#define CMPASSERT(A, B) CMPASSERTE(A, B, "") + +typedef bool (*test_t)(); diff --git a/tests/tests.cpp b/tests/tests.cpp new file mode 100644 index 0000000..c683601 --- /dev/null +++ b/tests/tests.cpp @@ -0,0 +1,14 @@ +#include "test.h" + +#include +#include + +#define REGISTERTEST(NAME) tests.insert({#NAME, TESTNAME(NAME)}) + +TESTFUNC(ABC); + +std::map tests; + +void loadTests() { + REGISTERTEST(ABC); +} \ No newline at end of file diff --git a/thirdparty/Log b/thirdparty/Log new file mode 160000 index 0000000..96d10d6 --- /dev/null +++ b/thirdparty/Log @@ -0,0 +1 @@ +Subproject commit 96d10d6e72ddaffd8b688db5ef2705e38aff3a75