#include "stdafx.h" #include "type.h" #include //#include "CString.h" #include #include "Cbmp.h" #include "CImgInfo.h" #include "Craw.h" #include "chist.h" #include "ccubspln.h" #include "cautohist.h" //////////////////////////////////////////////////////////////////////////////// // Public: Construction // ============================================================= // Description: // CAutoHist::CAutoHist() // ============================================================= { Empty(); } // ============================================================= // Description: // CAutoHist::~CAutoHist() // ============================================================= { Empty(); } // ============================================================= // Description: Empties the object // void CAutoHist::Empty() // ============================================================= { Chist::Empty(); for ( int i = 0; i < QUAD; i ++ ) { m_iMin[i] = 0; m_iMax[i] = 0; m_iQtr[i] = 0; m_iMid[i] = 0; m_iThrQtr[i] = 0; } m_raw1 = NULL; m_iChan = -1; } // ============================================================= // Description: Is object empty? // // Return: true if is empty // false if not empty bool CAutoHist::IsEmpty() // ============================================================= { if ( m_raw1 ) return false; return true; } //////////////////////////////////////////////////////////////////////////////// // Public: Initialization // ============================================================= // Description: Load an image in // ** Treat RGB image special... as if it is a CMYK // because I have created a cummulative plane in // Chist object just for the gray scale information // int CAutoHist::Load (CRaw *raw1) // [in] raw // ============================================================= { long lCount; int i, spp; if ( ( !raw1 ) || ( ( raw1->m_info.iBpp != 8 ) && ( raw1->m_info.iBpp != 24 ) && ( raw1->m_info.iBpp != 32 ) ) ) { m_sRc = "Load() Invalid input\n"; return ( m_iRc = INVALID_INPUT ); } spp = raw1->m_info.iBpp/8; m_raw1 = raw1; if ( Chist::Load ( raw1 ) != OKAY ) return m_iRc; if ( Create () != OKAY ) return m_iRc; for ( i = 0; i < spp; i ++ ) { if ( GetPercent ( 1, lCount, m_iMin[i], i ) != OKAY ) return m_iRc; if ( GetPercent ( 99, lCount, m_iMax[i], i ) != OKAY ) return m_iRc; if ( GetPercent ( 25, lCount, m_iQtr[i], i ) != OKAY ) return m_iRc; if ( GetPercent ( 50, lCount, m_iMid[i], i ) != OKAY ) return m_iRc; if ( GetPercent ( 75, lCount, m_iThrQtr[i], i ) != OKAY ) return m_iRc; } if ( spp == RGB ) { if ( GetPercent ( 1*RGB, lCount, m_iMin[RGB], i ) != OKAY ) return m_iRc; if ( GetPercent ( 99*RGB, lCount, m_iMax[RGB], i ) != OKAY ) return m_iRc; if ( GetPercent ( 25*RGB, lCount, m_iQtr[RGB], i ) != OKAY ) return m_iRc; if ( GetPercent ( 50*RGB, lCount, m_iMid[RGB], i ) != OKAY ) return m_iRc; if ( GetPercent ( 75*RGB, lCount, m_iThrQtr[RGB], i ) != OKAY ) return m_iRc; } return true; } // ============================================================= // Description: Get image statistics // int CAutoHist::GetStat (int &iMin // [out] min density point , int &iShdw // [out] shadow density point , int &iMid // [out] midtone density point , int &iHgh // [out] highlight density point , int &iMax // [out] max density point , int iChannel ) // [in] image plane // ============================================================= { if ( ( iChannel < 0 ) || ( iChannel > 3 ) ) { m_sRc = "GetStat() Invalid Channel\n"; return ( m_iRc = INVALID_INPUT ); } iMin = m_iMin[iChannel]; iShdw = m_iQtr[iChannel]; iMid = m_iMid[iChannel]; iHgh = m_iThrQtr[iChannel]; iMax = m_iMax[iChannel]; return true; } //////////////////////////////////////////////////////////////////////////////// // Public: Action // // Images must be in Raw format of 8bpp gray or 24, 32 bpp color // ============================================================= // Description: Equalize image // int CAutoHist::Equalize (int iChannel) // [in] image plane // ============================================================= { int i = iChannel; return ( Manual ( m_iMin[i], m_iQtr[i], m_iMid[i], m_iThrQtr[i], m_iMax[i], i ) ); } // ============================================================= // Description: AdjMinMax, stretch the dynamic range from // min & max to 0 -> 255. // int CAutoHist::AdjMinMax ( int iChannel) // [in] image plane // ============================================================= { return AdjMinMax ( m_iMin[iChannel], m_iMax[iChannel], iChannel ); } // ============================================================= // Description: AdjMinMax, stretch the dynamic range from // min & max to 0 -> 255. // int CAutoHist::AdjMinMax ( int iMin , int iMax , int iChannel) // [in] image plane // ============================================================= { uchar r, g, b; if ( IsEmpty() ) { m_sRc = "Equalize() Load image first\n"; return ( m_iRc = MISSING_IMAGE ); } if ( iChannel > CHAN_GRAY ) { m_sRc = "AdjMinMax() Invalid input\n"; return (m_iRc = INVALID_INPUT); } for ( long y = 0; y < m_raw1->m_info.lLength; y ++ ) { for ( long x = 0; x < m_raw1->m_info.lWidth; x ++ ) { if ( m_raw1->GetPixel(x, y, r, g, b ) != OKAY ) { m_sRc = m_raw1->m_sRc; return (m_iRc = m_raw1->m_iRc); } switch ( iChannel ) { case CHAN_RED: r = (uchar)( ((float)r-(float)m_iMin[CHAN_RED])*(float)(iMax-iMin)/(float)(m_iMax[CHAN_RED]-m_iMin[CHAN_RED])+iMin ); break; case CHAN_GRN: g = (uchar)( ((float)g-(float)m_iMin[CHAN_GRN])*(float)(iMax-iMin)/(float)(m_iMax[CHAN_GRN]-m_iMin[CHAN_GRN])+iMin ); break; case CHAN_BLU: b = (uchar)( ((float)b-(float)m_iMin[CHAN_BLU])*(float)(iMax-iMin)/(float)(m_iMax[CHAN_BLU]-m_iMin[CHAN_BLU])+iMin ); break; case CHAN_GRAY: r = (uchar)( ((float)r-(float)m_iMin[CHAN_RED])*(float)(iMax-iMin)/(float)(m_iMax[CHAN_RED]-m_iMin[CHAN_RED])+iMin ); g = (uchar)( ((float)g-(float)m_iMin[CHAN_GRN])*(float)(iMax-iMin)/(float)(m_iMax[CHAN_GRN]-m_iMin[CHAN_GRN])+iMin ); b = (uchar)( ((float)b-(float)m_iMin[CHAN_BLU])*(float)(iMax-iMin)/(float)(m_iMax[CHAN_BLU]-m_iMin[CHAN_BLU])+iMin ); break; }; if ( m_raw1->PutPixel(x, y, r, g, b ) != OKAY ) { m_sRc = m_raw1->m_sRc; return (m_iRc = m_raw1->m_iRc); } } } return true; } // ============================================================= // Description: Equalize image // Need to use cublic spline to create a smooth // transition between each quater point. // // ** ( try linear for the time being ) // int CAutoHist::Manual( int iMin // [in] new min density point , int iQtr // [in] new shadow density point , int iMid // [in] new midtone density point , int iThrQtr // [in] new highlight density point , int iMax // [in] new max density point , int iChannel) // [in] image plane // ============================================================= { uchar r, g, b; double p; CcubSpln spln; double X[5], Y[5]; int cnt = 5; if ( IsEmpty() ) { m_sRc = "Equalize() Load image first\n"; return ( m_iRc = MISSING_IMAGE ); } if ( iChannel > CHAN_GRAY ) { m_sRc = "AdjMinMax() Invalid input\n"; return (m_iRc = INVALID_INPUT); } // built list X[0] = m_iMin[iChannel]; Y[0] = iMin; X[1] = m_iQtr[iChannel]; Y[1] = iQtr; X[2] = m_iMid[iChannel]; Y[2] = iMid; X[3] = m_iThrQtr[iChannel]; Y[3] = iThrQtr; X[4] = m_iMax[iChannel]; Y[4] = iMax; if ( BuiltList ( X, Y, cnt ) != OKAY ) return m_iRc; if ( cnt == 2 ) return ( AdjMinMax ( iMin, iMax, iChannel ) ); if ( spln.Create ( X, Y, cnt, CCUBSPLN_CUBIC ) != OKAY ) { m_sRc = spln.m_sRc; return ( m_iRc = spln.m_iRc ); } for ( long y = 0; y < m_raw1->m_info.lLength; y ++ ) { for ( long x = 0; x < m_raw1->m_info.lWidth; x ++ ) { if ( m_raw1->GetPixel(x, y, r, g, b ) != OKAY ) { m_sRc = m_raw1->m_sRc; return (m_iRc = m_raw1->m_iRc); } switch ( iChannel ) { case CHAN_RED: p = r; if ( spln.GetPoint ( p, p ) != OKAY ) { m_sRc = spln.m_sRc; return (m_iRc = spln.m_iRc); } r = (uchar)p; break; case CHAN_GRN: p = g; if ( spln.GetPoint ( p, p ) != OKAY ) { m_sRc = spln.m_sRc; return (m_iRc = spln.m_iRc); } g = (uchar)p; break; case CHAN_BLU: p = b; if ( spln.GetPoint ( p, p ) != OKAY ) { m_sRc = spln.m_sRc; return (m_iRc = spln.m_iRc); } b = (uchar)p; break; case CHAN_GRAY: p = r; if ( spln.GetPoint ( p, p ) != OKAY ) { m_sRc = spln.m_sRc; return (m_iRc = spln.m_iRc); } r = (uchar)p; p = g; if ( spln.GetPoint ( p, p ) != OKAY ) { m_sRc = spln.m_sRc; return (m_iRc = spln.m_iRc); } g = (uchar)p; p = b; if ( spln.GetPoint ( p, p ) != OKAY ) { m_sRc = spln.m_sRc; return (m_iRc = spln.m_iRc); } b = (uchar)p; break; }; if ( m_raw1->PutPixel(x, y, r, g, b ) != OKAY ) { m_sRc = m_raw1->m_sRc; return (m_iRc = m_raw1->m_iRc); } } } return true; } //////////////////////////////////////////////////////////////////////////////// // Private - Built list // ============================================================= // Description: Built a list that has ascending order of Xs. // If any Xi+1 == Xi, omit it from list... // If all Xs are the same, return error // // Return true if success // (-) if errored int CAutoHist::BuiltList ( double X[] , double Y[] , int &cnt ) // ============================================================= { int OldCnt = cnt; // The only non-working situation if ( X[0] == X[cnt-1] ) { m_sRc = "BuiltList() all entries are the same\n"; return ( m_iRc = INVALID_INPUT ); } // Determine the real list length for ( int j = 0; j < OldCnt-1; j ++ ) { if ( Y[j+1] < Y[j] ) { m_sRc = "BuiltList() Invalid Entry \n"; return ( m_iRc = INVALID_INPUT ); } if ( X[j+1] <= X[j] ) cnt --; } // 0, 1/4, 1/2, 3/4, 255 // 0 --- a --- b --- c --- 255 switch ( cnt ) { case 2: X[1] = X[OldCnt-1]; // 0-255 Y[1] = Y[OldCnt-1]; return true; case 3: if ( X[2] >= X[3] ) // 0-a-255 { X[2] = X[4]; Y[2] = Y[4]; } else if ( X[1] >= X[2] ) // 0-c-255 { X[1] = X[3]; Y[1] = Y[3]; X[2] = X[4]; Y[2] = Y[4]; } else // 0-b-255 { X[1] = X[2]; Y[1] = Y[2]; X[2] = X[4]; Y[2] = Y[4]; } break; case 4: if ( X[3] >= X[4] ) // 0-a-b-255 { X[3] = X[4]; Y[3] = Y[4]; } else // 0-b-c-255 { for ( int k = 1; k < 4; k ++ ) { X[k] = X[k+1]; Y[k] = Y[k+1]; } } break; case 5: return true; }; return true; }