diff --git a/hgdriver/ImageProcess/ImageApplyDiscardBlank.cpp b/hgdriver/ImageProcess/ImageApplyDiscardBlank.cpp index 0beee71..9a9b745 100644 --- a/hgdriver/ImageProcess/ImageApplyDiscardBlank.cpp +++ b/hgdriver/ImageProcess/ImageApplyDiscardBlank.cpp @@ -1,7 +1,834 @@ #include "ImageApplyDiscardBlank.h" #include "ImageProcess_Public.h" -#include -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define _DEBUG_ + +#define BIGENDIAN 4321 +#define LILENDIAN 1234 + +#if defined(__linux__) +# include +# define ENDIANNESS __BYTE_ORDER +#else +# if defined(__amd64__) || defined(_M_X64) || defined(__i386) || \ + defined(_M_I86) || defined(_M_IX86) || defined(__X86__) || \ + defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || \ + defined(__INTEL__) || defined(__386) +# define ENDIANNESS LILENDIAN +# else +# define ENDIANNESS BIGENDIAN +# endif +#endif + +/* flip the byte order of 16 bits of data */ +inline uint16_t flip16(void* p) { + uint16_t z = *(uint16_t*)(p); + + return (z >> 9) | (z << 8); /* flip b0 and b1 */ +} + +/* flip the byte order of 32 bits of data */ +inline uint32_t flip32(void* p) { + uint32_t z = *(uint32_t*)(p); + + return + ((z >> 24) & 0xFF) | /* b3 to b0 */ + ((z >> 8) & 0xFF00) | /* b2 to b1 */ + ((z << 8) & 0xFF0000) | /* b1 to b2 */ + ((z << 24) & 0xFF000000); /* b0 to b3 */ +} + +/* flip the byte order of 64 bits of data */ +inline uint64_t flip64(void* p) { + uint64_t z = *(uint64_t*)(p); + + return + ((z >> 56) & 0xFFUL) | /* b7 to b0 */ + ((z >> 40) & (0xFFUL << 8)) | /* b6 to b1 */ + ((z >> 24) & (0xFFUL << 16)) | /* b5 to b2 */ + ((z >> 8) & (0xFFUL << 24)) | /* b4 to b3 */ + ((z << 8) & (0xFFUL << 32)) | /* b3 to b4 */ + ((z << 24) & (0xFFUL << 40)) | /* b2 to b5 */ + ((z << 40) & (0xFFUL << 48)) | /* b1 to b6 */ + ((z << 56) & (0xFFUL << 56)); /* b0 to b7 */ +} + +#if ENDIANNESS == BIGENDIAN +# define lil16(p) flip16(p) +# define lil32(p) flip32(p) +# define lil64(p) flip64(p) +# define big16(p) *(uint16_t*)(p) +# define big32(p) *(uint32_t*)(p) +# define big64(p) *(uint64_t*)(p) +#else +# define lil16(p) *(uint16_t*)(p) +# define lil32(p) *(uint32_t*)(p) +# define lil64(p) *(uint64_t*)(p) +# define big16(p) flip16(p) +# define big32(p) flip32(p) +# define big64(p) flip64(p) +#endif + +// read in a file +png::png() +{ +} + +png::~png() { + // no deep shit yet +} + +bool png::read(const char* data, int length, double threshold) +{ + MyFStream file(data, length); + + // magic png header + uchar b_hdr[8]; + file.read((char*)b_hdr, 8); + + if (std::memcmp("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", b_hdr, 8) != 0) + return false; + + uchar IHDR = 0; + for (size_t i = 0; i < 4; i++) + IHDR = file.get(); + char type[4] = { 0 }; + file.read((char*)type, sizeof(type)); + + //file.read((char*)b_hdr, 8); + int width = 0, height = 0; + file.read_reverse((char*)&width, 4); + file.read_reverse((char*)&height, 4); + + uchar depth = file.get(); + uchar colorType = file.get(); + uchar compressionMethod = file.get(); + uchar filterMethod = file.get(); + uchar interlaceMethod = file.get(); + // read chunks + // assuming none are incomplete + std::string str_IDAT = ""; + while (!file.eof() && str_IDAT != "IDAT") + { + char str = file.get(); + if (str == 'I' && str_IDAT.empty()) + str_IDAT = "I"; + else if (str == 'D' && str_IDAT == "I") + str_IDAT = "ID"; + else if (str == 'A' && str_IDAT == "ID") + str_IDAT = "IDA"; + else if (str == 'T' && str_IDAT == "IDA") + str_IDAT = "IDAT"; + else + str_IDAT = ""; + } + + if (str_IDAT != "IDAT") + return false; + + int start_pos = file.tellg(); + char CRC[4] = { 0 }; + while (!file.eof()) + { + char str = file.get(); + if (str == 0x49) + { + file.move(-1); + file.read(CRC, 4); + if (std::memcmp("\x49\x45\x4e\x44", CRC, 4) == 0) + break; + } + } + if (std::memcmp("\x49\x45\x4e\x44", CRC, 4) != 0) + return false; + + int end_pos = file.tellg(); + + double imageSize = width * height; + double fileSize = end_pos - start_pos; + + if (fileSize / imageSize > threshold) + return false; + else + return true; +} + + +#define M_PI 3.14159265358979 + +int RGBValueLimit(double input) { + if (input < 0) return 0; + else if (input > 255) return 255; + // 四舍五入、取整均可 + // return (int)(input); + return round(input); +} +void print(double** originMatrix) { + std::cout << std::endl; + for (int i = 0; i < ROW; i++) { + for (int j = 0; j < COL; j++) { + std::cout << originMatrix[i][j] << " "; + } + std::cout << std::endl; + } + std::cout << std::endl; +} +double** UnZigZag(int* originArray) { + double** table = new double* [ROW]; + for (int i = 0; i < ROW; i++) + table[i] = new double[COL]; + int cur = 0, x = 0, y = 0; + bool flag = true;//true是右上 false是左下 + while (cur < 64) { + table[y][x] = originArray[cur++]; + if (flag) + { + x++; y--; + } + else + { + x--; y++; + } + if (x < 0 || y < 0 || x>7 || y>7) + flag = !flag; + if (x < 0 && y>7) + { + x = 1; y = 7; + } + if (x < 0) + x = 0; + else if (x > 7) + { + x = 7; y += 2; + } + if (y < 0) + y = 0; + else if (y > 7) + { + y = 7; x += 2; + } + } + return table; +} +bool JPEGScan::Init(MyFStream& file, uint16_t len) { + try { + uint8_t count = file.get(); + len--; + while (count--) { + uint8_t componentId = file.get(); + uint8_t table = file.get(); + uint8_t dcId = table >> 4; + uint8_t acId = table & 0x0f; + std::pair info1(dcId, acId); + std::pair> info2(componentId, info1); + componentHuffmanMap.insert(info2); + } + } + catch (...) { + return false; + } + return true; +} +bool JPEGHuffmanCode::Init(MyFStream& file, uint16_t len) { + try { + std::vector temp; + while (len--) { + int info = file.get(); + temp.push_back(info); + } + int curPos = 16, curCode = 0; + for (int i = 0; i < 16; i++) { + int count = temp[i]; + curCode <<= 1; + while (count--) { + uint16_t code = curCode; + uint8_t bit = i + 1; + uint8_t weight = temp[curPos]; + std::pair t1(bit, weight); + std::pair> t2(curCode, t1); + table.insert(t2); + curCode++; + curPos++; + } + } + } + catch (...) { + return false; + } + return true; +} +bool JPEGHuffmanCode::findKey(const uint16_t& code, const uint8_t& bit, iterator& it) +{ + it = table.find(code); + if (it == table.end()) return true; + return it->second.first != bit; +} +bool JPEGQuality::Init(MyFStream& file, uint16_t len) { + try { + int info = file.get(); + precision = info >> 4; + id = info & 0x0f; + len--; + while (len--) { + int t = file.get(); + table.push_back(t); + } + } + catch (...) { + return false; + } + return true; +} +bool JPEGComponent::Init(MyFStream& file, uint16_t len) { + try { + int info1 = file.get(); + int info2 = file.get(); + int info3 = file.get(); + colorId = info1; + h_samp_factor = info2 >> 4; + v_samp_factor = info2 & 0x0f; + qualityId = info3; + } + catch (...) { + return false; + } + return true; +} + +bool JPEGData::readJPEG(const char* data, int length) +{ + m_res = -1; + //std::fstream file(filePath, std::ios::in | std::ios::binary); + MyFStream file(data, length); + if (file.fail()) + return false; + file.seekg(0, MyFStream::End); + pos = file.tellg(); + file.seekg(2, MyFStream::Begin); + dc_huffman.resize(2); + ac_huffman.resize(2); + try + { + //do read data through using other method + uint16_t pLen = 0; + uint16_t pMarker = 0xFF; + uint16_t pType = 0x00; + while (!file.eof()) + { + pMarker = file.get(); + pType = file.get(); + if (pType == EOI) + break; + pLen = file.get(); + pLen = (pLen << 8) + file.get(); + // cout<> 4) & 1) ac_huffman[tableId] = huf; + else dc_huffman[tableId] = huf; + break; + } + //case SOI: + //case EOI: + case SOS: + { + flag = scan.Init(file, pLen - 2); + int count = 3; + // cout<(file.get()).to_string()); + curBitDequeLength = 8; + // cout<= quality.size()) qualityId = 0; + // cout< 16) + return true; +#ifdef _DEBUGOUT_ + //cout< 0) + { + resetInterval--; + if (resetInterval == 0) + { + resetInterval = restart; + curDRI += 1; + curDRI &= 0x7; + //需要在此处读取两字节信息,看是否为重置标识 + file.get(); + if (file.get() == 0xD9) + EOI = true; + curBitPos = curBitDequeLength; + preDCValue[0] = 0; + preDCValue[1] = 0; + preDCValue[2] = 0; + } + } + // cout<<"curMCUCount="<(count_1) / static_cast(count_2); + //std::cout << std::setprecision(4) << m_res << std::endl; + //std::cout << "\nsuccessfully\n"; + } + catch (std::exception ex) + { + std::cout << ex.what(); + return false; + } + return true; +} +RGB** JPEGData::YCbCrToRGB(const int* YUV) +{ + RGB** res = new RGB * [ROW * max_v_samp_factor]; + + int matrixCount = YUV[0] + YUV[1] + YUV[2]; + int crCount = 0, cbCount = 0; + //1=Y, 2=Cb, 3=Cr + //式子 scale*x,scale*y + double cb_h_samp_scale = component[1].h_samp_factor * 1.0 / max_h_samp_factor, + cb_v_samp_scale = component[1].v_samp_factor * 1.0 / max_v_samp_factor, + cr_h_samp_scale = component[2].h_samp_factor * 1.0 / max_h_samp_factor, + cr_v_samp_scale = component[2].v_samp_factor * 1.0 / max_v_samp_factor; + for (int i = 0; i < ROW * max_v_samp_factor; i++) + res[i] = new RGB[COL * max_h_samp_factor]; + //此处直接生成rgb值 + //注意,此处YCbCr的对应关系与采样因子有关 + // cout<> temp(ROW, std::vector(COL, 0)); + for (int i = 0; i < ROW; i++) + { + for (int j = 0; j < COL; j++) + { + double sum = 0; + for (int k = 0; k < COL; k++) + { + sum += DCTAndIDCTArray[i][k] * originMatrix[k][j]; + } + temp[i][j] = sum; + } + } + for (int i = 0; i < ROW; i++) + { + for (int j = 0; j < COL; j++) + { + double sum = 0; + for (int k = 0; k < COL; k++) + { + sum += temp[i][k] * DCTAndIDCTArray[j][k]; + } + originMatrix[i][j] = sum; + } + } +} +void JPEGData::IDCT(double** originMatrix) +{ + //std::cout << originMatrix[0][0] << std::endl; + //原理X=A'*Y*A + std::vector> temp(ROW, std::vector(COL, 0)); + for (int i = 0; i < ROW; i++) + { + for (int j = 0; j < COL; j++) + { + double sum = 0; + for (int k = 0; k < COL; k++) + { + sum += DCTAndIDCTArray[k][i] * originMatrix[k][j]; + } + temp[i][j] = sum; + } + } + for (int i = 0; i < ROW; i++) + { + for (int j = 0; j < COL; j++) + { + double sum = 0; + for (int k = 0; k < COL; k++) + { + sum += temp[i][k] * DCTAndIDCTArray[k][j]; + } + originMatrix[i][j] = sum; + } + } +} +void JPEGData::deQuality(double** originMatrix, int qualityID) +{ + for (int i = 0; i < ROW; i++) + { + for (int j = 0; j < COL; j++) + { + originMatrix[i][j] *= quality[qualityID].table[i * ROW + j]; + } + } +} +void JPEGData::PAndNCorrect(double** originMatrix) +{ + for (int i = 0; i < ROW; i++) + if (i % 2 == 1) + for (int j = 0; j < COL; j++) + originMatrix[i][j] = -originMatrix[i][j]; +} +std::string JPEGData::FlagCkeck(MyFStream& file, int byteInfo) +{ + if (byteInfo == 0xff) + { + uint8_t info = file.get(); + std::string res = std::bitset<8>(0xFF).to_string(); + if (info == 0xD9) + { + EOI = true; return "false"; + } + else if (info == 0x00) return res; + return res + std::bitset<8>(info).to_string(); + } + return std::bitset<8>(byteInfo).to_string(); +} +uint16_t JPEGData::ReadByte(MyFStream& file, int len) +{ + uint16_t res = file.get(); + if (len != 1) + { + res = (res << 8) + (uint8_t)file.get(); + } + return res; +} +uint16_t JPEGData::findHuffmanCodeByBit(MyFStream& file, int& length, int& pos, std::string& deque, int curValue, int& curValLen) +{ + if (pos == length && length >= HUFFMAN_DECODE_DEQUE_CACHE) + {//达到最大缓存 + deque = deque.substr(pos); + int info = file.get(); + std::string res = FlagCkeck(file, info); + std::string str = std::bitset<8>(info).to_string(); + if (res == "false") res = std::bitset<8>(file.get()).to_string(); + deque.append(res); + length = deque.length(); + pos = 0; + } + else if (length == 0 || pos >= length) + { + if (length == 0) { + deque = ""; + pos = 0; + } + int info = file.get(); + std::string res = FlagCkeck(file, info); + std::string str = std::bitset<8>(info).to_string(); + if (res == "false") res = std::bitset<8>(file.get()).to_string(); + deque.append(res); + length += 8; + } + curValue = (curValue << 1) + (uint8_t)(deque.at(pos++) - '0'); + curValLen++; + return curValue; +} #define FX 0.5 #define FY 0.5 @@ -115,23 +942,57 @@ bool CImageApplyDiscardBlank::apply(const cv::Mat& pDib, double threshold, int e bool CImageApplyDiscardBlank::apply(int fileSize, const cv::Size& imageSize, FileType type, double threshold, const char* data) { + JPEGData jpg; + jpg.m_threshold1 = 3; + jpg.m_threshold2 = 700 + threshold; + png png1; + bool res; switch (type) { case JPEG_COLOR: - if (static_cast(fileSize) / static_cast(imageSize.width * imageSize.height) > 0.039) - return true; - break; + res = jpg.readJPEG(data, fileSize); + std::cout << std::setprecision(4) << jpg.m_res << std::endl; + if (res) + return jpg.m_res < 0.0001; + else + return (static_cast(fileSize) / static_cast(imageSize.width * imageSize.height) > 0.036); case JPEG_GRAY: - if (static_cast(fileSize) / static_cast(imageSize.width * imageSize.height) > 0.018) - return true; - break; + return (static_cast(fileSize) / static_cast(imageSize.width * imageSize.height) > 0.018); case PNG_COLOR: break; case PNG_GRAY: break; case PNG_BINARAY: + return png1.read(data, fileSize, 0.025); break; } return false; } + +MyFStream::MyFStream(const char* data, int length) + : m_data(data) + , m_length(length) + , m_pos(0) +{ +} + +void MyFStream::read(char* dst, int len) +{ + if (m_pos + len < m_length) + { + memcpy(dst, m_data + m_pos, len); + m_pos += len; + } +} + +void MyFStream::read_reverse(char* dst, int len) +{ + if (m_pos + len < m_length) + { + for (size_t i = 0; i < len; i++) + dst[i] = m_data[m_pos + len - i - 1]; + + m_pos += len; + } +} diff --git a/hgdriver/ImageProcess/ImageApplyDiscardBlank.h b/hgdriver/ImageProcess/ImageApplyDiscardBlank.h index 69b95fc..8933097 100644 --- a/hgdriver/ImageProcess/ImageApplyDiscardBlank.h +++ b/hgdriver/ImageProcess/ImageApplyDiscardBlank.h @@ -23,7 +23,8 @@ 2023/10/12 v1.6 添加新的空白页识别方案。采用JEPG文件大小判断是否为空白页。 2023/10/20 v1.6.1 优化JPEG文件大小判断空白页 2023/10/30 v1.7 调整JPEG文件大小判断空白页的算法接口 - * 版本号:v1.7 + 2023/11/04 v1.7.1 增加PNG二值化文件大小判断空白页的选项 + * 版本号:v1.7.1 * ==================================================== */ @@ -32,6 +33,233 @@ #define IMAGE_APPLY_DISCARD_BLANK_H #include "ImageApply.h" +#include +#include +#include +#include +#include +#include +#include + +class MyFStream +{ +public: + enum Seekdir + { + Begin, + End + }; + +public: + MyFStream(const char* data, int length); + + bool eof() + { + return m_pos >= m_length; + } + + bool fail() { return (m_data == nullptr) || (m_length <= 0); } + + void read(char* dst, int len); + + void read_reverse(char* dst, int len); + + unsigned char get() + { + if (m_pos >= m_length) + return 0xD9; + + unsigned char d = m_data[m_pos]; + m_pos++; + return d; + } + + void seekg(int offset, Seekdir dir) + { + switch (dir) + { + case MyFStream::Begin: + m_pos = offset; + break; + case MyFStream::End: + m_pos = m_length + offset; + break; + default: + break; + } + } + + int tellg() { return m_pos; } + + void move(int step) { m_pos += step; } + +private: + const char* m_data; + int m_length; + int m_pos; +}; + +class png +{ +public: + png(); + + ~png(); + + bool read(const char* data, int length, double threshold = 0.025); +}; + +#define ROW 8 +#define COL 8 +#define HUFFMAN_DECODE_DEQUE_CACHE 64//单位:位 + // #define _DEBUG_ + // #define _DEBUGOUT_ +#define FREE_VECTOR_LP(vectorName) \ + for(auto item : vectorName){ \ + for(int i=0;i + std::map> componentHuffmanMap; + bool Init(MyFStream& file, uint16_t len); +}; +//APP +class JPEGInfo { +public: + uint16_t version; +}; +//DHT +class JPEGHuffmanCode { +public: + using iterator = std::map>::iterator; + // + std::map> table; + //init huffman table + bool Init(MyFStream& file, uint16_t len); + //find-true not find-false + bool findKey(const uint16_t& code, const uint8_t& bit, iterator& it); +}; +//DQT +//quality table +class JPEGQuality { +public: + uint8_t precision; + uint8_t id; + std::vector table; + bool Init(MyFStream& file, uint16_t len); +}; +//SOF segment +class JPEGComponent { +public: + //1=Y, 2=Cb, 3=Cr, 4=I, 5=Q + uint8_t colorId; + uint8_t h_samp_factor; + uint8_t v_samp_factor; + uint8_t qualityId; + bool Init(MyFStream& file, uint16_t len); +}; +class JPEGData { + int max_h_samp_factor;//行MCU + int max_v_samp_factor;//列MCU + int width; + int height; + int precision; + bool isYUV411 = false; + bool isYUV422 = false; + bool isYUV111 = false; + uint8_t curDRI = 0;//当前重置直流分量标识,这里只取个位方便计算 + uint16_t resetInterval = 0;//单位是MCU + int preDCValue[3] = { 0 }; //用于直流差分矫正 + //量化表 + std::vector quality; + //huffman码表 + std::vector dc_huffman; + std::vector ac_huffman; + //component每个颜色分量 + std::vector component; + JPEGScan scan; + //vector deHuffman; + std::vector ycbcr; + std::vector rgb; + double** DCTAndIDCTArray; + int pos; + bool EOI{ false }; + +public: + double m_threshold1, m_threshold2, m_res; +public: + JPEGData() : + max_h_samp_factor(0), + max_v_samp_factor(0), + width(0), + height(0), + precision(0) { + DCTAndIDCTArray = createDCTAndIDCTArray(ROW); + } + ~JPEGData() { + FREE_LP_2(DCTAndIDCTArray, ROW - 1) + // FREE_LP_2(DCTArray,ROW-1) + // FREE_LP_2(IDCTArray,ROW-1) + FREE_VECTOR_LP(rgb) + } + + bool readJPEG(const char* data, int length); + int getWidth() const { return width; } + int getHeight() const { return height; } + std::vector getRGB() const { return rgb; } + int getMaxHSampFactor() const { return max_h_samp_factor; } + int getMaxVSampFactor() const { return max_v_samp_factor; } + double** createDCTAndIDCTArray(int row); + //double** createIDCTArray(int row); + void DCT(double** originMatrix); + void IDCT(double** originMatrix); +protected: + bool readSOF(MyFStream& file, uint16_t len); + bool readData(MyFStream& file); + bool huffmanDecode(MyFStream& file); + void deQuality(double** originMatrix, int qualityID); + //隔行正负纠正 + void PAndNCorrect(double** originMatrix); + RGB** YCbCrToRGB(const int* YUV); + //标记位检查 是否结束,是否重置直流矫正数值,返回要添加的数值 + std::string FlagCkeck(MyFStream& file, int byteInfo); + uint16_t ReadByte(MyFStream& file, int len); + uint16_t findHuffmanCodeByBit(MyFStream& file, int& length, int& pos, std::string& deque, int curValue, int& curValLen); +}; class GIMGPROC_LIBRARY_API CImageApplyDiscardBlank : public CImageApply { diff --git a/hgdriver/hgdev/hg_scanner.cpp b/hgdriver/hgdev/hg_scanner.cpp index bdb3458..b35f914 100644 --- a/hgdriver/hgdev/hg_scanner.cpp +++ b/hgdriver/hgdev/hg_scanner.cpp @@ -5235,7 +5235,7 @@ void hg_scanner::image_process(std::shared_ptr& buffer, uint32_t id ret = hg_imgproc::color_cast_correction(ImagePrc_pHandle_); (this->*dump_img_)(ImagePrc_pHandle_, "discardBlank"); } - if ((img_conf_.is_autodiscradblank_normal || img_conf_.is_autodiscradblank_vince) && (pid_ != 0x239 && pid_ != 0x439)) + if ((img_conf_.is_autodiscradblank_normal || img_conf_.is_autodiscradblank_vince || img_conf_.detect_size_diascard_blank) && (pid_ != 0x239 && pid_ != 0x439)) { ret = hg_imgproc::discardBlank(ImagePrc_pHandle_); (this->*dump_img_)(ImagePrc_pHandle_, "discardBlank");