Compare commits
2 Commits
7c520cf384
...
1abf364f2e
| Author | SHA1 | Date |
|---|---|---|
|
|
1abf364f2e | 2 months ago |
|
|
01be3d03c3 | 2 months ago |
@ -0,0 +1,25 @@
|
|||||||
|
# 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
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
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})
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
@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
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
#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
|
||||||
@ -0,0 +1,142 @@
|
|||||||
|
#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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
#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
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
#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"};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
#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
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
#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;
|
||||||
|
// }
|
||||||
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
#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
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
#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
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
#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
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
#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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
#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
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
#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
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
#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.
|
After Width: | Height: | Size: 306 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
Loading…
Reference in New Issue