手工局部二值化,MFC, BMP
拿知乎上的一张图练练手,光照是斜对角,全局二值化阈值20,一个角太白;改为全局80,另一个角太黑。
就把图分成4x4,16个小方块,用一个阈值矩阵手工搞,虽说是滥竽充数,好歹也弄出来了。
程序段:
void CXDTS1View::OnTestTest1() { // TODO: Add your command handler code here CXDTDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; unsigned char* pData; int DataLen; //pData = (unsigned char*)pDoc->m_Data; DataLen = pDoc->m_DataLen; if (DataLen == 0) return; pData = new unsigned char[DataLen]; memcpy(pData, (void*)pDoc->m_Data, DataLen); //if (DataLen > 10000) DataLen = 10000; BITMAPFILEHEADER hdr; LPBITMAPINFOHEADER lpbi; BITMAPINFOHEADER bmpInfo;//信息头 memcpy(&hdr, pData, 14); memcpy(&bmpInfo, pData + 14, 40); unsigned char* p = NULL; unsigned char* pBmpdata = NULL; unsigned char* pBmpdata2 = NULL; p = (unsigned char*)pData + 14; pBmpdata = (unsigned char*)(pData + hdr.bfOffBits); pBmpdata2 = (unsigned char*)(pData + hdr.bfOffBits); lpbi = (LPBITMAPINFOHEADER)(p); /*PRINT("%d-%d-%d", hdr.bfType, hdr.bfSize, hdr.bfOffBits); PRINT("%d-%d-%d", lpbi->biSize, lpbi->biWidth, lpbi->biHeight); PRINT("%d-%d-%d", bmpInfo.biSize, bmpInfo.biWidth, bmpInfo.biHeight);*/ int bfWidth, bfHeight; int biBitCount; int bytesPerLine; int skip; int imageSize; //DBI 数据信息 bfWidth = lpbi->biWidth; bfHeight = lpbi->biHeight; biBitCount = lpbi->biBitCount; //内存数据映射 bytesPerLine = ((bfWidth * biBitCount + 31) >> 5) << 2;//bfWidth *3+skip skip = 4 - ((bfWidth * biBitCount) >> 3) & 3; imageSize = bytesPerLine * bfHeight; PRINT("BMP W:%d-H:%d-b:%d MEM: BpL:%d-Skip:%d-Size:%d", bfWidth, bfHeight, biBitCount, bytesPerLine, skip, imageSize); CMainFrame* pMainFrame = (CMainFrame*)((CXDTApp*)AfxGetApp())->m_pMainWnd; CPropertiesWnd* pwndProperties = &(pMainFrame->m_wndProperties); CString str = pwndProperties->m_pSize->GetSubItem(0)->FormatProperty(); int door; door = atoi(str.GetBuffer()); int R, G, B, Gray; //door = 80; int tdoor[4][4] = { 50, 60, 70, 80, 40, 50, 60, 70, 30, 40, 50, 60, 20, 30, 40, 50 }; //处理内存数据 for (int i = 0; i < bfHeight; i++) //高度是反的 { for (int j = 0; j < bytesPerLine; j+=3) //宽度 转换为 行(列数) { B=pBmpdata[i * bytesPerLine + j] ; G=pBmpdata[i * bytesPerLine + j+1]; R=pBmpdata[i * bytesPerLine + j+2]; Gray = (R * 19595 + G * 38469 + B * 7472) >> 16; door = tdoor[i * 4 / bfHeight][j * 4 / bytesPerLine]; if (Gray > door) Gray = 255; else Gray = 0; pBmpdata[i * bytesPerLine + j] = Gray; pBmpdata[i * bytesPerLine + j+1] = Gray; pBmpdata[i * bytesPerLine + j+2] = Gray; } } //pDoc->UpdateAllViews(NULL); ShowBMP(pData, DataLen); delete pData; }
2022-07-27
忍不住又把程序改进了一下,做成了滑窗小区域的局部二值化,效果如下:滑窗中的阈值若是取均值的加权,对连续灰度会造成影响,导致空心化。
对于灰度变化的区域,实际上是边缘强化了,滑窗越小,突变越大,越难控制。
程序段如下:
void CXDTS1View::OnTestTest1() { // TODO: Add your command handler code here CXDTDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; unsigned char* pData; unsigned char* pData2; int DataLen; //pData = (unsigned char*)pDoc->m_Data; DataLen = pDoc->m_DataLen; if (DataLen == 0) return; pData = new unsigned char[DataLen]; pData2 = new unsigned char[DataLen]; memcpy(pData, (void*)pDoc->m_Data, DataLen); memcpy(pData2, (void*)pDoc->m_Data, DataLen); //if (DataLen > 10000) DataLen = 10000; BITMAPFILEHEADER hdr; LPBITMAPINFOHEADER lpbi; BITMAPINFOHEADER bmpInfo;//信息头 memcpy(&hdr, pData, 14); memcpy(&bmpInfo, pData + 14, 40); unsigned char* p = NULL; unsigned char* pBmpdata = NULL; unsigned char* pBmpdata2 = NULL; p = (unsigned char*)pData + 14; pBmpdata = (unsigned char*)(pData + hdr.bfOffBits); pBmpdata2 = (unsigned char*)(pData2 + hdr.bfOffBits); lpbi = (LPBITMAPINFOHEADER)(p); /*PRINT("%d-%d-%d", hdr.bfType, hdr.bfSize, hdr.bfOffBits); PRINT("%d-%d-%d", lpbi->biSize, lpbi->biWidth, lpbi->biHeight); PRINT("%d-%d-%d", bmpInfo.biSize, bmpInfo.biWidth, bmpInfo.biHeight);*/ int bfWidth, bfHeight; int biBitCount; int bytesPerLine; int skip; int imageSize; //DBI 数据信息 bfWidth = lpbi->biWidth; bfHeight = lpbi->biHeight; biBitCount = lpbi->biBitCount; //内存数据映射 bytesPerLine = ((bfWidth * biBitCount + 31) >> 5) << 2;//bfWidth *3+skip skip = 4 - ((bfWidth * biBitCount) >> 3) & 3; imageSize = bytesPerLine * bfHeight; PRINT("BMP W:%d-H:%d-b:%d MEM: BpL:%d-Skip:%d-Size:%d", bfWidth, bfHeight, biBitCount, bytesPerLine, skip, imageSize); CMainFrame* pMainFrame = (CMainFrame*)((CXDTApp*)AfxGetApp())->m_pMainWnd; CPropertiesWnd* pwndProperties = &(pMainFrame->m_wndProperties); CString str = pwndProperties->m_pSize->GetSubItem(0)->FormatProperty(); int door; door = atoi(str.GetBuffer()); int R, G, B, Gray; //door = 80; int tdoor[4][4] = { 50, 60, 70, 80, 40, 50, 60, 70, 30, 40, 50, 60, 20, 30, 40, 50 }; //处理内存数据 for (int i = 0; i < bfHeight; i++) //高度是反的 { for (int j = 0; j < bytesPerLine; j+=3) //宽度 转换为 行(列数) { B=pBmpdata[i * bytesPerLine + j] ; G=pBmpdata[i * bytesPerLine + j+1]; R=pBmpdata[i * bytesPerLine + j+2]; Gray = (R * 19595 + G * 38469 + B * 7472) >> 16; door = tdoor[i * 4 / bfHeight][j * 4 / bytesPerLine]; //if (Gray > door) Gray = 255; //else Gray = 0; pBmpdata[i * bytesPerLine + j] = Gray; pBmpdata[i * bytesPerLine + j+1] = Gray; pBmpdata[i * bytesPerLine + j+2] = Gray; } } //处理内存数据 int sum = 0; for (int i = 4; i < bfHeight-4; i++) //高度是反的 { for (int j = 12; j < bytesPerLine-12; j += 3) //宽度 转换为 行(列数) { B = pBmpdata2[i * bytesPerLine + j]; G = pBmpdata2[i * bytesPerLine + j + 1]; R = pBmpdata2[i * bytesPerLine + j + 2]; Gray = (R * 19595 + G * 38469 + B * 7472) >> 16; sum = 0; for (int m = i-4; m < i+4; m++) { for (int n = j - 4; n < j + 4; n++) //2022-7-28 这里有个错误,X轴是3个字节,所以是+-12,步长3, 改正后效果会好一些 { sum += pBmpdata[m * bytesPerLine + n]; } } door = (int)(sum *1.23/ 81); if (Gray > door) Gray = 255; else Gray = 0; pBmpdata2[i * bytesPerLine + j] = Gray; pBmpdata2[i * bytesPerLine + j + 1] = Gray; pBmpdata2[i * bytesPerLine + j + 2] = Gray; } } //pDoc->UpdateAllViews(NULL); ShowBMP(pData2, DataLen); delete pData; }