优化图像二值化算法

This commit is contained in:
lovelyyoung 2020-06-20 11:17:06 +08:00
parent a3af24ceab
commit 9d4a3a6e8f
4 changed files with 542 additions and 35 deletions

View File

@ -1,4 +1,366 @@
//#include "ImageApplyBWBinaray.h"
//
//CImageApplyBWBinaray::CImageApplyBWBinaray(ThresholdType type, int threshold, int blockSize, int constant)
// : m_threshold(threshold)
// , m_type(type)
// , m_blockSize(blockSize)
// , m_constant(constant)
// , m_table(new uchar[256])
//{
// memset(m_table, 255, 256);
// memset(m_table, 0, static_cast<size_t>(m_threshold));
//}
//
//CImageApplyBWBinaray::CImageApplyBWBinaray()
// : m_threshold(180)
// , m_type(ThresholdType::THRESH_BINARY)
// , m_blockSize(25)
// , m_constant(5)
// , m_table(new uchar[256])
//{
// memset(m_table, 255, 256);
// memset(m_table, 0, static_cast<size_t>(m_threshold));
//}
//
//
//CImageApplyBWBinaray::~CImageApplyBWBinaray(void)
//{
// delete[] m_table;
//}
//
//void CImageApplyBWBinaray::apply(cv::Mat& pDib,int side)
//{
//#ifdef LOG
// FileTools::write_log("imgprc.txt", "enter CImageApplyBWBinaray apply");
//#endif // LOG
// if (pDib.empty())
// {
//#ifdef LOG
// FileTools::write_log("imgprc.txt", "exit CImageApplyBWBinaray apply");
//#endif // LOG
// return;
// }
//
// if (pDib.channels() == 3)
// cv::cvtColor(pDib, pDib, cv::COLOR_BGR2GRAY);
//
// switch (m_type)
// {
// case ThresholdType::THRESH_BINARY:
// //cv::threshold(pDib, pDib, m_threshold, 255, CV_THRESH_BINARY);
// cv::adaptiveThreshold(pDib, pDib, 255.0, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 91, 41);
// break;
// case ThresholdType::THRESH_OTSU:
// cv::threshold(pDib, pDib, m_threshold, 255, CV_THRESH_OTSU);
// break;
// case ThresholdType::ADAPTIVE_GAUSSIAN:
// cv::adaptiveThreshold(pDib, pDib, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, m_blockSize, m_constant);
// break;
// case ThresholdType::ADAPTIVE_MEAN:
// cv::adaptiveThreshold(pDib, pDib, 255, cv::ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, m_blockSize, m_constant);
// break;
// case ThresholdType::ERROR_DIFFUSION:
// errorDiffuse(pDib);
// break;
// default:
// break;
// }
//
//#ifdef LOG
// FileTools::write_log("imgprc.txt", "exit CImageApplyBWBinaray apply");
//#endif // LOG
//}
//
//void CImageApplyBWBinaray::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
//{
// if (mats.empty()) return;
//
// if (!mats[0].empty()) {
// apply(mats[0], 0);
// }
//
//
// if (isTwoSide && mats.size() > 1) {
// if (!mats[1].empty())
// apply(mats[1], 1);
// }
//}
//
//void CImageApplyBWBinaray::errorDiffuse(cv::Mat & image)
//{
// if (image.rows < 3 || image.cols < 3)
// {
// cv::threshold(image, image, m_threshold, 255, CV_THRESH_BINARY);
// return;
// }
//
// cv::Mat dst;
// image.convertTo(dst, CV_16S);
//
// size_t rows = static_cast<size_t>(image.rows) - 1;
// size_t cols = static_cast<size_t>(image.cols) - 1;
//
// short** pixels_dst = new short*[static_cast<size_t>(image.rows)];
// for (int i = 0; i < image.rows; i++)
// pixels_dst[i] = reinterpret_cast<short*>(dst.data + i * static_cast<int>(dst.step));
//
// short error;
// for (size_t y = 0; y < rows; y++)
// for (size_t x = 1; x < cols; x++)
// {
// short dstPix = pixels_dst[y][x];
// if (dstPix >= m_threshold)
// {
// pixels_dst[y][x] = 255;
// error = dstPix - 255;
// }
// else
// {
// pixels_dst[y][x] = 0;
// error = dstPix;
// }
//
// pixels_dst[y][x + 1] += error * 7 / 16;
// pixels_dst[y + 1][x - 1] += error * 3 / 16;
// pixels_dst[y + 1][x] += error * 5 / 16;
// pixels_dst[y + 1][x + 1] += error * 1 / 16;
// }
// image.release();
// dst.convertTo(image, CV_8U);
//
// rows++;
// uchar* ptr = image.data;
// size_t step = image.step;
// size_t offset;
// for (size_t y = 0; y < rows; y++)
// {
// offset = y * step;
// ptr[offset] = m_table[ptr[offset]];
// offset += cols;
// ptr[offset] = m_table[ptr[offset]];
// }
//
// cols++;
// ptr = image.data + step * (rows - 1);
// for (size_t x = 0; x < cols; x++)
// ptr[x] = m_table[ptr[x]];
//
// delete[] pixels_dst;
//}
#include "ImageApplyBWBinaray.h"
#include "ImageProcess_Public.h"
#include "StopWatch.h"
#include "filetools.h"
typedef struct image_info
{
int32_t width;
int32_t height;
uint8_t* data;
}ImageInfo;
void updataIntergralImg(const uint8_t* const imgData, uint32_t* integralImg, const int32_t& width, const int32_t& height)
{
// create the integral image
//积分图像将窗口内像素值和计算时间由二阶转为常量,窗口越大,效果越明显
uint32_t sum = 0;
int32_t i, j;
const uint32_t* img = (uint32_t*)imgData;
uint32_t* interImgTmp = integralImg;
uint32_t tmp = 0;
for (i = 0; i < width; i += 4)
{
tmp = *img;
sum += tmp & 0x000000ff;//unportble
interImgTmp[0] = sum;
interImgTmp++;
sum += (tmp >> 8) & 0x000000ff;
interImgTmp[0] = sum;
interImgTmp++;
sum += (tmp >> 16) & 0x000000ff;
interImgTmp[0] = sum;
interImgTmp++;
sum += (tmp >> 24) & 0x000000ff;
interImgTmp[0] = sum;
interImgTmp++;
img++;
}
for (i = 1; i < height; ++i)
{
sum = 0;
for (j = 0; j < width; j += 4)
{
tmp = *img;
sum += tmp & 0x000000ff;
interImgTmp[0] = sum + interImgTmp[-width];
interImgTmp++;
sum += (tmp >> 8) & 0x000000ff;
interImgTmp[0] = sum + interImgTmp[-width];
interImgTmp++;
sum += (tmp >> 16) & 0x000000ff;
interImgTmp[0] = sum + interImgTmp[-width];
interImgTmp++;
sum += (tmp >> 24) & 0x000000ff;
interImgTmp[0] = sum + interImgTmp[-width];
interImgTmp++;
img++;
}
}
}
void threshold(ImageInfo& image_src, uint8_t thre)
{
uint8_t table[256];
memset(table, 0, thre);
memset(table + thre, 255, 256 - thre);
int32_t size = image_src.width * image_src.height;
uint8_t* ptr = image_src.data;
for (int32_t i = 0; i < size; i++)
{
ptr[i] = table[ptr[i]];
}
}
void adaptivethresh(ImageInfo& image_src, const uint8_t& lowThr, const uint8_t& upThr, const uint8_t& init_threshold)
{
//基本原理参见welner自适应阈值算法修改部分为将四个边界事先计算中心部分单独计算所有魔数为经验值
const uint8_t thresh = __min(init_threshold, 100);
uint8_t S = 17;
unsigned short T = 100 - thresh;
T = (unsigned short)((float)T * 10.24);
unsigned short i, j;
uint32_t sum = 0;
unsigned short count = 0;
int16_t x1, y1, x2, y2;
const uint8_t s2 = S >> 1;
uint8_t* pointer = image_src.data;
//pointer = image_src;
const int32_t width = image_src.width;
const int32_t height = image_src.height;
uint32_t* integralImg = new uint32_t[width * height];//积分图,用于自适应二值算法
updataIntergralImg(image_src.data, integralImg, width, height);
uint8_t t = 0;
ImageInfo img_up_dwon;
img_up_dwon.data = pointer;
img_up_dwon.width = width;
img_up_dwon.height = s2 + 1;
threshold(img_up_dwon, init_threshold);
pointer = image_src.data;
pointer += (height - s2) * width;
img_up_dwon.data = pointer;
img_up_dwon.width = width;
img_up_dwon.height = s2;
threshold(img_up_dwon, init_threshold);
pointer = image_src.data + (s2 + 1) * width;
for (i = 0; i <= s2; ++i)
{
for (j = s2 + 1; j < height - s2; ++j)
{
if (pointer[0] < init_threshold)
{
pointer[0] = 0;
}
else
{
pointer[0] = 255;
}
pointer += width;
}
pointer = image_src.data + (s2 + 1) * width + i + 1;
}
pointer = image_src.data + (s2 + 1) * width + (width - s2);
unsigned short ii = 1;
for (i = width - s2; i < width; ++i)
{
for (j = s2 + 1; j < height - s2; ++j)
{
if (pointer[0] < init_threshold)
{
pointer[0] = 0;
}
else
{
pointer[0] = 255;
}
pointer += width;
}
pointer = image_src.data + (s2 + 1) * width + (width - s2) + ii;
ii++;
}
///center part
unsigned short ww = width - s2 - s2 - 1;
unsigned short hh = height - s2 - s2 - 1;
uint8_t gap = width - ww;
count = (s2 << 1) + 1;
count *= count;
const uint32_t factor = count * 100 / (100 - 50);
uint32_t* plt;
uint32_t* prt;
uint32_t* plb;
uint32_t* prb;
plt = integralImg;
prt = plt + (s2 << 1) + 1;
plb = plt + ((s2 << 1) + 1) * width;
prb = prt + ((s2 << 1) + 1) * width;
pointer = image_src.data + (s2 + 1) * width + s2 + 1;
for (i = 0; i < hh; ++i)
{
for (j = 0; j < ww; j++)
{
if (/*expected_true(*/*pointer >= upThr/*)*/)
{
*pointer = 255;
}
else if (*pointer < lowThr)
{
*pointer = 0;
}
else
{
sum = *prb - *prt - *plb + *plt;
t = *pointer;
if (/*expected_false(*/t * factor <= sum/*)*/)
*pointer = 0;
else
*pointer = 255;
}
plt++;
prt++;
plb++;
prb++;
pointer++;
}
plt += gap;
prt += gap;
plb += gap;
prb += gap;
pointer += width - ww;
}
delete[]integralImg;
}
CImageApplyBWBinaray::CImageApplyBWBinaray(ThresholdType type, int threshold, int blockSize, int constant)
: m_threshold(threshold)
@ -12,7 +374,7 @@ CImageApplyBWBinaray::CImageApplyBWBinaray(ThresholdType type, int threshold, in
}
CImageApplyBWBinaray::CImageApplyBWBinaray()
: m_threshold(180)
: m_threshold(160)
, m_type(ThresholdType::THRESH_BINARY)
, m_blockSize(25)
, m_constant(5)
@ -28,18 +390,10 @@ CImageApplyBWBinaray::~CImageApplyBWBinaray(void)
delete[] m_table;
}
void CImageApplyBWBinaray::apply(cv::Mat& pDib,int side)
void CImageApplyBWBinaray::apply(cv::Mat& pDib, int side)
{
#ifdef LOG
FileTools::write_log("imgprc.txt", "enter CImageApplyBWBinaray apply");
#endif // LOG
if (pDib.empty())
{
#ifdef LOG
FileTools::write_log("imgprc.txt", "exit CImageApplyBWBinaray apply");
#endif // LOG
return;
}
(void)side;
if (pDib.empty()) return;
if (pDib.channels() == 3)
cv::cvtColor(pDib, pDib, cv::COLOR_BGR2GRAY);
@ -47,8 +401,75 @@ void CImageApplyBWBinaray::apply(cv::Mat& pDib,int side)
switch (m_type)
{
case ThresholdType::THRESH_BINARY:
cv::threshold(pDib, pDib, m_threshold, 255, CV_THRESH_BINARY);
{
#if 0
StopWatch sw;
float ksize = 9.0f;
float other = (1.0f - ksize) / 4.0f;
float kernel_data[] = { 0, other, 0, other, ksize, other, 0, other, 0 };
cv::Mat kernel(3, 3, CV_32FC1, kernel_data);
cv::filter2D(pDib, pDib, pDib.depth(), kernel);
FileTools::write_log("E:\\Users\\huago\\Desktop\\2.txt", "filter2D: " + std::to_string(sw.elapsed_ms()));
sw.reset();
cv::adaptiveThreshold(pDib, pDib, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 91, 21);
FileTools::write_log("E:\\Users\\huago\\Desktop\\2.txt", "adaptiveThreshold: " + std::to_string(sw.elapsed_ms()));
//cv::Mat mask = pDib.clone();
//std::vector<std::vector<cv::Point>> contours;
//std::vector<cv::Vec4i> h;
//hg::findContours(mask, contours, h);
//mask.release();
//for (const std::vector<cv::Point>& contour : contours)
// if (contourArea(contour) < 6)
// fillConvexPoly(pDib, contour, cv::Scalar(255));
#endif
//StopWatch sw;
cv::Mat integ;
cv::integral(pDib, integ, CV_32S);
int blockSize = 31;//邻域尺寸
int threshold = 21;
int low = 110;
int up = 220;
int halfSize = blockSize / 2;
int square_block = blockSize * blockSize;
for (int j = halfSize; j < integ.rows - halfSize - 1; j++)
{
uchar* data = pDib.ptr<uchar>(j);
int* idata1 = integ.ptr<int>(j - halfSize);
int* idata2 = integ.ptr<int>(j + halfSize + 1);
for (int i = halfSize; i < integ.cols - halfSize - 1; i++)
{
int sum = (idata2[i + halfSize + 1] - idata2[i - halfSize] - idata1[i + halfSize + 1] + idata1[i - halfSize]) / square_block;
if (data[i] < low) data[i] = 0;
else if (data[i] > up) data[i] = 255;
else
{
if (data[i] < (sum - threshold))
data[i] = 0;
else
data[i] = 255;
}
}
}
cv::threshold(pDib(cv::Rect(0, 0, halfSize, pDib.rows)), pDib(cv::Rect(0, 0, halfSize, pDib.rows)), m_threshold, 255, cv::THRESH_BINARY);
cv::threshold(pDib(cv::Rect(pDib.cols - halfSize, 0, halfSize, pDib.rows)), pDib(cv::Rect(pDib.cols - halfSize, 0, halfSize, pDib.rows)), m_threshold, 255, cv::THRESH_BINARY);
cv::threshold(pDib(cv::Rect(0, 0, pDib.cols, halfSize)), pDib(cv::Rect(0, 0, pDib.cols, halfSize)), m_threshold, 255, cv::THRESH_BINARY);
cv::threshold(pDib(cv::Rect(0, pDib.rows - halfSize, pDib.cols, halfSize)), pDib(cv::Rect(0, pDib.rows - halfSize, pDib.cols, halfSize)), m_threshold, 255, cv::THRESH_BINARY);
cv::Mat mask = pDib.clone();
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> h;
hg::findContours(mask, contours, h);
mask.release();
for (const std::vector<cv::Point>& contour : contours)
if (contourArea(contour) < 6)
fillConvexPoly(pDib, contour, cv::Scalar(255));
//FileTools::write_log("E:\\Users\\huago\\Desktop\\2.txt", "THRESH_BINARY eplsed: " + std::to_string(sw.elapsed_ms()));
//cv::threshold(pDib, pDib, m_threshold, 255, CV_THRESH_OTSU);
break;
}
case ThresholdType::THRESH_OTSU:
cv::threshold(pDib, pDib, m_threshold, 255, CV_THRESH_OTSU);
break;
@ -72,20 +493,18 @@ void CImageApplyBWBinaray::apply(cv::Mat& pDib,int side)
void CImageApplyBWBinaray::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
{
if (mats.empty()) return;
if (!mats[0].empty()) {
apply(mats[0], 0);
}
if (isTwoSide && mats.size() > 1) {
if (!mats[1].empty())
apply(mats[1], 1);
(void)isTwoSide;
int i = 0;
for (cv::Mat& var : mats) {
if (i != 0 && isTwoSide == false)
break;
if (!var.empty())
apply(var, 0);
i++;
}
}
void CImageApplyBWBinaray::errorDiffuse(cv::Mat & image)
void CImageApplyBWBinaray::errorDiffuse(cv::Mat& image)
{
if (image.rows < 3 || image.cols < 3)
{
@ -99,7 +518,7 @@ void CImageApplyBWBinaray::errorDiffuse(cv::Mat & image)
size_t rows = static_cast<size_t>(image.rows) - 1;
size_t cols = static_cast<size_t>(image.cols) - 1;
short** pixels_dst = new short*[static_cast<size_t>(image.rows)];
short** pixels_dst = new short* [static_cast<size_t>(image.rows)];
for (int i = 0; i < image.rows; i++)
pixels_dst[i] = reinterpret_cast<short*>(dst.data + i * static_cast<int>(dst.step));

View File

@ -1,30 +1,112 @@
//#ifndef IMAGE_APPLY_BW_BINARAY_H
//#define IMAGE_APPLY_BW_BINARAY_H
//
//#include "ImageApply.h"
//
//class CImageApplyBWBinaray:public CImageApply
//{
//public:
//
// enum class ThresholdType
// {
// THRESH_BINARY = 0,
// THRESH_OTSU,
//
// ADAPTIVE_GAUSSIAN,
// ADAPTIVE_MEAN,
//
// ERROR_DIFFUSION
// };
//
// CImageApplyBWBinaray(ThresholdType type, int threshold = 180, int blockSize = 25, int constant = 5);
//
// CImageApplyBWBinaray();
//
// virtual ~CImageApplyBWBinaray(void);
//
// virtual void apply(cv::Mat& pDib,int side);
//
// virtual void apply(std::vector<cv::Mat>& mats, bool isTwoSide);
//
// double getThreshold() { return m_threshold; }
//
// ThresholdType getThresholdType() { return m_type; }
//
// int getBlockSize() { return m_blockSize; }
//
// double getConstant() { return m_constant; }
//
// void setThreshold(double value) { m_threshold = value; }
//
// void setThresholdType(ThresholdType type) { m_type = type; }
//
// void setBlockSize(int value) { m_blockSize = value; }
//
// void setConstant(double value) { m_constant = value; }
//
//private:
//
// void errorDiffuse(cv::Mat& image);
//
//private:
// double m_threshold;
//
// ThresholdType m_type;
//
// int m_blockSize;
//
// double m_constant;
//
// uchar* m_table;
//};
//
//#endif //!IMAGE_APPLY_BW_BINARAY_H
//
/*
* ====================================================
*
*
* 2020/4/21
* 2020/4/21
* v1.0
* ====================================================
*/
#ifndef IMAGE_APPLY_BW_BINARAY_H
#define IMAGE_APPLY_BW_BINARAY_H
#include "ImageApply.h"
class CImageApplyBWBinaray:public CImageApply
class CImageApplyBWBinaray :public CImageApply
{
public:
enum class ThresholdType
{
THRESH_BINARY = 0,
THRESH_OTSU,
THRESH_BINARY = 0, //传统二值化
THRESH_OTSU, //大津阈值
ADAPTIVE_GAUSSIAN,
ADAPTIVE_MEAN,
ADAPTIVE_GAUSSIAN, //高斯局部自适应阈值
ADAPTIVE_MEAN, //均值局部自适应阈值
ERROR_DIFFUSION
ERROR_DIFFUSION //错误扩散
};
CImageApplyBWBinaray(ThresholdType type, int threshold = 180, int blockSize = 25, int constant = 5);
/*
* type [in]:
* threshold [in]:THRESH_OTSU时无效
* blockSize [in]:ADAPTIVE_GAUSSIAN和ADAPTIVE_MEAN模式有效
* constant [in]:ADAPTIVE_GAUSSIAN和ADAPTIVE_MEAN模式有效blockSize形成比例关系
*/
CImageApplyBWBinaray(ThresholdType type, int threshold = 120, int blockSize = 25, int constant = 5);
CImageApplyBWBinaray();
virtual ~CImageApplyBWBinaray(void);
virtual void apply(cv::Mat& pDib,int side);
virtual void apply(cv::Mat& pDib, int side);
virtual void apply(std::vector<cv::Mat>& mats, bool isTwoSide);

View File

@ -1,17 +1,21 @@
#include "ImageApplyChannel.h"
//#include "ImageApplyAdjustColors.h"
CImageApplyChannel::CImageApplyChannel()
: m_channel(Invalid)
//, colors(new CImageApplyAdjustColors(0, 30, 1.0))
{
}
CImageApplyChannel::CImageApplyChannel(Channel channel)
: m_channel(channel)
//,colors(new CImageApplyAdjustColors(0, 30, 1.0))
{
}
CImageApplyChannel::~CImageApplyChannel(void)
{
//if (colors != NULL)
//delete colors;
}
void CImageApplyChannel::apply(cv::Mat& pDib,int side)
@ -33,6 +37,7 @@ void CImageApplyChannel::apply(cv::Mat& pDib,int side)
{
case Red:
cv::extractChannel(pDib, dst, 2);
//colors->apply(pDib, side);
break;
case Green:
cv::extractChannel(pDib, dst, 1);

View File

@ -2,7 +2,7 @@
#define IMAGE_APPLY_CHANNEL_H
#include "imageapply.h"
//class CImageApplyAdjustColors;
class CImageApplyChannel : public CImageApply
{
public:
@ -37,6 +37,7 @@ private:
private:
Channel m_channel;
//CImageApplyAdjustColors* colors;
};
#endif // !IMAGE_APPLY_CHANNEL_H