OTSU 算法学习,二值化,MFC,BMP

学习了一下OTSU算法,写成程序,加深理解。

这个算法是用来计算最优全局阈值的,所以对光照不均问题似乎不适用。按算法的原理,背景灰度有大的梯度时,会被分割成前景和背景。

对背景一致的图片的分割比较好,其实就是图片整体明暗变化时能自动调节阈值。好比白天到黑夜,阈值自动变。

 

 

 

程序如下:

// XBMPView.cpp : implementation of the CXBMPView class
//

#include "stdafx.h"
// SHARED_HANDLERS can be defined in an ATL project implementing preview, thumbnail
// and search filter handlers and allows sharing of document code with that project.
#ifndef SHARED_HANDLERS
#include "XBMP.h"
#endif

#include "XBMPDoc.h"
#include "XBMPView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CXBMPView

IMPLEMENT_DYNCREATE(CXBMPView, CScrollView)

BEGIN_MESSAGE_MAP(CXBMPView, CScrollView)
    // Standard printing commands
    ON_COMMAND(ID_FILE_PRINT, &CScrollView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_DIRECT, &CScrollView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CXBMPView::OnFilePrintPreview)
    ON_WM_CONTEXTMENU()
    ON_WM_RBUTTONUP()
END_MESSAGE_MAP()

// CXBMPView construction/destruction

CXBMPView::CXBMPView()
{
    // TODO: add construction code here

}

CXBMPView::~CXBMPView()
{
}

BOOL CXBMPView::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: Modify the Window class or styles here by modifying
    //  the CREATESTRUCT cs

    return CScrollView::PreCreateWindow(cs);
}

// CXBMPView drawing

void CXBMPView::OnDraw(CDC* /*pDC*/)
{
    CXBMPDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;
    ShowBMP((void*)pDoc->m_Data, (int)pDoc->m_DataLen,0);
    ShowBMPGrayM((void*)pDoc->m_Data, (int)pDoc->m_DataLen);
    
    // TODO: add draw code for native data here
}

void CXBMPView::OnInitialUpdate()
{
    CScrollView::OnInitialUpdate();

    CSize sizeTotal;
    // TODO: calculate the total size of this view
    sizeTotal.cx = sizeTotal.cy = 100;
    SetScrollSizes(MM_TEXT, sizeTotal);
}


// CXBMPView printing


void CXBMPView::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
    AFXPrintPreview(this);
#endif
}

BOOL CXBMPView::OnPreparePrinting(CPrintInfo* pInfo)
{
    // default preparation
    return DoPreparePrinting(pInfo);
}

void CXBMPView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
    // TODO: add extra initialization before printing
}

void CXBMPView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
    // TODO: add cleanup after printing
}

void CXBMPView::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
    ClientToScreen(&point);
    OnContextMenu(this, point);
}

void CXBMPView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
    theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}


// CXBMPView diagnostics

#ifdef _DEBUG
void CXBMPView::AssertValid() const
{
    CScrollView::AssertValid();
}

void CXBMPView::Dump(CDumpContext& dc) const
{
    CScrollView::Dump(dc);
}

CXBMPDoc* CXBMPView::GetDocument() const // non-debug version is inline
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CXBMPDoc)));
    return (CXBMPDoc*)m_pDocument;
}
#endif //_DEBUG


// CXBMPView message handlers


//BMP文件模式
void CXBMPView::ShowBMP(void* Data, int Len, int k)
{


    unsigned char* pData;
    int            DataLen;

    pData = (unsigned char*)Data;
    DataLen = Len;

    if (DataLen == 0) return;
    //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;
    p = (unsigned char*)pData + 14;
    pBmpdata = (unsigned char*)(pData + hdr.bfOffBits);
    lpbi = (LPBITMAPINFOHEADER)(p);

    /*CSize sizeTotal;
    sizeTotal.cx = lpbi->biWidth;
    sizeTotal.cy = lpbi->biHeight;
    SetScrollSizes(MM_TEXT, sizeTotal);*/


    CRect rt;
    GetClientRect(&rt);
    CDC* pDC = GetDC();
    pDC->SetStretchBltMode(COLORONCOLOR);

    
    
    int width = 0;
    int pos1 = 0;
    
    width = rt.Width() / 3;

    if (k == 0)
    {
        pos1 = 0;
    }
    else
    {
        pos1 = width;
    }


    StretchDIBits(pDC->GetSafeHdc(),
        pos1, 0, width, rt.Height(),
        0, 0, lpbi->biWidth, lpbi->biHeight,
        pBmpdata, (BITMAPINFO*)&bmpInfo, DIB_RGB_COLORS, SRCCOPY);

    
}

//BMP文件模式
void CXBMPView::ShowBMPGrayM(void* Data, int Len)
{
    
    unsigned char* pData;
    unsigned char* pData2;
    int            DataLen;

    pData = (unsigned char*)Data;
    DataLen = Len;
    
    if (DataLen == 0) return;

    
    pData2 = new unsigned char[DataLen];
        
    memcpy(pData2, (void*)pData, DataLen); //复制一份

    BITMAPFILEHEADER hdr;
    LPBITMAPINFOHEADER lpbi;
    BITMAPINFOHEADER  bmpInfo;//信息头  

    memcpy(&hdr, pData, 14);
    memcpy(&bmpInfo, pData + 14, 40);


    unsigned char* p = NULL;
    unsigned char* pBmpdata = NULL;
    p = (unsigned char*)pData + 14;
    pBmpdata = (unsigned char*)(pData + hdr.bfOffBits);
    lpbi = (LPBITMAPINFOHEADER)(p);

    /*CSize sizeTotal;
    sizeTotal.cx = lpbi->biWidth;
    sizeTotal.cy = lpbi->biHeight;
    SetScrollSizes(MM_TEXT, sizeTotal);*/

        
    unsigned char* pBmpdata2 = NULL;
    pBmpdata2 = (unsigned char*)(pData2 + hdr.bfOffBits);


    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;

    int R, G, B, Gray;

    int door = 80;

    int tdoor[4][4] =         //这是之前手工分区二值化用的,没有删掉
    {
        50, 60, 70, 80,
        40, 50, 60, 70,
        30, 40, 50, 60,
        20, 30, 40, 50
    };

    int GK[256];    //各灰度值的像素点数
    int GKSum = 0;  //像素点总数
    int GKMax1 = 0;  //峰值
    int GKMax2 = 0;

    for (int i = 0; i < 256; i++) GK[i] = 0;

    //处理内存数据
    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;
            GK[Gray]++;
            GKSum++;

            if (GK[Gray] > GKMax2) GKMax2 = GK[Gray];
            if(GKMax2> GKMax1)GKMax1 = GKMax2;

            //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;

        }
    }

    
    CString str;
    CString str2;

    double pk[256];  //灰度的概率
    for (int i = 0; i < 256; i++)
    {
        pk[i] = (double)GK[i] / (double)GKSum;
    }

    double mg = 0;   //图片的平均灰度值
    for (int i = 0; i < 256; i++)
    {
        mg = mg + pk[i] * i;
    }

    PRINT("mg=%f", mg);

    int th;    //阈值

    double m1[256];  //对应每个阈值的 C1 均值
    double m2[256];  //对应每个阈值的 C2 均值
    double p1[256];  //p1[i]+p2[i] == 1 
    double p2[256];
    for (th = 0; th < 256; th++)
    {
        m1[th] = 0;
        m2[th] = 0;
        p1[th] = 0;
        p2[th] = 0;
        for (int i = 0; i < th; i++)
        {
            m1[th] = m1[th] + pk[i] * i;
            p1[th] = p1[th] + pk[i];
        }
        m1[th] = m1[th] / p1[th];
        for (int i = th; i < 256; i++)
        {
            m2[th] = m2[th] + pk[i] * i;
            p2[th] = p2[th] + pk[i];
        }
        m2[th] = m2[th] / p2[th];
    }

    str = "P1+P2 == 1:";  //验证 P1+P2 == 1
    for (int i = 0; i < 256; i++)
    {
        str2.Format(";%f+%f=%f", p1[i], p2[i],p1[i]+p2[i]);
        str += str2;
    }
    PRINT(str);

    str = "p1*m1+ p2*m2 - mg ==0:";//验证 p1*m1+ p2*m2 == mg
    for (int i = 0; i < 256; i++)
    {
        str2.Format(";%f", p1[i]*m1[i]+ p2[i]*m2[i]- mg);
        str += str2;
    }
    PRINT(str);


    double a[256];//类间方差
    double amax;
    int thmax;
    amax = 0;
    thmax = 0;


    for (int i = 0; i < 256; i++)
    {
        a[i] = 0;
        //a[i]=p1[i] * p2[i] * (m1[i] - m2[i])* (m1[i] - m2[i]);
        a[i] = p1[i] * (m1[i] - mg) * (m1[i] - mg) + p2[i] * (m2[i] - mg) * (m2[i] - mg);
        
        if (a[i] > amax)
        {
            amax = a[i];
            thmax = i;
        }
    }
    
    str = "a[i]:";
    for (int i = 0; i < 256; i++)
    {
        str2.Format(";%d %f", i, a[i]);
        str += str2;
    }
    PRINT(str);

    
    PRINT("th=%d, amax= %f", thmax, amax);
    
    //pDC->Rectangle(t0 + thmax, 200, t0 + thmax + 1, 200 - GK[thmax] * 100 / GKMax1);  //GKMax1!=0
    


    //处理内存数据

    for (int i = 0; i < bfHeight ; i++) //高度是反的
    {
        for (int j = 0; j < bytesPerLine ; 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;

            if (Gray > thmax) Gray = 255;
            else  Gray = 0;

            pBmpdata2[i * bytesPerLine + j] = Gray;
            pBmpdata2[i * bytesPerLine + j + 1] = Gray;
            pBmpdata2[i * bytesPerLine + j + 2] = Gray;

        }
    }

    //在右边显示灰度直方图
    CRect rt;
    GetClientRect(&rt);
    CDC* pDC = GetDC();
    

    int t0 = rt.Width()/3*2 + 10;
    int high = rt.Height() / 3 * 2;

    for (int i = 0; i < 256; i++)
    {
        pDC->Rectangle(t0 + i, high, t0 + i + 1, high - GK[i] * high / GKMax1);  //GKMax1!=0
    }

    PRINT("m1=%d,m2=%d, mg=%d", GKMax1, GKMax2, GKSum);
    pDC->Rectangle(t0 + thmax, high+20, t0 + thmax + 1, 0);  

//在1号位置显示二值化后的图 ShowBMP(pData2, DataLen, 1); delete pData2; }

 

posted @ 2022-07-27 17:04  XGZ21  阅读(102)  评论(0编辑  收藏  举报