Compare commits
No commits in common. '1abf364f2e9c21df89160860afe939a5b95a9c45' and '7c520cf384a54d6f8c29cb1dbfa1850200ace3cc' have entirely different histories.
1abf364f2e
...
7c520cf384
@ -1,25 +0,0 @@
|
|||||||
# IDE 配置
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# 编译产物
|
|
||||||
build/
|
|
||||||
*.exe
|
|
||||||
*.dll
|
|
||||||
*.obj
|
|
||||||
*.a
|
|
||||||
|
|
||||||
# CMake 缓存
|
|
||||||
CMakeCache.txt
|
|
||||||
CMakeFiles/
|
|
||||||
cmake_install.cmake
|
|
||||||
*.ninja
|
|
||||||
.ninja_deps
|
|
||||||
.ninja_log
|
|
||||||
|
|
||||||
# 测试输出
|
|
||||||
test_image/
|
|
||||||
|
|
||||||
# 系统文件
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
desktop.ini
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
project(SealProcessing)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
find_package(OpenCV REQUIRED)
|
|
||||||
|
|
||||||
include_directories(${OpenCV_INCLUDE_DIRS})
|
|
||||||
include_directories(${CMAKE_SOURCE_DIR})
|
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/extract_seal_main)
|
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/remove_border)
|
|
||||||
|
|
||||||
set(COMMON_SOURCES
|
|
||||||
common.cpp
|
|
||||||
image_utilities.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(EXTRACT_SEAL_SOURCES
|
|
||||||
extract_seal_main/hsv_color.cpp
|
|
||||||
extract_seal_main/image_morphology.cpp
|
|
||||||
extract_seal_main/adjust_image.cpp
|
|
||||||
extract_seal_main/color_filter.cpp
|
|
||||||
extract_seal_main/extract_seal_main.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(REMOVE_BORDER_SOURCES
|
|
||||||
remove_border/remove_border.cpp
|
|
||||||
remove_border/remove_border_main.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(seal_lib STATIC
|
|
||||||
${COMMON_SOURCES}
|
|
||||||
${EXTRACT_SEAL_SOURCES}
|
|
||||||
${REMOVE_BORDER_SOURCES}
|
|
||||||
)
|
|
||||||
target_link_libraries(seal_lib ${OpenCV_LIBS})
|
|
||||||
|
|
||||||
add_executable(test_extract_seal extract_seal_main/test_extract_seal.cpp)
|
|
||||||
target_link_libraries(test_extract_seal seal_lib ${OpenCV_LIBS})
|
|
||||||
|
|
||||||
add_executable(test_remove_border remove_border/test_remove_border.cpp)
|
|
||||||
target_link_libraries(test_remove_border seal_lib ${OpenCV_LIBS})
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001>nul
|
|
||||||
setlocal
|
|
||||||
|
|
||||||
set "PROJECT_DIR=%~dp0"
|
|
||||||
set "BUILD_DIR=%PROJECT_DIR%build"
|
|
||||||
|
|
||||||
echo ========================================
|
|
||||||
echo Building SealProcessing project with CMake
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
|
|
||||||
|
|
||||||
cd /d "%BUILD_DIR%"
|
|
||||||
|
|
||||||
echo [1/3] Configuring CMake...
|
|
||||||
rem 尝试多个可能的 OpenCV 路径
|
|
||||||
set "OPENCV_PATHS=C:/msys64/mingw64/lib/cmake/opencv4;C:/mingw64/lib/cmake/opencv4;C:/opencv/build/x64/mingw/lib"
|
|
||||||
|
|
||||||
set "CMAKE_COMMAND=cmake .. -G "MinGW Makefiles""
|
|
||||||
|
|
||||||
rem 优先尝试我们指定的路径,忽略环境变量
|
|
||||||
set "FOUND_OPENCV=0"
|
|
||||||
echo Trying multiple OpenCV paths...
|
|
||||||
for %%p in (%OPENCV_PATHS%) do (
|
|
||||||
if exist "%%p" (
|
|
||||||
echo Found OpenCV at: %%p
|
|
||||||
set "CMAKE_COMMAND=%CMAKE_COMMAND% -DOpenCV_DIR="%%p""
|
|
||||||
set "FOUND_OPENCV=1"
|
|
||||||
goto :cmake_config
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
rem 如果没有找到,尝试使用环境变量
|
|
||||||
if %FOUND_OPENCV%==0 (
|
|
||||||
if defined OpenCV_DIR (
|
|
||||||
echo Warning: Using OpenCV_DIR from environment variable: %OpenCV_DIR%
|
|
||||||
echo This might not be compatible with MinGW.
|
|
||||||
set "CMAKE_COMMAND=%CMAKE_COMMAND% -DOpenCV_DIR="%OpenCV_DIR%""
|
|
||||||
) else (
|
|
||||||
echo Warning: No OpenCV directory found. CMake will try to find it automatically.
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
:cmake_config
|
|
||||||
%CMAKE_COMMAND%
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo.
|
|
||||||
echo CMake configuration failed!
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo [2/3] Building project...
|
|
||||||
cmake --build .
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo.
|
|
||||||
echo Build failed!
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo Build successful!
|
|
||||||
echo ========================================
|
|
||||||
echo Executables are in: %BUILD_DIR%
|
|
||||||
echo.
|
|
||||||
echo Available targets:
|
|
||||||
echo - test_extract_seal.exe
|
|
||||||
echo - test_remove_border.exe
|
|
||||||
echo.
|
|
||||||
|
|
||||||
pause
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
#include "common.h"
|
|
||||||
|
|
||||||
ShapeInfo::ShapeInfo()
|
|
||||||
: type(""), minAreaRect(cv::Point2f(0, 0), cv::Size2f(0, 0), 0), implement(false) {
|
|
||||||
ignores = {"implement", "needInits", "ignores"};
|
|
||||||
needInits = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeInfo::ShapeInfo(const string& t, const cv::RotatedRect& rect,
|
|
||||||
const vector<cv::Point>& pts,
|
|
||||||
const vector<string>& ign,
|
|
||||||
const vector<string>& ni, bool imp)
|
|
||||||
: type(t), minAreaRect(rect), points(pts), ignores(ign), needInits(ni), implement(imp) {}
|
|
||||||
|
|
||||||
ShapeInfo ShapeInfo::copy() const {
|
|
||||||
return ShapeInfo(type, minAreaRect, points, ignores, needInits, implement);
|
|
||||||
}
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
#ifndef COMMON_H
|
|
||||||
#define COMMON_H
|
|
||||||
|
|
||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
struct ShapeInfo {
|
|
||||||
string type;
|
|
||||||
cv::RotatedRect minAreaRect;
|
|
||||||
vector<cv::Point> points;
|
|
||||||
vector<string> ignores;
|
|
||||||
vector<string> needInits;
|
|
||||||
bool implement;
|
|
||||||
|
|
||||||
ShapeInfo();
|
|
||||||
ShapeInfo(const string& t, const cv::RotatedRect& rect,
|
|
||||||
const vector<cv::Point>& pts,
|
|
||||||
const vector<string>& ign,
|
|
||||||
const vector<string>& ni, bool imp);
|
|
||||||
|
|
||||||
ShapeInfo copy() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum SHAPETYPE {
|
|
||||||
CIRCULAR = 0,
|
|
||||||
ELLIPTICAL = 1,
|
|
||||||
SQUARE = 2,
|
|
||||||
DIAMOND = 3,
|
|
||||||
TRIANGLE = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
inline const char* const SHAPETYPE_NAMES[] = {"circle", "ellipse", "square", "diamond", "triangle"};
|
|
||||||
|
|
||||||
struct SealInfo {
|
|
||||||
int x1;
|
|
||||||
int y1;
|
|
||||||
int x2;
|
|
||||||
int y2;
|
|
||||||
double conf;
|
|
||||||
int shape;
|
|
||||||
cv::Mat cropped_img;
|
|
||||||
cv::Mat extracted_img;
|
|
||||||
std::string color_name;
|
|
||||||
ShapeInfo shape_info;
|
|
||||||
int angle;
|
|
||||||
cv::Mat adj_img;
|
|
||||||
cv::Mat rm_bd_img;
|
|
||||||
std::vector<cv::Mat> txt_img;
|
|
||||||
std::vector<cv::Mat> rec_img;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace seal_data {
|
|
||||||
|
|
||||||
class SealDataManager {
|
|
||||||
public:
|
|
||||||
static SealDataManager& getInstance() {
|
|
||||||
static SealDataManager instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSealSet(const std::vector<SealInfo>& seal_set) {
|
|
||||||
this->seal_set = seal_set;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<SealInfo>& getSealSet() {
|
|
||||||
return seal_set;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasSealSet() const {
|
|
||||||
return !seal_set.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearSealSet() {
|
|
||||||
seal_set.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
SealDataManager() {}
|
|
||||||
~SealDataManager() {}
|
|
||||||
SealDataManager(const SealDataManager&) = delete;
|
|
||||||
SealDataManager& operator=(const SealDataManager&) = delete;
|
|
||||||
|
|
||||||
std::vector<SealInfo> seal_set;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace seal_data
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,142 +0,0 @@
|
|||||||
#include "adjust_image.h"
|
|
||||||
#include "image_morphology.h"
|
|
||||||
#include <opencv2/imgproc.hpp>
|
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static tuple<cv::Mat, int, ShapeInfo> adjust_image_impl(const cv::Mat& img, const ShapeInfo& shapeinfo, int rotate_range) {
|
|
||||||
int final_angle = 0;
|
|
||||||
cv::Mat final_img;
|
|
||||||
|
|
||||||
int img_h = img.rows;
|
|
||||||
int img_w = img.cols;
|
|
||||||
string shapetype = shapeinfo.type;
|
|
||||||
cv::RotatedRect minAreaRect = shapeinfo.minAreaRect;
|
|
||||||
cv::Point2f center = minAreaRect.center;
|
|
||||||
cv::Size2f wh = minAreaRect.size;
|
|
||||||
double wh_ratio = max(wh.width / wh.height, wh.height / wh.width);
|
|
||||||
|
|
||||||
cv::Mat gray;
|
|
||||||
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
|
|
||||||
|
|
||||||
cv::Mat binary;
|
|
||||||
cv::threshold(gray, binary, 200, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
|
|
||||||
|
|
||||||
if (shapetype == "ellipse" || shapetype == "circle") {
|
|
||||||
binary = wipe_star(binary, minAreaRect);
|
|
||||||
}
|
|
||||||
binary = wipe_noise(binary);
|
|
||||||
|
|
||||||
ShapeInfo new_shapeinfo = shapeinfo.copy();
|
|
||||||
cv::Mat fck_gray = wipe_noise_gray(gray);
|
|
||||||
|
|
||||||
if (shapetype == "ellipse" || shapetype == "circle") {
|
|
||||||
fck_gray = my_dilate(fck_gray, cv::Size(16, 16), 1, WHITE_BG);
|
|
||||||
}
|
|
||||||
|
|
||||||
double min_cal = 1e10;
|
|
||||||
cv::Mat first_M;
|
|
||||||
int first_angle = 0;
|
|
||||||
|
|
||||||
for (int angle = -rotate_range; angle < rotate_range; ++angle) {
|
|
||||||
cv::Mat M = cv::getRotationMatrix2D(center, angle, 1.0);
|
|
||||||
cv::Mat fck_gray1;
|
|
||||||
cv::warpAffine(fck_gray, fck_gray1, M, cv::Size(img_w, img_h), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(255));
|
|
||||||
|
|
||||||
int center_x = static_cast<int>(center.x);
|
|
||||||
center_x = min(max(1, center_x), img_w - 1);
|
|
||||||
|
|
||||||
int left_w = center_x;
|
|
||||||
int right_start = (img_w % 2 == 0) ? center_x : (center_x + 1);
|
|
||||||
int right_w = img_w - right_start;
|
|
||||||
int min_w = min(left_w, right_w);
|
|
||||||
|
|
||||||
cv::Mat left, right;
|
|
||||||
if (min_w > 0) {
|
|
||||||
left = fck_gray1(cv::Rect(0, 0, min_w, img_h)).clone();
|
|
||||||
cv::Mat right_part = fck_gray1(cv::Rect(right_start, 0, min_w, img_h));
|
|
||||||
cv::flip(right_part, right, 1);
|
|
||||||
} else {
|
|
||||||
left = cv::Mat::zeros(img_h, 1, fck_gray1.type());
|
|
||||||
right = cv::Mat::zeros(img_h, 1, fck_gray1.type());
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat diff;
|
|
||||||
cv::absdiff(left, right, diff);
|
|
||||||
int cal = cv::countNonZero(diff);
|
|
||||||
|
|
||||||
if (cal < min_cal) {
|
|
||||||
min_cal = cal;
|
|
||||||
first_M = M.clone();
|
|
||||||
first_angle = angle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat first_gray;
|
|
||||||
cv::warpAffine(gray, first_gray, first_M, cv::Size(img_w, img_h), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(255));
|
|
||||||
final_angle = first_angle;
|
|
||||||
|
|
||||||
if (wh_ratio < 1.1 && abs(first_angle) > 45) {
|
|
||||||
cv::Mat binary2;
|
|
||||||
cv::threshold(first_gray, binary2, 200, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
|
|
||||||
|
|
||||||
int x_range[2] = {static_cast<int>(center.x - wh.width / 2 * 0.4),
|
|
||||||
static_cast<int>(center.x + wh.width / 2 * 0.4)};
|
|
||||||
int y_range[2] = {static_cast<int>(center.y - wh.height / 2 * 0.85),
|
|
||||||
static_cast<int>(center.y + wh.height / 2 * 0.85)};
|
|
||||||
|
|
||||||
int tx1 = max(0, x_range[0]);
|
|
||||||
int ty1 = max(0, y_range[0]);
|
|
||||||
int tx2 = min(static_cast<int>(first_gray.cols), x_range[1]);
|
|
||||||
int ty2 = min(static_cast<int>(first_gray.rows), y_range[1]);
|
|
||||||
|
|
||||||
if (tx1 < tx2 && ty1 < ty2) {
|
|
||||||
int range_w = tx2 - tx1;
|
|
||||||
int range_h = ty2 - ty1;
|
|
||||||
if (range_w > 0 && range_h > 0) {
|
|
||||||
cv::Mat hahaha = binary2(cv::Rect(tx1, ty1, range_w, range_h)).clone();
|
|
||||||
|
|
||||||
int up_h = max(1, static_cast<int>(hahaha.rows / 4.0));
|
|
||||||
int down_y = static_cast<int>(hahaha.rows / 4.0 * 3);
|
|
||||||
down_y = min(max(down_y, 0), hahaha.rows - up_h);
|
|
||||||
|
|
||||||
if (up_h > 0 && down_y >= 0 && down_y + up_h <= hahaha.rows) {
|
|
||||||
cv::Mat up = hahaha(cv::Rect(0, 0, hahaha.cols, up_h)).clone();
|
|
||||||
cv::Mat down = hahaha(cv::Rect(0, down_y, hahaha.cols, up_h)).clone();
|
|
||||||
|
|
||||||
int up_cnt = cv::countNonZero(up);
|
|
||||||
int down_cnt = cv::countNonZero(down);
|
|
||||||
double up_div_down = max(up_cnt, down_cnt) / max(1, min(up_cnt, down_cnt));
|
|
||||||
|
|
||||||
if (up_div_down > 2) {
|
|
||||||
if (up_cnt < down_cnt) {
|
|
||||||
final_angle = first_angle + 180;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat final_M = cv::getRotationMatrix2D(center, final_angle, 1);
|
|
||||||
cv::warpAffine(img, final_img, final_M, cv::Size(img_w, img_h), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(255, 255, 255));
|
|
||||||
|
|
||||||
new_shapeinfo.minAreaRect = cv::RotatedRect(new_shapeinfo.minAreaRect.center,
|
|
||||||
new_shapeinfo.minAreaRect.size, 0);
|
|
||||||
|
|
||||||
return make_tuple(final_img, final_angle, new_shapeinfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
tuple<cv::Mat, int, ShapeInfo> adjust_img(const cv::Mat& img, const ShapeInfo& shapeinfo) {
|
|
||||||
string shapetype = shapeinfo.type;
|
|
||||||
|
|
||||||
if (shapetype == "ellipse" || shapetype == "circle") {
|
|
||||||
return adjust_image_impl(img, shapeinfo, 90);
|
|
||||||
} else if (shapetype == "square") {
|
|
||||||
return adjust_image_impl(img, shapeinfo, 30);
|
|
||||||
} else {
|
|
||||||
return make_tuple(img.clone(), 0, shapeinfo.copy());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
#ifndef ADJUST_IMAGE_H
|
|
||||||
#define ADJUST_IMAGE_H
|
|
||||||
|
|
||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <tuple>
|
|
||||||
#include "../common.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
tuple<cv::Mat, int, ShapeInfo> adjust_img(const cv::Mat& img, const ShapeInfo& shapeinfo);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
#include "color_filter.h"
|
|
||||||
#include "hsv_color.h"
|
|
||||||
#include "../image_utilities.h"
|
|
||||||
#include <opencv2/imgproc.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <cmath>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
pair<cv::Mat, cv::Mat> filter_color(const cv::Mat& img, const vector<vector<vector<int>>>& ranges, int fillval /* = 255 */) {
|
|
||||||
cv::Mat out = cv::Mat::ones(img.size(), img.type()) * fillval;
|
|
||||||
cv::Mat hsv;
|
|
||||||
cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);
|
|
||||||
|
|
||||||
cv::Mat mask = cv::Mat::zeros(img.size(), CV_8UC1);
|
|
||||||
|
|
||||||
for (const auto& range_pair : ranges) {
|
|
||||||
cv::Scalar lower(range_pair[0][0], range_pair[0][1], range_pair[0][2]);
|
|
||||||
cv::Scalar upper(range_pair[1][0], range_pair[1][1], range_pair[1][2]);
|
|
||||||
|
|
||||||
cv::Mat temp_mask;
|
|
||||||
cv::inRange(hsv, lower, upper, temp_mask);
|
|
||||||
|
|
||||||
mask += temp_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat pos = mask == 255;
|
|
||||||
for (int y = 0; y < img.rows; y++) {
|
|
||||||
for (int x = 0; x < img.cols; x++) {
|
|
||||||
if (pos.at<uchar>(y, x)) {
|
|
||||||
out.at<cv::Vec3b>(y, x) = img.at<cv::Vec3b>(y, x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {out, mask};
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<cv::Mat, string> color_fliter_process(const cv::Mat& img, int threshold) {
|
|
||||||
try {
|
|
||||||
cv::Mat grayscale;
|
|
||||||
cv::cvtColor(img, grayscale, cv::COLOR_BGR2GRAY);
|
|
||||||
|
|
||||||
cv::Mat binary = block_threshold(grayscale);
|
|
||||||
cv::Mat mask = binary > 0;
|
|
||||||
|
|
||||||
vector<string> color_names = {"red"};
|
|
||||||
vector<cv::Scalar> color_values = {{0, 0, 255}};
|
|
||||||
int best_color_index = 0;
|
|
||||||
double max_color_area = -1e10;
|
|
||||||
cv::Mat final_mask = mask.clone();
|
|
||||||
|
|
||||||
for (int i = 0; i < color_names.size(); i++) {
|
|
||||||
string color_name = color_names[i];
|
|
||||||
vector<vector<vector<int>>> ranges = get_color_range(color_name).check;
|
|
||||||
auto [_, color_mask] = filter_color(img, ranges);
|
|
||||||
cv::Mat color_threshold = color_mask == 255;
|
|
||||||
cv::Mat combined_mask;
|
|
||||||
cv::bitwise_and(color_threshold, mask, combined_mask);
|
|
||||||
double color_area = cv::countNonZero(combined_mask);
|
|
||||||
|
|
||||||
if (color_area > max_color_area) {
|
|
||||||
best_color_index = i;
|
|
||||||
max_color_area = color_area;
|
|
||||||
|
|
||||||
vector<vector<vector<int>>> full_ranges = get_color_range(color_name).full;
|
|
||||||
vector<vector<vector<int>>> strict_ranges = get_color_range(color_name).ranges2;
|
|
||||||
vector<vector<vector<int>>> combined_ranges;
|
|
||||||
|
|
||||||
for (int r = 0; r < full_ranges.size(); r++) {
|
|
||||||
vector<vector<int>> range_pair;
|
|
||||||
vector<int> lower;
|
|
||||||
vector<int> upper;
|
|
||||||
for (int k = 0; k < 3; k++) {
|
|
||||||
int lower_val = static_cast<int>(full_ranges[r][0][k] * (100 - threshold)/100.0 + strict_ranges[r][0][k] * threshold/100.0);
|
|
||||||
int upper_val = static_cast<int>(full_ranges[r][1][k] * (100 - threshold)/100.0 + strict_ranges[r][1][k] * threshold/100.0);
|
|
||||||
lower.push_back(lower_val);
|
|
||||||
upper.push_back(upper_val);
|
|
||||||
}
|
|
||||||
range_pair.push_back(lower);
|
|
||||||
range_pair.push_back(upper);
|
|
||||||
combined_ranges.push_back(range_pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto [__, final_threshold] = filter_color(img, combined_ranges);
|
|
||||||
final_mask = final_threshold == 255;
|
|
||||||
cv::bitwise_and(final_mask, mask, final_mask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat output = cv::Mat(img.size(), img.type(), cv::Scalar(255, 255, 255));
|
|
||||||
cv::Scalar color_value = color_values[best_color_index];
|
|
||||||
|
|
||||||
for (int y = 0; y < img.rows; y++) {
|
|
||||||
for (int x = 0; x < img.cols; x++) {
|
|
||||||
if (final_mask.at<uchar>(y, x)) {
|
|
||||||
output.at<cv::Vec3b>(y, x) = cv::Vec3b(color_value[0], color_value[1], color_value[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {output, color_names[best_color_index]};
|
|
||||||
} catch (const exception& e) {
|
|
||||||
cout << "Exception in color_fliter_process: " << e.what() << endl;
|
|
||||||
return {img, "red"};
|
|
||||||
} catch (...) {
|
|
||||||
cout << "Unknown exception in color_fliter_process" << endl;
|
|
||||||
return {img, "red"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
#ifndef COLOR_FILTER_H
|
|
||||||
#define COLOR_FILTER_H
|
|
||||||
|
|
||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <utility>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "hsv_color.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
pair<cv::Mat, cv::Mat> filter_color(const cv::Mat& img, const vector<vector<vector<int>>>& ranges, int fillval = 255);
|
|
||||||
|
|
||||||
pair<cv::Mat, string> color_fliter_process(const cv::Mat& img, int threshold);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <opencv2/highgui.hpp>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace cv;
|
|
||||||
|
|
||||||
#include <opencv2/imgcodecs.hpp>
|
|
||||||
#include <opencv2/imgproc.hpp>
|
|
||||||
#include "../common.h"
|
|
||||||
#include "color_filter.h"
|
|
||||||
#include "adjust_image.h"
|
|
||||||
|
|
||||||
vector<SealInfo> extract_seal_main(const Mat& img, const vector<vector<double>>& det) {
|
|
||||||
int i = 0;
|
|
||||||
vector<SealInfo> seal_set;
|
|
||||||
for (const auto& seal : det) {
|
|
||||||
int x1 = int(seal[0]);
|
|
||||||
int y1 = int(seal[1]);
|
|
||||||
int x2 = int(seal[2]);
|
|
||||||
int y2 = int(seal[3]);
|
|
||||||
double conf = seal[4];
|
|
||||||
if (conf < 0.6) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int shape = int(seal[5]);
|
|
||||||
|
|
||||||
int roi_x = max(0, x1);
|
|
||||||
int roi_y = max(0, y1);
|
|
||||||
int roi_width = min(x2 - x1, img.cols - roi_x);
|
|
||||||
int roi_height = min(y2 - y1, img.rows - roi_y);
|
|
||||||
|
|
||||||
if (roi_width <= 0 || roi_height <= 0) {
|
|
||||||
cout << "Warning: Invalid ROI for detection box, skipping" << endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat cropped_img = img(Rect(roi_x, roi_y, roi_width, roi_height));
|
|
||||||
|
|
||||||
auto [extracted_img, color_name] = color_fliter_process(cropped_img, 50);
|
|
||||||
|
|
||||||
vector<Mat> channels;
|
|
||||||
split(extracted_img, channels);
|
|
||||||
Mat red_channel = channels[0];
|
|
||||||
|
|
||||||
Scalar mean_val = mean(red_channel);
|
|
||||||
double red_average = mean_val[0];
|
|
||||||
if (red_average > 240) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SealInfo seal_info;
|
|
||||||
seal_info.x1 = x1;
|
|
||||||
seal_info.y1 = y1;
|
|
||||||
seal_info.x2 = x2;
|
|
||||||
seal_info.y2 = y2;
|
|
||||||
seal_info.conf = conf;
|
|
||||||
seal_info.shape = shape;
|
|
||||||
seal_info.cropped_img = cropped_img;
|
|
||||||
seal_info.extracted_img = extracted_img;
|
|
||||||
seal_info.color_name = color_name;
|
|
||||||
|
|
||||||
ShapeInfo si;
|
|
||||||
if (shape >= 0 && shape < 5) {
|
|
||||||
si.type = SHAPETYPE_NAMES[shape];
|
|
||||||
} else {
|
|
||||||
si.type = "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = x2 - x1;
|
|
||||||
int height = y2 - y1;
|
|
||||||
si.minAreaRect = {{width / 2.0f, height / 2.0f}, {float(width), float(height)}, 0};
|
|
||||||
|
|
||||||
auto [adj_img, angle, new_si] = adjust_img(seal_info.extracted_img, ShapeInfo(si.type, si.minAreaRect, si.points, si.ignores, si.needInits, si.implement));
|
|
||||||
seal_info.shape_info = ShapeInfo(new_si.type, new_si.minAreaRect, new_si.points, new_si.ignores, new_si.needInits, new_si.implement);
|
|
||||||
seal_info.angle = angle;
|
|
||||||
seal_info.adj_img = adj_img;
|
|
||||||
|
|
||||||
seal_set.push_back(seal_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 存储到单例类中,实现数据共享
|
|
||||||
seal_data::SealDataManager::getInstance().setSealSet(seal_set);
|
|
||||||
|
|
||||||
return seal_set;
|
|
||||||
}
|
|
||||||
|
|
||||||
// int main() {
|
|
||||||
// // TODO: 修改为你的图片路径
|
|
||||||
// string img_path = "..\\..\\image\\2.png";
|
|
||||||
//
|
|
||||||
// Mat img = imread(img_path);
|
|
||||||
// if (img.empty()) {
|
|
||||||
// cout << "Error: Cannot read image " << img_path << endl;
|
|
||||||
// return 1;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// cout << "Image dimensions: " << img.cols << "x" << img.rows << endl;
|
|
||||||
//
|
|
||||||
// vector<vector<double>> det = {{256.0, 384.0, 471.0, 592.0, 0.980, 2},
|
|
||||||
// {511.0, 423.0, 665.0, 581.0, 0.969, 2}};
|
|
||||||
//
|
|
||||||
// cout << "Number of detection boxes: " << det.size() << endl;
|
|
||||||
// for (const auto& seal : det) {
|
|
||||||
// cout << "Detection box: x1=" << seal[0] << ", y1=" << seal[1] << ", x2=" << seal[2] << ", y2=" << seal[3] << endl;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// vector<SealInfo> seal_info = extract_seal_main(img, det);
|
|
||||||
//
|
|
||||||
// for (size_t i = 0; i < seal_info.size(); ++i) {
|
|
||||||
// // TODO: 修改为你的输出路径
|
|
||||||
// string filename = "test_image\\extracted_seal_";
|
|
||||||
// filename += to_string(i) + ".png";
|
|
||||||
// imwrite(filename, seal_info[i].adj_img);
|
|
||||||
// cout << "Test seal " << i << " saved to: " << filename << endl;
|
|
||||||
// cout << " Shape: " << seal_info[i].shape_info.type << endl;
|
|
||||||
// cout << " Center: (" << seal_info[i].shape_info.minAreaRect.center.x << ", " << seal_info[i].shape_info.minAreaRect.center.y << ")" << endl;
|
|
||||||
// cout << " Size: (" << seal_info[i].shape_info.minAreaRect.size.width << ", " << seal_info[i].shape_info.minAreaRect.size.height << ")" << endl;
|
|
||||||
// cout << " Angle: " << seal_info[i].angle << endl;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#ifndef EXTRACT_SEAL_MAIN_H
|
|
||||||
#define EXTRACT_SEAL_MAIN_H
|
|
||||||
|
|
||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
struct SealInfo;
|
|
||||||
|
|
||||||
vector<SealInfo> extract_seal_main(const cv::Mat& img, const vector<vector<double>>& det);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
#include "hsv_color.h"
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static const map<string, ColorRange> COLOR_RANGES = {
|
|
||||||
{"red", {
|
|
||||||
{{{130, 50, 100}, {180, 255, 255}}, {{0, 50, 100}, {6, 255, 255}}},
|
|
||||||
{{{130, 70, 100}, {180, 255, 255}}, {{0, 70, 100}, {12, 255, 255}}},
|
|
||||||
{{{130, 43, 60}, {180, 255, 255}}, {{0, 43, 60}, {12, 255, 255}}},
|
|
||||||
{{{130, 5, 5}, {180, 255, 255}}, {{0, 5, 5}, {25, 255, 255}}}
|
|
||||||
}},
|
|
||||||
{"blue", {
|
|
||||||
{{{78, 50, 50}, {124, 255, 255}}},
|
|
||||||
{{{78, 70, 50}, {124, 255, 255}}},
|
|
||||||
{{{78, 43, 20}, {124, 255, 255}}},
|
|
||||||
{{{78, 43, 0}, {124, 255, 255}}}
|
|
||||||
}},
|
|
||||||
{"black", {
|
|
||||||
{{{0, 0, 0}, {180, 255, 43}}},
|
|
||||||
{{{0, 0, 0}, {180, 255, 43}}},
|
|
||||||
{{{0, 0, 0}, {180, 255, 43}}},
|
|
||||||
{{{0, 0, 0}, {180, 255, 43}}}
|
|
||||||
}}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ColorRange& get_color_range(const string& color) {
|
|
||||||
auto it = COLOR_RANGES.find(color);
|
|
||||||
if (it != COLOR_RANGES.end()) {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
throw invalid_argument("Color not found: " + color);
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
#ifndef HSV_COLOR_H
|
|
||||||
#define HSV_COLOR_H
|
|
||||||
|
|
||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
struct ColorRange {
|
|
||||||
vector<vector<vector<int>>> ranges1;
|
|
||||||
vector<vector<vector<int>>> ranges2;
|
|
||||||
vector<vector<vector<int>>> check;
|
|
||||||
vector<vector<vector<int>>> full;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ColorRange& get_color_range(const string& color);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
#include "image_morphology.h"
|
|
||||||
#include <opencv2/imgproc.hpp>
|
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
const string WHITE_BG = "white";
|
|
||||||
const string BLACK_BG = "black";
|
|
||||||
|
|
||||||
cv::Mat my_dilate(const cv::Mat& img, const cv::Size& ksize, int iter, const string& bg_type) {
|
|
||||||
assert(bg_type == WHITE_BG || bg_type == BLACK_BG);
|
|
||||||
|
|
||||||
cv::Mat kernel(ksize.height, ksize.width, CV_8UC1, cv::Scalar(255));
|
|
||||||
cv::Mat result;
|
|
||||||
|
|
||||||
if (bg_type == BLACK_BG) {
|
|
||||||
cv::dilate(img, result, kernel, cv::Point(-1, -1), iter);
|
|
||||||
} else {
|
|
||||||
cv::erode(img, result, kernel, cv::Point(-1, -1), iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat wipe_noise(const cv::Mat& img) {
|
|
||||||
cv::Mat result = img.clone();
|
|
||||||
int num_objects;
|
|
||||||
cv::Mat labels, stats, centroids;
|
|
||||||
num_objects = cv::connectedComponentsWithStats(img, labels, stats, centroids, 8);
|
|
||||||
|
|
||||||
int img_min = min(img.rows, img.cols);
|
|
||||||
double scale = pow(max(1.0, img_min / 300.0), 2);
|
|
||||||
int threshold_area = static_cast<int>(10 * scale);
|
|
||||||
|
|
||||||
for (int index = 0; index < num_objects; ++index) {
|
|
||||||
int area = stats.at<int>(index, 4);
|
|
||||||
if (area < threshold_area) {
|
|
||||||
result.setTo(0, labels == index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat wipe_noise_gray(const cv::Mat& img) {
|
|
||||||
cv::Mat result = img.clone();
|
|
||||||
cv::Mat blur;
|
|
||||||
cv::GaussianBlur(img, blur, cv::Size(3, 3), 0);
|
|
||||||
|
|
||||||
cv::Mat binary;
|
|
||||||
cv::threshold(blur, binary, 200, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
|
|
||||||
|
|
||||||
int num_objects;
|
|
||||||
cv::Mat labels, stats, centroids;
|
|
||||||
num_objects = cv::connectedComponentsWithStats(binary, labels, stats, centroids, 8);
|
|
||||||
|
|
||||||
int img_min = min(img.rows, img.cols);
|
|
||||||
double scale = pow(max(1.0, img_min / 300.0), 2);
|
|
||||||
int threshold_area = static_cast<int>(10 * scale);
|
|
||||||
|
|
||||||
for (int index = 0; index < num_objects; ++index) {
|
|
||||||
int area = stats.at<int>(index, 4);
|
|
||||||
if (area < threshold_area) {
|
|
||||||
result.setTo(255, labels == index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat wipe_star(const cv::Mat& img, const cv::RotatedRect& minAreaRect) {
|
|
||||||
cv::Mat result = img.clone();
|
|
||||||
cv::Point2f center = minAreaRect.center;
|
|
||||||
cv::Size2f wh = minAreaRect.size;
|
|
||||||
double r = max(wh.width, wh.height) / min(wh.width, wh.height);
|
|
||||||
int base_wh = static_cast<int>(wh.height / 2 + wh.height / 20);
|
|
||||||
double _W = base_wh * r;
|
|
||||||
double _H = base_wh;
|
|
||||||
|
|
||||||
int num_objects;
|
|
||||||
cv::Mat labels, stats, centroids;
|
|
||||||
num_objects = cv::connectedComponentsWithStats(img, labels, stats, centroids, 8);
|
|
||||||
|
|
||||||
for (int index = 1; index < num_objects; ++index) {
|
|
||||||
int x = stats.at<int>(index, 0);
|
|
||||||
int y = stats.at<int>(index, 1);
|
|
||||||
int w = stats.at<int>(index, 2);
|
|
||||||
int h = stats.at<int>(index, 3);
|
|
||||||
cv::Point2f _center(x + w / 2.0, y + h / 2.0);
|
|
||||||
double center_dis = sqrt(pow(_center.x - center.x, 2) + pow(_center.y - center.y, 2));
|
|
||||||
double hehe = min(w, h);
|
|
||||||
|
|
||||||
if (hehe > max(_W, _H) * 0.25 && hehe < min(_W, _H) && center_dis < max(_W, _H) * 0.2) {
|
|
||||||
result.setTo(0, labels == index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r < 1.2) {
|
|
||||||
int y1 = static_cast<int>(center.y - wh.height * 0.15);
|
|
||||||
int y2 = static_cast<int>(center.y + wh.height * 0.15);
|
|
||||||
int x1 = static_cast<int>(center.x - wh.width * 0.15);
|
|
||||||
int x2 = static_cast<int>(center.x + wh.width * 0.15);
|
|
||||||
|
|
||||||
int tx1 = max(0, x1);
|
|
||||||
int ty1 = max(0, y1);
|
|
||||||
int tx2 = min(static_cast<int>(result.cols), x2);
|
|
||||||
int ty2 = min(static_cast<int>(result.rows), y2);
|
|
||||||
|
|
||||||
if (tx1 < tx2 && ty1 < ty2) {
|
|
||||||
result(cv::Rect(tx1, ty1, tx2 - tx1, ty2 - ty1)).setTo(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
#ifndef IMAGE_MORPHOLOGY_H
|
|
||||||
#define IMAGE_MORPHOLOGY_H
|
|
||||||
|
|
||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
extern const string WHITE_BG;
|
|
||||||
extern const string BLACK_BG;
|
|
||||||
|
|
||||||
cv::Mat my_dilate(const cv::Mat& img, const cv::Size& ksize = cv::Size(5, 5), int iter = 1, const string& bg_type = BLACK_BG);
|
|
||||||
|
|
||||||
cv::Mat wipe_noise(const cv::Mat& img);
|
|
||||||
|
|
||||||
cv::Mat wipe_noise_gray(const cv::Mat& img);
|
|
||||||
|
|
||||||
cv::Mat wipe_star(const cv::Mat& img, const cv::RotatedRect& minAreaRect);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,145 +0,0 @@
|
|||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <opencv2/highgui.hpp>
|
|
||||||
#include <opencv2/imgcodecs.hpp>
|
|
||||||
#include <opencv2/imgproc.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <filesystem>
|
|
||||||
#include "../common.h"
|
|
||||||
#include "extract_seal_main.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
struct ImageTask {
|
|
||||||
string img_path;
|
|
||||||
vector<vector<double>> det;
|
|
||||||
};
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
// TODO: 修改为你的图片目录路径
|
|
||||||
// 示例: "c:\\Users\\用户名\\Desktop\\Final project\\image" 或使用相对路径 "..\\..\\image"
|
|
||||||
string image_dir = "..\\..\\image";
|
|
||||||
vector<ImageTask> tasks;
|
|
||||||
|
|
||||||
cout << "Scanning image directory: " << image_dir << endl;
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (const auto& entry : fs::directory_iterator(image_dir)) {
|
|
||||||
if (entry.is_regular_file()) {
|
|
||||||
string ext = entry.path().extension().string();
|
|
||||||
if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp") {
|
|
||||||
ImageTask task;
|
|
||||||
task.img_path = entry.path().string();
|
|
||||||
|
|
||||||
string filename = entry.path().filename().string();
|
|
||||||
if (filename == "2.png") {
|
|
||||||
task.det = {
|
|
||||||
{256.0, 384.0, 471.0, 592.0, 0.980, 2},
|
|
||||||
{511.0, 423.0, 665.0, 581.0, 0.969, 2}
|
|
||||||
};
|
|
||||||
} else if (filename == "1.png") {
|
|
||||||
task.det = {
|
|
||||||
{408.0, 32.0, 559.0, 186.0, 0.974, 0},
|
|
||||||
{267.0, 61.0, 348.0, 143.0, 0.973, 2},
|
|
||||||
{54.0, 36.0, 212.0, 147.0, 0.972, 1}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.push_back(task);
|
|
||||||
cout << "Found image: " << filename << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (const fs::filesystem_error& e) {
|
|
||||||
cout << "Error scanning directory: " << e.what() << endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tasks.empty()) {
|
|
||||||
cout << "No image files found in directory." << endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "\nTotal images found: " << tasks.size() << endl;
|
|
||||||
|
|
||||||
for (size_t task_idx = 0; task_idx < tasks.size(); ++task_idx) {
|
|
||||||
const ImageTask& task = tasks[task_idx];
|
|
||||||
string img_path = task.img_path;
|
|
||||||
vector<vector<double>> det = task.det;
|
|
||||||
|
|
||||||
cout << "\n========================================" << endl;
|
|
||||||
cout << "Processing image " << task_idx + 1 << "/" << tasks.size() << endl;
|
|
||||||
cout << "File: " << fs::path(img_path).filename().string() << endl;
|
|
||||||
cout << "========================================" << endl;
|
|
||||||
|
|
||||||
cv::Mat img = cv::imread(img_path);
|
|
||||||
if (img.empty()) {
|
|
||||||
cout << "Error: Cannot read image " << img_path << endl;
|
|
||||||
if (task_idx < tasks.size() - 1) {
|
|
||||||
cout << "Press Enter to continue to next image..." << endl;
|
|
||||||
cin.ignore(numeric_limits<streamsize>::max(), '\n');
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "Image dimensions: " << img.cols << "x" << img.rows << endl;
|
|
||||||
|
|
||||||
cout << "Number of detection boxes: " << det.size() << endl;
|
|
||||||
for (size_t i = 0; i < det.size(); ++i) {
|
|
||||||
cout << "Detection box " << i+1 << ": x1=" << det[i][0] << ", y1=" << det[i][1]
|
|
||||||
<< ", x2=" << det[i][2] << ", y2=" << det[i][3]
|
|
||||||
<< ", conf=" << det[i][4] << ", shape=" << det[i][5] << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "\nStart extracting seals..." << endl;
|
|
||||||
vector<SealInfo> seal_set = extract_seal_main(img, det);
|
|
||||||
|
|
||||||
cout << "Total extracted seals: " << seal_set.size() << endl << endl;
|
|
||||||
|
|
||||||
// TODO: 修改为你的输出目录路径
|
|
||||||
// 示例: "c:\\Users\\用户名\\Desktop\\Final project\\c\\extract_seal_main\\test_image" 或使用相对路径 "..\\extract_seal_main\\test_image"
|
|
||||||
string output_dir = "..\\extract_seal_main\\test_image";
|
|
||||||
fs::create_directories(output_dir);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < seal_set.size(); ++i) {
|
|
||||||
cout << "========== Seal " << i << " ==========" << endl;
|
|
||||||
|
|
||||||
string img_name = fs::path(img_path).stem().string();
|
|
||||||
string cropped_filename = output_dir + "\\seal_" + img_name + "_" + to_string(i) + "_cropped.png";
|
|
||||||
string ex_filename = output_dir + "\\seal_" + img_name + "_" + to_string(i) + "_ex_img.png";
|
|
||||||
string adj_filename = output_dir + "\\seal_" + img_name + "_" + to_string(i) + "_adj_img.png";
|
|
||||||
|
|
||||||
cv::imwrite(cropped_filename, seal_set[i].cropped_img);
|
|
||||||
cv::imwrite(ex_filename, seal_set[i].extracted_img);
|
|
||||||
cv::imwrite(adj_filename, seal_set[i].adj_img);
|
|
||||||
|
|
||||||
cout << "Saved files:" << endl;
|
|
||||||
cout << " " << cropped_filename << " (cropped_img)" << endl;
|
|
||||||
cout << " " << ex_filename << " (extracted_img)" << endl;
|
|
||||||
cout << " " << adj_filename << " (adj_img)" << endl;
|
|
||||||
|
|
||||||
cout << endl << "region: [" << seal_set[i].x1 << ", " << seal_set[i].y1 << ", "
|
|
||||||
<< seal_set[i].x2 << ", " << seal_set[i].y2 << "]" << endl;
|
|
||||||
cout << "shape: " << seal_set[i].shape << endl;
|
|
||||||
cout << "color_name: " << seal_set[i].color_name << endl;
|
|
||||||
cout << "angle: " << seal_set[i].angle << endl;
|
|
||||||
cout << "shape_info.type: " << seal_set[i].shape_info.type << endl;
|
|
||||||
cout << "shape_info.minAreaRect: center=("
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.center.x << ", "
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.center.y << "), size=("
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.size.width << ", "
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.size.height << ")" << endl;
|
|
||||||
cout << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task_idx < tasks.size() - 1) {
|
|
||||||
cout << "Press Enter to process next image..." << endl;
|
|
||||||
cin.ignore(numeric_limits<streamsize>::max(), '\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "\nAll images processed!" << endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
#include "image_utilities.h"
|
|
||||||
#include <opencv2/imgproc.hpp>
|
|
||||||
#include <vector>
|
|
||||||
#include <cmath>
|
|
||||||
#include <iostream>
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
vector<cv::Point> mrect2box(const cv::RotatedRect& mrect) {
|
|
||||||
cv::Point2f boxPoints[4];
|
|
||||||
mrect.points(boxPoints);
|
|
||||||
|
|
||||||
vector<cv::Point> result;
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
result.emplace_back(cv::Point(static_cast<int>(boxPoints[i].x), static_cast<int>(boxPoints[i].y)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat block_threshold(const cv::Mat& gray) {
|
|
||||||
try {
|
|
||||||
int height = gray.rows;
|
|
||||||
int width = gray.cols;
|
|
||||||
|
|
||||||
vector<int> divisions = {8, 13};
|
|
||||||
vector<int> block_sizes;
|
|
||||||
for (int div : divisions) {
|
|
||||||
int size = max(19, (height + width) / 2 / div);
|
|
||||||
if (size % 2 == 0) size++;
|
|
||||||
block_sizes.push_back(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<pair<int, int>> block_strategies;
|
|
||||||
for (int size : block_sizes) {
|
|
||||||
block_strategies.push_back({size, 0});
|
|
||||||
block_strategies.push_back({size, size / 2});
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat otsu;
|
|
||||||
double global_threshold = cv::threshold(gray, otsu, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
|
|
||||||
|
|
||||||
cv::Mat adaptive_ref;
|
|
||||||
cv::adaptiveThreshold(gray, adaptive_ref, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, block_sizes[0], 20);
|
|
||||||
|
|
||||||
cv::Mat result = cv::Mat::zeros(gray.size(), CV_8UC1);
|
|
||||||
|
|
||||||
for (auto& strategy : block_strategies) {
|
|
||||||
int block_size = strategy.first;
|
|
||||||
int offset = strategy.second;
|
|
||||||
|
|
||||||
cv::Mat current_result = cv::Mat::zeros(gray.size(), CV_8UC1);
|
|
||||||
|
|
||||||
for (int y = offset; y < height; y += block_size) {
|
|
||||||
for (int x = offset; x < width; x += block_size) {
|
|
||||||
int end_y = min(y + block_size, height);
|
|
||||||
int end_x = min(x + block_size, width);
|
|
||||||
|
|
||||||
cv::Mat block_roi = gray(cv::Rect(x, y, end_x - x, end_y - y));
|
|
||||||
|
|
||||||
cv::Scalar mean, stddev;
|
|
||||||
cv::meanStdDev(block_roi, mean, stddev);
|
|
||||||
double variance = stddev[0] * stddev[0];
|
|
||||||
|
|
||||||
cv::Mat block_result;
|
|
||||||
if (variance < 80) {
|
|
||||||
block_result = cv::Mat::zeros(block_roi.size(), CV_8UC1);
|
|
||||||
if (mean[0] < global_threshold) {
|
|
||||||
block_result = cv::Mat::ones(block_roi.size(), CV_8UC1) * 255;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cv::threshold(block_roi, block_result, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
|
|
||||||
|
|
||||||
cv::Scalar ref_mean, block_mean;
|
|
||||||
cv::Mat adaptive_block = adaptive_ref(cv::Rect(x, y, end_x - x, end_y - y));
|
|
||||||
cv::meanStdDev(adaptive_block, ref_mean, cv::noArray());
|
|
||||||
cv::meanStdDev(block_result, block_mean, cv::noArray());
|
|
||||||
|
|
||||||
if (abs(ref_mean[0] - block_mean[0]) > 200) {
|
|
||||||
block_result = 255 - block_result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block_result.copyTo(current_result(cv::Rect(x, y, end_x - x, end_y - y)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::bitwise_or(result, current_result, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (const exception& e) {
|
|
||||||
cout << "Exception in block_threshold: " << e.what() << endl;
|
|
||||||
return cv::Mat();
|
|
||||||
} catch (...) {
|
|
||||||
cout << "Unknown exception in block_threshold" << endl;
|
|
||||||
return cv::Mat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#ifndef IMAGE_UTILITIES_H
|
|
||||||
#define IMAGE_UTILITIES_H
|
|
||||||
|
|
||||||
#include <opencv2/core.hpp>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
cv::Mat block_threshold(const cv::Mat& gray);
|
|
||||||
|
|
||||||
vector<cv::Point> mrect2box(const cv::RotatedRect& mrect);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
#ifndef REMOVE_BORDER_MAIN_H
|
|
||||||
#define REMOVE_BORDER_MAIN_H
|
|
||||||
|
|
||||||
#include "../common.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void remove_border_main(vector<SealInfo>& seal_set, bool is_debug = false);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
#include <opencv2/opencv.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <fstream>
|
|
||||||
#include <filesystem>
|
|
||||||
#include "../extract_seal_main/extract_seal_main.h"
|
|
||||||
#include "remove_border.h"
|
|
||||||
#include "remove_border_main.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
void process_seal_remove_border() {
|
|
||||||
// TODO: 修改为你的测试图片路径
|
|
||||||
// 示例: "c:\\Users\\用户名\\Desktop\\Final project\\image\\1.png" 或使用相对路径 "..\\..\\image\\1.png"
|
|
||||||
string img_path = "..\\..\\image\\1.png";
|
|
||||||
|
|
||||||
cout << "Image path: " << img_path << endl;
|
|
||||||
|
|
||||||
ifstream file(img_path);
|
|
||||||
if (!file.good()) {
|
|
||||||
cout << "Error: Image file does not exist!" << endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
cv::Mat img = cv::imread(img_path);
|
|
||||||
if (img.empty()) {
|
|
||||||
cout << "Error: Cannot read image!" << endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "Image loaded: " << img.cols << "x" << img.rows << endl;
|
|
||||||
|
|
||||||
vector<vector<double>> det = {
|
|
||||||
{408.0, 32.0, 559.0, 186.0, 0.974, 0},
|
|
||||||
{267.0, 61.0, 348.0, 143.0, 0.973, 2},
|
|
||||||
{54.0, 36.0, 212.0, 147.0, 0.972, 1}
|
|
||||||
};
|
|
||||||
|
|
||||||
cout << "Number of detection boxes: " << det.size() << endl;
|
|
||||||
|
|
||||||
cout << "Extracting seals..." << endl;
|
|
||||||
try {
|
|
||||||
vector<SealInfo> seal_set = extract_seal_main(img, det);
|
|
||||||
|
|
||||||
cout << "Total extracted seals: " << seal_set.size() << endl;
|
|
||||||
|
|
||||||
if (seal_set.empty()) {
|
|
||||||
cout << "Error: No seals extracted!" << endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < seal_set.size(); ++i) {
|
|
||||||
cout << "Seal " << i << ":" << endl;
|
|
||||||
cout << " Shape: " << seal_set[i].shape_info.type << endl;
|
|
||||||
cout << " Adjusted image size: " << seal_set[i].adj_img.cols << "x" << seal_set[i].adj_img.rows << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << endl << "Removing borders..." << endl;
|
|
||||||
remove_border_main(seal_set, false);
|
|
||||||
|
|
||||||
string output_dir = "..\\remove_border\\test_image";
|
|
||||||
fs::create_directories(output_dir);
|
|
||||||
|
|
||||||
cout << endl << "========== Results after remove_border ==========" << endl;
|
|
||||||
for (size_t i = 0; i < seal_set.size(); ++i) {
|
|
||||||
cout << endl << "========== Seal " << i << " ==========" << endl;
|
|
||||||
|
|
||||||
if (seal_set[i].rm_bd_img.empty()) {
|
|
||||||
cout << " Warning: rm_bd_img is empty!" << endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string save_path = output_dir + "\\seal_" + to_string(i) + "_rm_bd_img.png";
|
|
||||||
cv::imwrite(save_path, seal_set[i].rm_bd_img);
|
|
||||||
cout << "rm_bd_img saved: " << save_path << endl;
|
|
||||||
cout << "rm_bd_img size: " << seal_set[i].rm_bd_img.cols << "x" << seal_set[i].rm_bd_img.rows << endl;
|
|
||||||
|
|
||||||
cout << "shape_info.type: " << seal_set[i].shape_info.type << endl;
|
|
||||||
cout << "shape_info.minAreaRect: center=("
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.center.x << ", "
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.center.y << "), size=("
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.size.width << ", "
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.size.height << "), angle="
|
|
||||||
<< seal_set[i].shape_info.minAreaRect.angle << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << endl << "Done!" << endl;
|
|
||||||
} catch (const exception& e) {
|
|
||||||
cout << "Error: " << e.what() << endl;
|
|
||||||
} catch (...) {
|
|
||||||
cout << "Error: Unknown exception occurred!" << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
process_seal_remove_border();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 306 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 MiB |
Loading…
Reference in New Issue