Files
eUi/include/opencvPaintAlgo.hpp
T
2025-07-11 02:02:09 +08:00

193 lines
6.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <opencv.hpp>
#pragma region 画叉
static void Cross(cv::Mat& image, const cv::Point& center, int length, int thickness, const cv::Scalar& color = cv::Scalar(255, 255, 255)) {
// 检查输入图像是否有效
if (image.empty()) {
throw std::invalid_argument("Input image is empty");
}
// 计算水平和垂直线段的起点和终点
cv::Point horizontalStart(center.x - length / 2, center.y);
cv::Point horizontalEnd(center.x + length / 2, center.y);
cv::Point verticalStart(center.x, center.y - length / 2);
cv::Point verticalEnd(center.x, center.y + length / 2);
// 绘制水平和垂直线段
cv::line(image, horizontalStart, horizontalEnd, color, thickness);
cv::line(image, verticalStart, verticalEnd, color, thickness);
}
#pragma endregion
#pragma region 写中文
static std::wstring utf8ToWstring(const std::string& utf8Str) {
int len = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, nullptr, 0);
if (len == 0) return L"";
std::wstring wstr(len, L'\0');
MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &wstr[0], len);
wstr.pop_back(); // remove null terminator
return wstr;
}
// 在图像上绘制中文(仅 Windows,使用 GDI)
static void putTextZH(cv::Mat& image, const std::string& text, cv::Point org, int fontSize = 20, cv::Scalar color = cv::Scalar(255, 0, 0)) {
if (image.channels() != 3)
throw std::runtime_error("Only supports 3-channel images");
// 转为 BGRA
cv::Mat img;
cv::cvtColor(image, img, cv::COLOR_BGR2BGRA);
HDC hdc = GetDC(nullptr);
HDC memDC = CreateCompatibleDC(hdc);
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = img.cols;
bmi.bmiHeader.biHeight = -img.rows;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
void* pBits = nullptr;
HBITMAP hBitmap = CreateDIBSection(memDC, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
SelectObject(memDC, hBitmap);
memcpy(pBits, img.data, img.total() * img.elemSize());
HFONT hFont = CreateFontW(fontSize, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_SWISS, L"微软雅黑");
SelectObject(memDC, hFont);
SetBkMode(memDC, TRANSPARENT);
SetTextColor(memDC, RGB(color[2], color[1], color[0]));
TextOutW(memDC, org.x, org.y, utf8ToWstring(text).c_str(), static_cast<int>(utf8ToWstring(text).length()));
GetDIBits(memDC, hBitmap, 0, img.rows, img.data, &bmi, DIB_RGB_COLORS);
cv::cvtColor(img, image, cv::COLOR_BGRA2BGR);
DeleteObject(hFont);
DeleteObject(hBitmap);
DeleteDC(memDC);
ReleaseDC(nullptr, hdc);
}
class TextRendererBase {
public:
virtual ~TextRendererBase() = default;
virtual void putText(cv::Mat& image, const std::string& text, cv::Point org, cv::Scalar color = cv::Scalar(255, 0, 0)) = 0;
};
class TextRendererZH : public TextRendererBase {
public:
TextRendererZH(int width, int height, int fontSize = 20, const std::wstring& fontName = L"微软雅黑")
: width_(width), height_(height), fontSize_(fontSize), fontName_(fontName)
{
screenDC_ = GetDC(nullptr);
createResources();
}
~TextRendererZH() {
releaseResources();
if (screenDC_) ReleaseDC(nullptr, screenDC_);
}
void putText(cv::Mat& image, const std::string& text, cv::Point org, cv::Scalar color = cv::Scalar(255, 0, 0)) override {
if (image.channels() != 3)
throw std::runtime_error("TextRendererZH only supports 3-channel BGR images.");
if (image.cols != width_ || image.rows != height_) {
resize(image.cols, image.rows);
}
cv::Mat img;
cv::cvtColor(image, img, cv::COLOR_BGR2BGRA);
memcpy(pBits_, img.data, img.total() * img.elemSize());
SetBkMode(memDC_, TRANSPARENT);
SetTextColor(memDC_, RGB(color[2], color[1], color[0]));
std::wstring wtext = utf8ToWstring(text);
TextOutW(memDC_, org.x, org.y, wtext.c_str(), (int)wtext.length());
GetDIBits(memDC_, hBitmap_, 0, height_, img.data, &bmi_, DIB_RGB_COLORS);
cv::cvtColor(img, image, cv::COLOR_BGRA2BGR);
}
void resize(int width, int height) {
if (width == width_ && height == height_) return;
releaseResources();
width_ = width;
height_ = height;
createResources();
}
private:
void createResources() {
memDC_ = CreateCompatibleDC(screenDC_);
memset(&bmi_, 0, sizeof(bmi_));
bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi_.bmiHeader.biWidth = width_;
bmi_.bmiHeader.biHeight = -height_;
bmi_.bmiHeader.biPlanes = 1;
bmi_.bmiHeader.biBitCount = 32;
bmi_.bmiHeader.biCompression = BI_RGB;
hBitmap_ = CreateDIBSection(memDC_, &bmi_, DIB_RGB_COLORS, &pBits_, nullptr, 0);
SelectObject(memDC_, hBitmap_);
hFont_ = CreateFontW(
fontSize_, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_SWISS, fontName_.c_str());
SelectObject(memDC_, hFont_);
}
void releaseResources() {
if (hFont_) {
DeleteObject(hFont_);
hFont_ = nullptr;
}
if (hBitmap_) {
DeleteObject(hBitmap_);
hBitmap_ = nullptr;
pBits_ = nullptr;
}
if (memDC_) {
DeleteDC(memDC_);
memDC_ = nullptr;
}
}
// UTF8 转宽字符串
static std::wstring utf8ToWstring(const std::string& str) {
if (str.empty()) return {};
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
std::wstring wstrTo(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstrTo[0], size_needed);
return wstrTo;
}
private:
int width_;
int height_;
int fontSize_;
std::wstring fontName_;
HDC screenDC_ = nullptr;
HDC memDC_ = nullptr;
HBITMAP hBitmap_ = nullptr;
void* pBits_ = nullptr;
BITMAPINFO bmi_{};
HFONT hFont_ = nullptr;
};
#pragma endregion