From 92a9bec2786b909e664187349412a452cdc477b9 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sat, 26 Feb 2022 19:29:10 +0100 Subject: [PATCH] init c/c++ repo --- .gitignore | 7 ++ Image/ImageUtils.h | 42 +++++++++ Image/Skew.h | 104 ++++++++++++++++++++++ Image/Thresholding.h | 92 +++++++++++++++++++ Tools/InvoicePreprocessing/CMakeLists.txt | 6 ++ Tools/InvoicePreprocessing/main.cpp | 46 ++++++++++ 6 files changed, 297 insertions(+) create mode 100644 .gitignore create mode 100644 Image/ImageUtils.h create mode 100644 Image/Skew.h create mode 100644 Image/Thresholding.h create mode 100644 Tools/InvoicePreprocessing/CMakeLists.txt create mode 100644 Tools/InvoicePreprocessing/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29d9ff6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.out +.directory +*.cmake +CMakeCache.txt +Makefile +CMakeFiles +App \ No newline at end of file diff --git a/Image/ImageUtils.h b/Image/ImageUtils.h new file mode 100644 index 0000000..9d0ed53 --- /dev/null +++ b/Image/ImageUtils.h @@ -0,0 +1,42 @@ +/** + * Karaka + * + * PHP Version 8.0 + * + * @package Image + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link https://karaka.app + */ +#ifndef IMAGE_IMAGE_UTILS_H +#define IMAGE_IMAGE_UTILS_H + +#include +#include + +namespace Image { + class ImageUtils { + private: + + public: + static inline + float lightnessFromRgb(int r, int g, int b) + { + float vR = r / 255.0; + float vG = g / 255.0; + float vB = b / 255.0; + + float lR = vR <= 0.04045 ? vR / 12.92 : pow(((vR + 0.055) / 1.055), 2.4); + float lG = vG <= 0.04045 ? vG / 12.92 : pow(((vG + 0.055) / 1.055), 2.4); + float lB = vB <= 0.04045 ? vB / 12.92 : pow(((vB + 0.055) / 1.055), 2.4); + + float y = 0.2126 * lR + 0.7152 * lG + 0.0722 * lB; + float lStar = y <= 216.0 / 24389.0 ? y * 24389.0 / 27.0 : pow(y, (1.0 / 3.0)) * 116.0 - 16.0; + + return lStar / 100.0; + } + }; +} + +#endif \ No newline at end of file diff --git a/Image/Skew.h b/Image/Skew.h new file mode 100644 index 0000000..4030e8e --- /dev/null +++ b/Image/Skew.h @@ -0,0 +1,104 @@ +/** + * Karaka + * + * PHP Version 8.0 + * + * @package Image + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link https://karaka.app + */ +#ifndef IMAGE_SKEW_H +#define IMAGE_SKEW_H + +#include +#include +#include +#include + +#define deg2rad(angle) \ + ({ __typeof__ (angle) _angle = (angle); \ + (_angle) * M_PI / 180.0; }) + +#define rad2deg(angle) \ + ({ __typeof__ (angle) _angle = (angle); \ + (_angle) * 180.0 / M_PI; }) + +namespace Image { + class Skew { + private: + + public: + static inline + cv::Mat deskewHoughLines(cv::Mat in, int maxDegree = 45) + { + cv::Size dim = in.size(); + + cv::Mat inv; + cv::cvtColor(in, inv, cv::COLOR_BGR2GRAY); + cv::threshold(inv, inv, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU); + + std::vector lines; + cv::HoughLinesP(inv, lines, 1.0, M_PI / 180, 200, dim.width / 12, dim.width / 150); + + int imageOrientation = 0; // > 0 -> horizontal + + std::vector tmpAngles; + for (int i = 0; i < lines.size(); ++i) { + float angle = atan2(lines[i][3] - lines[i][1], lines[i][2] - lines[i][0]); + tmpAngles.push_back(angle); + + imageOrientation += abs(angle) > M_PI / 4 ? 1 : -1; + } + + std::vector angles; + for (int i = 0; i < tmpAngles.size(); ++i) { + if (imageOrientation > 0) { + if (deg2rad(90 - maxDegree) < abs(tmpAngles[i]) < deg2rad(90 + maxDegree)) { + angles.push_back(tmpAngles[i]); + } + } else { + if (abs(tmpAngles[i]) < deg2rad(maxDegree)) { + angles.push_back(tmpAngles[i]); + } + } + } + + if (angles.size() < 5) { + return in; + } + + float median = 0.0; + for (int i = 0; i < angles.size(); ++i) { + median += angles[i]; + } + + float angleDeg = rad2deg(median / angles.size()); + + cv::Mat orientFix; + if (imageOrientation > 0) { + if (angleDeg < 0) { + cv::rotate(in, orientFix, cv::ROTATE_90_CLOCKWISE); + angleDeg += 90.0; + } else if (angleDeg > 0) { + cv::rotate(in, orientFix, cv::ROTATE_90_COUNTERCLOCKWISE); + angleDeg -= 90.0; + } else { + orientFix = in; + } + } else { + orientFix = in; + } + + cv::Mat rot = cv::getRotationMatrix2D(cv::Point2f(dim.width / 2, dim.height / 2), angleDeg, 1.0); + + cv::Mat out; + cv::warpAffine(orientFix, out, rot, dim, cv::INTER_LINEAR, cv::BORDER_REPLICATE); + + return out; + } + }; +} + +#endif \ No newline at end of file diff --git a/Image/Thresholding.h b/Image/Thresholding.h new file mode 100644 index 0000000..1ad9ced --- /dev/null +++ b/Image/Thresholding.h @@ -0,0 +1,92 @@ +/** + * Karaka + * + * PHP Version 8.0 + * + * @package Image + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link https://karaka.app + */ +#ifndef IMAGE_THRESHOLDING_H +#define IMAGE_THRESHOLDING_H + +#include +#include + +#include "ImageUtils.h" + +#define max(a, b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define min(a, b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +namespace Image { + class Thresholding { + private: + + public: + static inline + cv::Mat integralThresholding(cv::Mat in) + { + cv::Size dim = in.size(); + cv::Mat out(in.size(), in.type()); + + float intImg[dim.width][dim.height]; + float sum; + + cv::Vec3b bgr; + for (int i = 0; i < dim.width; ++i) { + sum = 0.0; + + for (int j = 0; j < dim.height; ++j) { + bgr = in.at(j, i); + sum += Image::ImageUtils::lightnessFromRgb(bgr[2], bgr[1], bgr[0]); + + intImg[i][j] = i == 0 ? sum : intImg[i - 1][j] + sum; + } + } + + int s = dim.width / 96.0; + int t = 30; + + int x1, x2; + int y1, y2; + int count; + float brightness; + int color; + + for (int i = 0; i < dim.width; ++i) { + for (int j = 0; j < dim.height; ++j) { + x1 = max(1, i - s / 2.0); + x2 = min(i + s / 2.0, dim.width - 1); + + y1 = max(1, j - s / 2.0); + y2 = min(j + s / 2.0, dim.height - 1); + + count = (x2 - x1) * (y2 - y1); + sum = intImg[x2][y2] - intImg[x2][y1 - 1] - intImg[x1 - 1][y2] + intImg[x1 - 1][y1 - 1]; + + bgr = in.at(j, i); + brightness = Image::ImageUtils::lightnessFromRgb(bgr[2], bgr[1], bgr[0]); + + color = brightness * count <= (sum * (100.0 - t) / 100.0) ? 0 : 255; + + out.at(j, i)[0] = color; + out.at(j, i)[1] = color; + out.at(j, i)[2] = color; + } + } + + return out; + } + }; +} + +#endif \ No newline at end of file diff --git a/Tools/InvoicePreprocessing/CMakeLists.txt b/Tools/InvoicePreprocessing/CMakeLists.txt new file mode 100644 index 0000000..c279d57 --- /dev/null +++ b/Tools/InvoicePreprocessing/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 2.8) +project( App ) +find_package( OpenCV REQUIRED ) +include_directories( ${OpenCV_INCLUDE_DIRS} ) +add_executable( App main.cpp ) +target_link_libraries( App ${OpenCV_LIBS} ) \ No newline at end of file diff --git a/Tools/InvoicePreprocessing/main.cpp b/Tools/InvoicePreprocessing/main.cpp new file mode 100644 index 0000000..5e7f501 --- /dev/null +++ b/Tools/InvoicePreprocessing/main.cpp @@ -0,0 +1,46 @@ +/** + * Karaka + * + * PHP Version 8.0 + * + * @package App + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link https://karaka.app + */ +#include +#include + +#include "../../Image/Skew.h" +#include "../../Image/Thresholding.h" + +int main(int argc, char** argv ) +{ + if (argc != 3) { + printf("A input image and a output image is required\n"); + + return -1; + } + + cv::Mat in; + in = cv::imread(argv[1], cv::IMREAD_UNCHANGED); + if (!in.data) { + printf("Couldn't read image.\n"); + + return -1; + } + + cv::Mat out = Image::Thresholding::integralThresholding(in); + out = Image::Skew::deskewHoughLines(out); + + cv::imwrite(argv[2], out); + + /* + cv::imshow("in", in); + cv::imshow("out", out); + cv::waitKey(0); + */ + + return 0; +} \ No newline at end of file