Measure/DN_sample/MicroImage.cpp
2024-07-18 18:17:33 +08:00

503 lines
13 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.

#include "stdafx.h"
#include "MicroImage.h"
using namespace cv;
using namespace std;
MicroImage::MicroImage()
: m_width(0)
, m_height(0)
, m_OriginData(nullptr)
, m_CalData(nullptr)
, m_ImgName("")
, m_DistanceX(0.f)
, m_DistanceY(0.f)
, m_knowLength(0)
, m_TotalSize(0){
m_image.release();
}
MicroImage::~MicroImage() {
if(m_OriginData) delete m_OriginData;
if(m_CalData) delete m_CalData;
m_image.release();
}
std::string MicroImage::GetStrTime()
{
char buffer[50];
SYSTEMTIME snow;
GetLocalTime(&snow);
sprintf_s(buffer, sizeof(buffer), u8"%02d%02d%02d%02d%02d%03d", snow.wMonth, snow.wDay, snow.wHour, snow.wMinute, snow.wSecond, snow.wMilliseconds);
return string(buffer);
}
void MicroImage::Init(const BITMAP& bmp) {
m_height = bmp.bmHeight;
m_width = bmp.bmWidth;
m_Radio = m_width / m_height;
m_TotalSize = ((bmp.bmWidth * bmp.bmBitsPixel + 31) / 32) * 4 * bmp.bmHeight * bmp.bmPlanes;
m_OriginData = new uchar[m_TotalSize];
m_ImgName = u8"抓图" + GetStrTime();
memcpy_s(m_OriginData, m_TotalSize, bmp.bmBits, m_TotalSize);
//m_image = cv::Mat(m_height, m_width, CV_8UC3, m_OriginData);
//cv::imwrite(m_ImgName, m_image);
}
void MicroImage::CropByBorder(int yBegin, int yEnd, int xBegin, int xEnd) {
for (int y = yBegin; y <= yEnd; ++y) {
for (int x = xBegin; x <= xEnd; ++x) {
auto& pc = m_image.at<Vec3b>(y, x);
if (pc[0] != 0 || pc[1] != 0 || pc[2] != 0) {
pc[0] = 0; pc[1] = 0; pc[2] = 0;
}
}
}
}
void MicroImage::StatisticsBGColor() {
Rect cropRect1(m_width / 2 - 40, m_height / 2 - 40, 80, 80); // 中心位置
cropRect1 &= Rect(0, 0, m_width, m_height); // 检查ROI是否在图像范围内
Mat cropMat1 = m_image(cropRect1);
Rect cropRect2(0, m_height / 2 - 25, 50, 50); //中左位置
cropRect2 &= Rect(0, 0, m_width, m_height);
Mat cropMat2 = m_image(cropRect2);
Rect cropRect3(m_width - 25, m_height / 2 - 25, 50, 50); //中右位置
cropRect3 &= Rect(0, 0, m_width, m_height);
Mat cropMat3 = m_image(cropRect3);
Rect cropRect4(0, 0, 25, 25); //左上角位置
cropRect4 &= Rect(0, 0, m_width, m_height);
Mat cropMat4 = m_image(cropRect4);
StatisticsRGB(cropMat1);
StatisticsRGB(cropMat2);
StatisticsRGB(cropMat3);
StatisticsRGB(cropMat4);
printf("rgb size:%zd\n", m_RgbSet.size());
}
void MicroImage::StatisticsRGB(cv::Mat& cropMat) {
for (int y = 0; y < cropMat.rows; ++y) {
for (int x = 0; x < cropMat.cols; ++x) {
Vec3b pixel = cropMat.at<Vec3b>(y, x);
int r = pixel[2]; // Red
int g = pixel[1]; // Green
int b = pixel[0]; // Blue
//RGB 每个值范围[-2,1],趋近0偏黑
for (int i = -2; i <= 1; ++i) {
for (int j = -2; j <= 1; ++j) {
for (int k = -2; k <= 1; ++k) {
int rr = max(r + i, 0), gg = max(g + j, 0), bb = max(b + k, 0);
rr = min(r + i, 255), gg = min(g + j, 255), bb = min(b + k, 255);
m_RgbSet.insert((rr << 16) | (gg << 8) | (bb));
}
}
}
}
}
}
void MicroImage::DrawRedLine(int length, int x, int y) {
int centerx = m_width / 2, centery = m_height / 2;
if (x == centerx) { //画水平线
for (int xx = centerx - length / 2; xx < centerx + length / 2; ++xx) { //设置红色
auto& pixel = m_image.at<Vec3b>(y, xx);
pixel[0] = 0;
pixel[1] = 0;
pixel[2] = 255;
}
}
else { //画垂直线
for (int yy = centery - length / 2; yy < centery + length / 2; ++yy) { //设置红色
auto& pixel = m_image.at<Vec3b>(yy, x);
pixel[0] = 0;
pixel[1] = 0;
pixel[2] = 255;
}
}
}
bool MicroImage::IslandCheck(int y, int x, std::set<int>& tempSet) {
tempSet.insert((y << 16) | x);
queue<int> que;
que.push((y << 16) | x);
while (!que.empty()) {
x = que.front() & 0xffff;
y = (que.front() >> 16) & 0xffff;
que.pop();
int val = (y << 16) + x + 1; //right
int colorVal = 0;
if (tempSet.find(val) == tempSet.end() && x + 1 < m_width) {
auto& right = m_image.at<Vec3b>(y, x + 1);
if (right[0] != 0 || right[1] != 0 || right[2] != 0) { //不是黑色判断
tempSet.insert(val);
que.push(val);
}
}
val = ((y + 1) << 16) + x; //down
if (tempSet.find(val) == tempSet.end() && y + 1 < m_height) {
auto& down = m_image.at<Vec3b>(y + 1, x);
if (down[0] != 0 || down[1] != 0 || down[2] != 0) { //不是黑色判断
tempSet.insert(val);
que.push(val);
}
}
val = (y << 16) + x - 1; //left
if (tempSet.find(val) == tempSet.end() && x - 1 >= 0) {
auto& left = m_image.at<Vec3b>(y, x - 1);
if (left[0] != 0 || left[1] != 0 || left[2] != 0) { //不是黑色判断
tempSet.insert(val);
que.push(val);
}
}
val = ((y - 1) << 16) | x; //up
if (tempSet.find(val) == tempSet.end() && y - 1 >= 0) {
auto& up = m_image.at<Vec3b>(y - 1, x);
if (up[0] != 0 || up[1] != 0 || up[2] != 0) { //不是黑色判断
tempSet.insert(val);
que.push(val);
}
}
if (tempSet.size() > 400) break;
}
if (que.empty()) //判断是孤岛噪点
return true;
return false;
}
void MicroImage::SquareRemove() {
for (int z = 50; z >= 5; z -= 5) { //步长
int x = 0;
int y = 0;
while (x < m_width && y < m_height) {
int allNum = 0, okNum = 0;
vector<Point> vec;
for (int i = y; i < m_height && i < y + z; ++i) { //计算边长z的方形判断是否要变黑
for (int j = x; j < m_width && j < x + z; ++j) {
auto pc = m_image.at<Vec3b>(i, j);
int val = (pc[2] << 16) | (pc[1] << 8) | pc[0];
if ((pc[0] == 0 && pc[1] == 0 && pc[2] == 0) || m_RgbSet.find(val) != m_RgbSet.end()) { //判断是黑色或者背景色
++okNum;
}
++allNum;
vec.emplace_back(Point{ i,j });
}
}
if (okNum * 1.0f / allNum >= 0.86f) { //背景像素占85%,全部置为黑色
for (auto& it : vec) {
auto& pc = m_image.at<Vec3b>(it.x, it.y);
pc[2] = 0;
pc[1] = 0;
pc[0] = 0;
}
}
if (x + z < m_width) x += z;
else {
y += z; x = 0;
}
}
}
//if (g_debug) {
// cv::imshow("result", m_image);
// cv::waitKey(0);
//}
}
bool MicroImage::IsBlack(int y, int x) {
if (x < 0 || x >= m_width || y < 0 || y >= m_height) return true;
auto pc = m_image.at<Vec3b>(y, x);
if ((pc[0] != 0 || pc[1] != 0 || pc[2] != 0)) { //非黑色
return false;
}
return true;
}
void MicroImage::IsLandRemove() {
set<int> tempSet;
set<int> candiSet; //可能是孤岛噪点的位置
for (int y = 0; y < m_height; ++y) {
candiSet.clear();
int preLastBlack = -1;
for (int x = 0; x <= m_width; ++x) {
if (IsBlack(y, x) && !IsBlack(y, x - 1) && x - 1 - preLastBlack <= 40) {
candiSet.insert(x);
}
if (!IsBlack(y, x) && IsBlack(y, x - 1)) {
preLastBlack = x - 1;
}
}
for (auto x = candiSet.begin(); x != candiSet.end(); ++x) {
tempSet.clear();
if (IslandCheck(y, *x, tempSet)) { //是孤岛噪点
for (auto start = tempSet.begin(); start != tempSet.end(); ++start) {
Vec3b& pixel = m_image.at<Vec3b>(((*start) >> 16) & 0xffff, (*start) & 0xffff);
pixel[2] = 0;
pixel[1] = 0;
pixel[0] = 0;
}
}
}
}
//if (g_debug) {
// cv::imshow("result", m_image);
// cv::waitKey(0);
//}
}
void MicroImage::CalBorderCrop(int& dx, int& dy) {
//中心点发出一条线,向边缘平推
int centerx = m_width / 2, centery = m_height / 2;
int bNum = 0;
int lineLen = 250;
int j = centery;
int upDown = 0, upUp = 0;
while (j >= 0) {
bNum = 0;
for (int i = centerx - lineLen / 2; i < centerx + lineLen / 2; ++i) {
auto pixel = m_image.at<Vec3b>(j, i);
if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) { //判断是黑色
++bNum;
}
}
if (bNum * 1.f / lineLen <= 0.3f) { //算边界
if (upDown == 0) upDown = j; //矩形的下边
else {
upUp = j; //矩形的上边
}
}
else {
if (upDown && upUp && bNum * 1.f >= lineLen * 0.95f) break;
}
if (bNum == lineLen) j -= 3;
else --j;
}
DrawRedLine(lineLen, centerx, upDown);
DrawRedLine(lineLen, centerx, upUp);
printf("upDown:%d,upUp:%d\n", upDown, upUp);
j = centery;
int downDown = 0, downUp = 0;
while (j < m_height) {
bNum = 0;
for (int i = centerx - lineLen / 2; i < centerx + lineLen / 2; ++i) {
auto pixel = m_image.at<Vec3b>(j, i);
if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) { //判断是黑色
++bNum;
}
}
if (bNum * 1.f / lineLen <= 0.3f) { //算边界
if (downUp == 0) downUp = j; //矩形的下边
else {
downDown = j; //矩形的上边
}
}
else {
if (downDown && downUp && bNum * 1.f >= lineLen * 0.95f) break;
}
if (bNum == lineLen) j += 3;
else ++j;
}
DrawRedLine(lineLen, centerx, downDown);
DrawRedLine(lineLen, centerx, downUp);
printf("downDown:%d,downUp:%d\n", downDown, downUp);
int i = centerx;
int leftLeft = 0, leftRight = 0;
while (i >= 0) {
bNum = 0;
for (int y = centery - lineLen / 2; y < centery + lineLen / 2; ++y) {
auto pixel = m_image.at<Vec3b>(y, i);
if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) { //判断是黑色
++bNum;
}
}
if (bNum * 1.f / lineLen <= 0.3f) { //算边界
if (leftRight == 0) leftRight = i; //矩形的下边
else {
leftLeft = i; //矩形的上边
}
}
else {
if (leftRight && leftLeft && bNum * 1.f >= lineLen * 0.95f) break;
}
if (bNum == lineLen) i -= 3;
else --i;
}
DrawRedLine(lineLen, leftRight, centery);
DrawRedLine(lineLen, leftLeft, centery);
printf("leftLeft:%d,leftRight:%d\n", leftLeft, leftRight);
i = centerx;
int rightLeft = 0, rightRight = 0;
while (i < m_width) {
bNum = 0;
for (int y = centery - lineLen / 2; y < centery + lineLen / 2; ++y) {
auto pixel = m_image.at<Vec3b>(y, i);
if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) { //判断是黑色
++bNum;
}
}
if (bNum * 1.f / lineLen <= 0.3f) { //算边界
if (rightLeft == 0) rightLeft = i; //矩形的下边
else {
rightRight = i; //矩形的上边
}
}
else {
if (rightLeft && rightRight && bNum * 1.f >= lineLen * 0.95f) break;
}
if (bNum == lineLen) i += 3;
else ++i;
}
DrawRedLine(lineLen, rightLeft, centery);
DrawRedLine(lineLen, rightRight, centery);
printf("rightLeft:%d,rightRight:%d\n", rightLeft, rightRight);
CropByBorder(upDown + 1, downUp - 1, 0, leftLeft - 1);
CropByBorder(upDown + 1, downUp - 1, leftRight + 1, rightLeft - 1);
CropByBorder(upDown + 1, downUp - 1, rightRight + 1, m_width - 1);
CropByBorder(0, upUp - 1, 0, leftLeft - 1);
CropByBorder(0, upUp - 1, leftRight + 1, rightLeft - 1);
CropByBorder(0, upUp - 1, rightRight + 1, m_width - 1);
CropByBorder(downDown + 1, m_height - 1, 0, leftLeft - 1);
CropByBorder(downDown + 1, m_height - 1, leftRight + 1, rightLeft - 1);
CropByBorder(downDown + 1, m_height - 1, rightRight + 1, m_width - 1);
//if (g_debug) {
// cv::imshow("result", m_image);
// cv::waitKey(0);
//}
//cv::imwrite("11.png", m_image);
dy = downUp - upUp;
dx = rightLeft - leftLeft;
}
void MicroImage::CalDistance() {
m_image.release();
m_image = cv::Mat(m_height, m_width, CV_8UC3, m_OriginData).clone();
//cv::imwrite(m_ImgName, m_image);
//StandardLineDetect(); //标准线段检测 先固定值
StatisticsBGColor();
SquareRemove();
//#if 0
// IsLandRemove();
// Binarization();
//
//#endif
int dx = 0, dy = 0;
CalBorderCrop(dx, dy);
m_knowLength = 148; //先固定
m_DistanceY = 0.2 * dy / m_knowLength;
m_DistanceX = 0.2 * dx / m_knowLength;
m_CalData = new uchar[m_TotalSize]{0};
memcpy_s(m_CalData, m_TotalSize,m_image.data,m_TotalSize);
m_image.release();
}
void MicroImage::StandardLineDetect() {
int cropH = 250; //裁剪高
int cropW = 300; //裁剪宽
Rect cropRect(0, m_height - cropH, cropW, cropH); // 定义感兴趣的区域的矩形框
cropRect &= Rect(0, 0, m_width, m_height); // 检查ROI是否在图像范围内
Mat cropMat = m_image(cropRect);
//if (g_isDebug) {
// imshow("Mask", cropMat);
// cv::waitKey(0);
//}
cv::Mat m_HsvMat;
cv::cvtColor(cropMat, m_HsvMat, COLOR_BGR2HSV);
// 定义黑色的HSV范围
Scalar lower_black = Scalar(0, 0, 0);
Scalar upper_black = Scalar(180, 255, 60); // 对于V的区域进行了确定
Mat mask;
inRange(m_HsvMat, lower_black, upper_black, mask);
//if (g_isDebug) {
// cv::imshow("Mask", mask);
// cv::waitKey(0);
//}
int whiteThreshold = 250; //定义白色的灰度阈值 根据需要调整阈值
int width = mask.cols, height = mask.rows;
int whiteW = 0;
for (int y = 0; y < height; ++y) { //先行后列
int whiteCount = 0;
for (int x = 0; x < width; ++x) {
if (x > 0 && mask.at<uchar>(y, x) >= whiteThreshold && mask.at<uchar>(y, x - 1) >= whiteThreshold) { // 判断是否是白色像素
++whiteCount; // 这里可以根据需求处理白色像素,例如计数、修改像素值等
}
else whiteCount = 0;
whiteW = max(whiteCount + 1, whiteW); //要加上第一个像素点
}
}
// 遍历图像的每个像素
int whiteH = 0;
for (int x = 0; x < width; ++x) {
int whiteCount = 0;
for (int y = 0; y < height; ++y) {
if (y > 0 && mask.at<uchar>(y, x) >= whiteThreshold && mask.at<uchar>(y - 1, x) >= whiteThreshold) { // 判断是否是白色像素
++whiteCount; // 这里可以根据需求处理白色像素,例如计数、修改像素值等
}
else whiteCount = 0;
whiteH = max(whiteCount + 1, whiteH);
}
}
printf("whiteW:%d,whiteH:%d\n", whiteW, whiteH);
m_knowLength = (int)(whiteW + whiteH) / 2;
return;
}