diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 4dd6b78..e9a2503 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -8,6 +8,7 @@ set(plaYUver_Lib_SRCS # Frame PlaYUVerFrame.cpp PlaYUVerFramePixelFormats.cpp + PlaYUVerFrameStats.cpp # Stream PlaYUVerStream.cpp ) diff --git a/lib/PlaYUVerFrame.cpp b/lib/PlaYUVerFrame.cpp index 3a9d311..b2e1ff4 100644 --- a/lib/PlaYUVerFrame.cpp +++ b/lib/PlaYUVerFrame.cpp @@ -110,6 +110,7 @@ Void PlaYUVerFrame::init( UInt width, UInt height, Int pel_format ) } getMem1D( &m_pcRGB32, m_uiHeight * m_uiWidth * 4 ); + xSetupStatistics( this ); openPixfc(); } diff --git a/lib/PlaYUVerFrame.h b/lib/PlaYUVerFrame.h index 6c2b157..f8c50fb 100644 --- a/lib/PlaYUVerFrame.h +++ b/lib/PlaYUVerFrame.h @@ -25,18 +25,19 @@ #ifndef __PLAYUVERFRAME_H__ #define __PLAYUVERFRAME_H__ -#include "PlaYUVerDefs.h" #include #include #include #include +#include "PlaYUVerDefs.h" +#include "PlaYUVerFrameStats.h" class PixFcSSE; namespace plaYUVer { -class PlaYUVerFrame +class PlaYUVerFrame : public PlaYUVerFrameStats { public: class Pixel @@ -131,7 +132,6 @@ public: { return m_iPixelFormat; } - Int getBitsChannel() const { return m_iBitsChannels; @@ -157,6 +157,7 @@ public: } Pel*** getPelBufferYUV() { + m_bHasHistogram = false; m_bHasRGBPel = false; return m_pppcInputPel; } diff --git a/lib/PlaYUVerFrameStats.cpp b/lib/PlaYUVerFrameStats.cpp new file mode 100644 index 0000000..fb37bbe --- /dev/null +++ b/lib/PlaYUVerFrameStats.cpp @@ -0,0 +1,429 @@ +/* This file is a part of plaYUVer project + * Copyright (C) 2014 by Luis Lucas (luisfrlucas@gmail.com) + * Joao Carreira (jfmcarreira@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * \file PlaYUVerFrameStats.cpp + * \brief Measure PlaYUVerFrame statistics + * Based on the work of Ricardo N. Rocha Sardo in SCode project + */ + +#include +#include +#include + +#include "PlaYUVerFrameStats.h" +#include "PlaYUVerFrame.h" +#include "LibMemory.h" + +namespace plaYUVer +{ + +PlaYUVerFrameStats::PlaYUVerFrameStats() : + m_pdHistogram( NULL ), + m_bHasHistogram( false ) +{ +} + +PlaYUVerFrameStats::~PlaYUVerFrameStats() +{ + if( m_pdHistogram ) + delete[] m_pdHistogram; + +} + +// statistics +Void PlaYUVerFrameStats::xSetupStatistics( const PlaYUVerFrame *pcFrame, UInt options ) +{ + m_pppImageData = pcFrame->getPelBufferYUV(); + m_uiImageWidth = pcFrame->getWidth(); + m_uiImageHeight = pcFrame->getHeight(); + m_uiChannels = pcFrame->getNumberChannels(); + + m_uiHistoSegments = ( pcFrame->getBitsChannel() == 16 ) ? 65536 : 256; + imageColorSpace = pcFrame->getColorSpace(); + imageChromaSize = pcFrame->getChromaLength(); + + if( options == CalcLumaWhenRGB ) + calcLuma = true; + else + calcLuma = false; + + if( calcLuma && ( imageColorSpace == PlaYUVerFrame::COLOR_RGB ) ) + histoChannels = m_uiChannels + 1; + else + histoChannels = m_uiChannels; +} + +Void PlaYUVerFrameStats::calcHistogram() +{ + m_bHasHistogram = false; + getMem1D( &( m_pdHistogram ), m_uiHistoSegments * histoChannels ); + + if( !m_pdHistogram ) + { + qWarning( ) << "PlaYUVerFrameStats::calcHistogramValuess: " + "Unable to allocate memory!" + << endl; + + return; + } + + UInt i, j; + + if( imageColorSpace == PlaYUVerFrame::COLOR_YUV ) + { + const Pel *data[3]; + data[LUMA] = m_pppImageData[LUMA][0]; + data[CHROMA_U] = m_pppImageData[CHROMA_U][0]; + data[CHROMA_V] = m_pppImageData[CHROMA_V][0]; + for( i = 0; i < m_uiImageHeight * m_uiImageWidth; i++ ) + { + m_pdHistogram[*( data[LUMA] ) + LUMA * m_uiHistoSegments]++; + data[LUMA] += 1; + } + for( i = 0; ( i < imageChromaSize ) && runningFlag; i++ ) + { + for( j = 1; j < m_uiChannels; j++ ) + { + m_pdHistogram[*( data[j] ) + j * m_uiHistoSegments]++; + } + for( j = 1; j < m_uiChannels; j++ ) + data[j] += 1; + } + } + else + { + const Pel* data[3]; + Pel luma; + data[COLOR_R] = m_pppImageData[COLOR_R][0]; + data[COLOR_G] = m_pppImageData[COLOR_G][0]; + data[COLOR_B] = m_pppImageData[COLOR_B][0]; + + for( i = 0; ( i < m_uiImageHeight * m_uiImageWidth ) && runningFlag; i++ ) + { + for( j = 0; j < m_uiChannels; j++ ) + { + m_pdHistogram[*( data[j] ) + j * m_uiHistoSegments]++; + } + if( calcLuma && ( imageColorSpace == PlaYUVerFrame::COLOR_RGB || imageColorSpace == PlaYUVerFrame::COLOR_ARGB ) ) + { + PlaYUVerFrame::Pixel currPixel( imageColorSpace, *( data[COLOR_R] ), *( data[COLOR_G] ), *( data[COLOR_B] ) ); + luma = PlaYUVerFrame::ConvertPixel( currPixel, PlaYUVerFrame::COLOR_YUV ).Y(); + m_pdHistogram[luma + j * m_uiHistoSegments]++; + } + for( j = 0; j < m_uiChannels; j++ ) + data[j] += 1; + } + } + m_bHasHistogram = true; +} + +Int PlaYUVerFrameStats::getHistogramSegment() +{ + return m_uiHistoSegments; +} + +Int PlaYUVerFrameStats::getRealHistoChannel( Int channel ) +{ + Int histoChannel; + + switch( imageColorSpace ) + { + case PlaYUVerFrame::COLOR_GRAY: + if( channel != LumaChannel ) + { + return -1; + } + histoChannel = 0; + break; + case PlaYUVerFrame::COLOR_RGB: + if( channel != RedChannel && channel != GreenChannel && channel != BlueChannel && ( channel != LumaChannel && calcLuma ) ) + { + return -1; + } + else + { + if( channel == RedChannel ) + histoChannel = 0; + + if( channel == GreenChannel ) + histoChannel = 1; + + if( channel == BlueChannel ) + histoChannel = 2; + + if( channel == LumaChannel ) + histoChannel = 3; + } + break; + case PlaYUVerFrame::COLOR_ARGB: + if( channel != RedChannel && channel != GreenChannel && channel != BlueChannel && channel != AlphaChannel && ( channel != LumaChannel && calcLuma ) ) + { + return -1; + } + else + { + if( channel == AlphaChannel ) + histoChannel = 0; + + if( channel == RedChannel ) + histoChannel = 1; + + if( channel == GreenChannel ) + histoChannel = 2; + + if( channel == BlueChannel ) + histoChannel = 3; + + if( channel == LumaChannel ) + histoChannel = 4; + } + break; + case PlaYUVerFrame::COLOR_YUV: + if( channel != LumaChannel && channel != YChannel && channel != CbChannel && channel != CrChannel ) + { + return -1; + } + else + { + if( channel == YChannel || channel == LumaChannel ) + histoChannel = 0; + + if( channel == CbChannel ) + histoChannel = 1; + + if( channel == CrChannel ) + histoChannel = 2; + } + break; + default: + histoChannel = -1; + } + return histoChannel; +} + +Double PlaYUVerFrameStats::getCount( Int channel, UInt start, UInt end ) +{ + UInt i; + Int histoChannel; + Int indexStart; + Double count = 0.0; + + if( !m_pdHistogram || start < 0 || end > m_uiHistoSegments - 1 || start > end ) + { + return 0.0; + } + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * m_uiHistoSegments; + + for( i = start; i <= end; i++ ) + { + count += m_pdHistogram[indexStart + i]; + } + return count; +} + +Double PlaYUVerFrameStats::getPixels() +{ + if( !m_pdHistogram ) + { + return 0.0; + } + + return ( m_uiImageWidth * m_uiImageHeight ); +} + +Double PlaYUVerFrameStats::getMean( Int channel, UInt start, UInt end ) +{ + Int indexStart; + Int histoChannel; + Double mean = 0.0; + Double count; + + if( !m_pdHistogram || start < 0 || end > m_uiHistoSegments - 1 || start > end ) + { + return 0.0; + } + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * m_uiHistoSegments; + + for( UInt i = start; i <= end; i++ ) + { + mean += i * m_pdHistogram[indexStart + i]; + } + + count = getCount( channel, start, end ); + + if( count > 0.0 ) + { + return mean / count; + } + + return mean; +} + +Int PlaYUVerFrameStats::getMedian( Int channel, UInt start, UInt end ) +{ + Int histoChannel; + Int indexStart; + Double sum = 0.0; + Double count; + + if( !m_pdHistogram || start < 0 || end > m_uiHistoSegments - 1 || start > end ) + { + return 0; + } + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0; + } + + indexStart = histoChannel * m_uiHistoSegments; + + count = getCount( channel, start, end ); + + for( UInt i = start; i <= end; i++ ) + { + sum += m_pdHistogram[indexStart + i]; + if( sum * 2 > count ) + return i; + } + + return 0; +} + +Double PlaYUVerFrameStats::getStdDev( Int channel, UInt start, UInt end ) +{ + Int histoChannel; + Int indexStart; + Double dev = 0.0; + Double count; + Double mean; + + if( !m_pdHistogram || start < 0 || end > m_uiHistoSegments - 1 || start > end ) + { + return 0.0; + } + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * m_uiHistoSegments; + mean = getMean( channel, start, end ); + count = getCount( channel, start, end ); + + if( count == 0.0 ) + count = 1.0; + + /*------------ original + + for ( i = start ; i <= end ; i++ ) + { + dev += ( i - mean ) * ( i - mean ) * m_pdHistogram[indexStart +i]; + } + + return sqrt( dev / count ); + + -----------------------*/ + + for( UInt i = start; i <= end; i++ ) + { + dev += ( i * i ) * m_pdHistogram[indexStart + i]; + } + + return sqrt( ( dev - count * mean * mean ) / ( count - 1 ) ); + +} + +Double PlaYUVerFrameStats::getHistogramValue( Int channel, UInt bin ) +{ + Double value; + Int histoChannel; + Int indexStart; + + if( !m_pdHistogram || bin < 0 || bin > m_uiHistoSegments - 1 ) + return 0.0; + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * m_uiHistoSegments; + + value = m_pdHistogram[indexStart + bin]; + + return value; +} + +Double PlaYUVerFrameStats::getMaximum( Int channel ) +{ + Double max = 0.0; + + Int histoChannel; + Int indexStart; + + if( !m_pdHistogram ) + return 0.0; + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * m_uiHistoSegments; + + for( UInt x = 0; x < m_uiHistoSegments; x++ ) + { + if( m_pdHistogram[indexStart + x] > max ) + { + max = m_pdHistogram[indexStart + x]; + } + } + + return max; +} + +} // NAMESPACE + diff --git a/lib/PlaYUVerFrameStats.h b/lib/PlaYUVerFrameStats.h new file mode 100644 index 0000000..780def7 --- /dev/null +++ b/lib/PlaYUVerFrameStats.h @@ -0,0 +1,120 @@ +/* This file is a part of plaYUVer project + * Copyright (C) 2014 by Luis Lucas (luisfrlucas@gmail.com) + * Joao Carreira (jfmcarreira@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * \file PlaYUVerFrameStats.h + * \brief Measure PlaYUVerFrame statistics + * Based on the work of Ricardo N. Rocha Sardo in SCode project + */ + +#ifndef __PLAYUVERFRAMESTATS_H__ +#define __PLAYUVERFRAMESTATS_H__ + +#include "PlaYUVerDefs.h" + +namespace plaYUVer +{ + +class PlaYUVerFrame; + +class PlaYUVerFrameStats +{ + +public: + + enum StatisticsChannel + { + LumaChannel = 0, + RedChannel = 1, + GreenChannel = 2, + BlueChannel = 3, + AlphaChannel = 4, + YChannel = 1, + CbChannel = 2, + CrChannel = 3 + }; + + enum StatisticsOptions + { + CalcLumaWhenRGB = 1 + }; + +public: + PlaYUVerFrameStats(); + ~PlaYUVerFrameStats(); + + Void calcHistogram(); + + // Methods for to manipulate the histogram data. + Double getCount( Int channel, UInt start, UInt end ); + Double getMean( Int channel, UInt start, UInt end ); + Int getMedian( Int channel, UInt start, UInt end ); + + /** + * @return Number of image pixels + */ + Double getPixels(); + Double getStdDev( Int channel, UInt start, UInt end ); + Double getHistogramValue( Int channel, UInt bin ); + Double getMaximum( Int channel ); + + /** + * @return Number of histogram segments: 256 or 65536 for 8 or 16 bits per + * sample. + */ + Int getHistogramSegment(); + +private: + + /** The histogram data.*/ + Double* m_pdHistogram; + + /** If true, calc the luminosity channel histogram when the image is RGB */ + Bool calcLuma; + + /** If the image is RGB and calcLuma is true, we have 1 more channel */ + Int histoChannels; + + /** Image information.*/ + Pel*** m_pppImageData; + UInt m_uiImageWidth; + UInt m_uiImageHeight; + UInt m_uiChannels; + Int imageColorSpace; + UInt imageChromaSize; + + /** Numbers of histogram segments dependaing of image bytes depth*/ + UInt m_uiHistoSegments; + + /** Used to stop thread during calculations.*/ + Bool runningFlag; + + Int getRealHistoChannel( Int channel ); + +protected: + + Bool m_bHasHistogram; + + Void xSetupStatistics( const PlaYUVerFrame *pcFrame, UInt options = CalcLumaWhenRGB ); + +}; + +} // NAMESPACE + +#endif // __PLAYUVERFRAMESTATS_H__