From 7dd02189a1e98a3bebcee3688eba0d4d6c78bbb3 Mon Sep 17 00:00:00 2001 From: Joao Carreira Date: Sat, 5 Apr 2014 11:23:31 +0100 Subject: [PATCH] Add histogram files Histogram does not work, although it has the required functions --- source/app/HistogramWidget.cpp | 1089 ++++++++++++++++++++++++ source/app/HistogramWidget.h | 200 +++++ source/app/PropertiesSidebar.cpp | 453 +++++++++- source/app/PropertiesSidebar.h | 67 +- source/lib/PlaYUVerFrameStatistics.cpp | 578 +++++++++++++ source/lib/PlaYUVerFrameStatistics.h | 135 +++ 6 files changed, 2519 insertions(+), 3 deletions(-) create mode 100755 source/app/HistogramWidget.cpp create mode 100755 source/app/HistogramWidget.h create mode 100755 source/lib/PlaYUVerFrameStatistics.cpp create mode 100755 source/lib/PlaYUVerFrameStatistics.h diff --git a/source/app/HistogramWidget.cpp b/source/app/HistogramWidget.cpp new file mode 100755 index 0000000..a34054d --- /dev/null +++ b/source/app/HistogramWidget.cpp @@ -0,0 +1,1089 @@ +/* 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 HistogramWidget.cpp + * \brief Function to show image histogram + * Based on the work of Ricardo N. Rocha Sardo in SCode project + */ + +//TODO: +// - Optimizar paintEvent. Parece haver codigo redundante !! +#include +#include +#include +#include "HistogramWidget.h" +#include "PlaYUVerFrameStatistics.h" +#include "PlaYUVerFrame.h" + +namespace plaYUVer +{ + +class HistogramWidgetPrivate +{ +public: + + enum RepaintType + { + HistogramNone = 0, // No current histogram values calculation. + HistogramDataLoading, // The image is being loaded + HistogramStarted, // Histogram values calculation started. + HistogramCompleted, // Histogram values calculation completed. + HistogramFailed // Histogram values calculation failed. + }; + + HistogramWidgetPrivate() + { + blinkTimer = 0; + sixteenBits = false; + inSelected = false; + blinkFlag = false; + clearFlag = HistogramNone; + xmin = 0.0; + xmax = 0.0; + range = 255; + guideVisible = false; + inInitialRepaintWait = false; + } + + // Current selection information. + double xmin; + double xminOrg; + double xmax; + int range; + int clearFlag; // Clear drawing zone with message. + + // Image informations + int imageChannels; + Int imageColorSpace; + bool sixteenBits; + + bool guideVisible; // Display color guide. + bool statisticsVisible; // Display tooltip histogram statistics. + bool inSelected; + bool selectMode; // If true, a part of the histogram can be + // selected ! + bool blinkFlag; + bool blinkComputation; // If true, a message will be displayed during + // histogram computation, else nothing (limit + // flicker effect in widget especially for small + // image/computation time). + bool lumaChannel; // Compute luminance channel if true. + bool inInitialRepaintWait; + + QTimer *blinkTimer; + + QColor colorGuide; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Constructors +//////////////////////////////////////////////////////////////////////////////// + +//Constructor without image data +HistogramWidget::HistogramWidget( int width, int height, QWidget *parent ) : + QWidget( parent ) +{ + setAttribute( Qt::WA_DeleteOnClose ); + + d = new HistogramWidgetPrivate; + setupWidget( width, height ); + + m_imageHistogram = 0; + m_selectionHistogram = 0; + + update(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Destructor +//////////////////////////////////////////////////////////////////////////////// +HistogramWidget::~HistogramWidget() +{ + d->blinkTimer->stop(); + + if( m_imageHistogram ) + delete m_imageHistogram; + + if( m_selectionHistogram ) + delete m_selectionHistogram; + + delete d; +} +//////////////////////////////////////////////////////////////////////////////// +// Setup Functions +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::setupWidget( int width, int height, HistogramOptions options ) +{ + m_channelType = LumaHistogram; + m_scaleType = LogScaleHistogram; + m_colorType = RedColor; + m_renderingType = FullImageHistogram; + + setOptions( options ); + + setMouseTracking( true ); + setMinimumSize( width, height ); + + d->blinkTimer = new QTimer( this ); + + connect( d->blinkTimer, SIGNAL( timeout() ), this, SLOT( slotBlinkTimerDone() ) ); +} + +void HistogramWidget::setOptions( HistogramOptions options ) +{ + d->statisticsVisible = ( bool )( options & ShowStatistics ); + d->selectMode = ( bool )( options & SelectMode ); + d->blinkComputation = ( bool )( options & BlinkComputation ); + d->lumaChannel = ( bool )( options & ShowLumaChannel ); +} + +void HistogramWidget::setHistogramGuideByColor( QColor color ) +{ + d->guideVisible = true; + d->colorGuide = color; + update(); +} + +void HistogramWidget::reset( void ) +{ + d->guideVisible = false; + d->clearFlag = HistogramWidgetPrivate::HistogramNone; + // Remove histogram data from memory. + if( m_imageHistogram ) + { + delete m_imageHistogram; + m_imageHistogram = 0; + } + if( m_selectionHistogram ) + { + delete m_selectionHistogram; + m_selectionHistogram = 0; + } + update(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Custom Event Handler +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::customEvent( QEvent *event ) +{ + if( !event ) + { + return; + } + + PlaYUVerFrameStatistics::EventData *ed = ( PlaYUVerFrameStatistics::EventData* )event; + + if( !ed ) + { + return; + } + + if( ed->histogram != m_imageHistogram && ed->histogram != m_selectionHistogram ) + { + return; + } + + if( ed->starting ) + { + setCursor( Qt::WaitCursor ); + d->clearFlag = HistogramWidgetPrivate::HistogramStarted; + if( !d->inInitialRepaintWait ) + { + if( d->clearFlag != HistogramWidgetPrivate::HistogramDataLoading ) + { + // enter initial repaint wait, repaint only after waiting + // a short time so that very fast computation does not create + // flicker. + d->inInitialRepaintWait = true; + d->blinkTimer->start( 100 ); + } + else + { + // after the initial repaint, we can repaint immediately + update(); + d->blinkTimer->start( 200 ); + } + } + } + else + { + if( ed->success ) + { + // Repaint histogram + d->clearFlag = HistogramWidgetPrivate::HistogramCompleted; + d->blinkTimer->stop(); + d->inInitialRepaintWait = false; + unsetCursor(); + + // Send signals to refresh information if necessary. + // The signals may trigger multiple repaints, avoid this, + // we repaint once afterwards. + setUpdatesEnabled( false ); + + notifyValuesChanged(); + emit signalHistogramComputationDone( d->sixteenBits ); + + setUpdatesEnabled( true ); + update(); + } + else + { + d->clearFlag = HistogramWidgetPrivate::HistogramFailed; + d->blinkTimer->stop(); + d->inInitialRepaintWait = false; + update(); + unsetCursor(); + // Remove old histogram data from memory. + if( m_imageHistogram ) + { + delete m_imageHistogram; + m_imageHistogram = 0; + } + if( m_selectionHistogram ) + { + delete m_selectionHistogram; + m_selectionHistogram = 0; + } + emit signalHistogramComputationFailed(); + } + } + + //delete ed; +} +//////////////////////////////////////////////////////////////////////////////// +// Data Loading Functions +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::setDataLoading() +{ + if( d->clearFlag != HistogramWidgetPrivate::HistogramDataLoading ) + { + setCursor( Qt::WaitCursor ); + d->clearFlag = HistogramWidgetPrivate::HistogramDataLoading; + // enter initial repaint wait, repaint only after waiting + // a short time so that very fast computation does not create flicker + d->inInitialRepaintWait = true; + d->blinkTimer->start( 100 ); + update(); + } +} + +void HistogramWidget::setLoadingFailed() +{ + d->clearFlag = HistogramWidgetPrivate::HistogramFailed; + d->blinkTimer->stop(); + d->inInitialRepaintWait = false; + update(); + unsetCursor(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::stopHistogramComputation( void ) +{ + if( m_imageHistogram ) + m_imageHistogram->stopCalcHistogramValues(); + + if( m_selectionHistogram ) + m_selectionHistogram->stopCalcHistogramValues(); + + d->blinkTimer->stop(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Update Data Methods +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::updateData( uchar *imageData, uint imageWidth, uint imageHeight, int bitsPerChannel, Int pixel_format, uchar *selData, uint selWidth, + uint selHeight ) +{ + Int colorSpace = PlaYUVerFrame::isRGBorYUVorGray( pixel_format ); + d->sixteenBits = ( bool )( bitsPerChannel == 16 ); + d->imageColorSpace = colorSpace; + + switch( colorSpace ) + { + case PlaYUVerFrame::COLOR_YUV: + case PlaYUVerFrame::COLOR_GRAY: + d->imageChannels = 1; + break; + case PlaYUVerFrame::COLOR_RGB: + d->imageChannels = 3; + break; + case PlaYUVerFrame::COLOR_ARGB: + d->imageChannels = 4; + break; + default: + d->imageChannels = 1; + } + + // We are deleting the histogram data, + // so we must not use it to draw any more. + d->clearFlag = HistogramWidgetPrivate::HistogramNone; + + // Do not using PlaYUVerFrameStatistics::getHistogramSegment() + // method here because histogram hasn't yet been computed. + d->range = d->sixteenBits ? 65535 : 255; + emit signalMaximumValueChanged( d->range ); + + // Remove old histogram data from memory. + if( m_imageHistogram ) + delete m_imageHistogram; + + if( m_selectionHistogram ) + delete m_selectionHistogram; + + // Calc new histogram data + m_imageHistogram = new PlaYUVerFrameStatistics( imageData, imageWidth, imageHeight, bitsPerChannel, colorSpace, this ); + + if( selData && selWidth && selHeight ) + m_selectionHistogram = new PlaYUVerFrameStatistics( selData, selWidth, selHeight, bitsPerChannel, colorSpace, this ); + else + m_selectionHistogram = 0L; +} + +void HistogramWidget::updateData( const PlaYUVerFrame &image, const PlaYUVerFrame &selection ) +{ + d->sixteenBits = false; + d->imageColorSpace = image.getPelFormat(); + d->imageChannels = image.getNumberChannels(); + + // We are deleting the histogram data, + // so we must not use it to draw any more. + d->clearFlag = HistogramWidgetPrivate::HistogramNone; + + // Do not using PlaYUVerFrameStatistics::getHistogramSegment() + // method here because histogram hasn't yet been computed. + + d->range = d->sixteenBits ? 65535 : 255; + emit signalMaximumValueChanged( d->range ); + + // Remove old histogram data from memory. + if( m_imageHistogram ) + delete m_imageHistogram; + + if( m_selectionHistogram ) + delete m_selectionHistogram; + + // Calc new histogram data + m_imageHistogram = new PlaYUVerFrameStatistics( image, this ); + + if( !selection.isValid() ) + m_selectionHistogram = new PlaYUVerFrameStatistics( selection, this ); + else + m_selectionHistogram = 0L; + +} + +void HistogramWidget::updateSelectionData( uchar *selData, uint selWidth, uint selHeight, int bitsPerChannel, Int colorSpace ) +{ + // Remove old histogram data from memory. + + if( m_selectionHistogram ) + delete m_selectionHistogram; + + // Calc new histogram data + m_selectionHistogram = new PlaYUVerFrameStatistics( selData, selWidth, selHeight, bitsPerChannel, colorSpace, this ); +} + +void HistogramWidget::updateSelectionData( const PlaYUVerFrame &selection ) +{ + // Remove old histogram data from memory. + + if( m_selectionHistogram ) + delete m_selectionHistogram; + + m_selectionHistogram = new PlaYUVerFrameStatistics( selection, this ); + +} + +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::slotBlinkTimerDone( void ) +{ + d->blinkFlag = !d->blinkFlag; + d->inInitialRepaintWait = false; + update(); + d->blinkTimer->start( 200 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// Paint Event +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::paintEvent( QPaintEvent * ) +{ + int maxX = size().width() - 1; + int maxY = size().height() - 1; + + // Widget is disabled, not initialized, + // or loading, but no message shall be drawn: + // Drawing grayed frame. + if( !isEnabled() || d->clearFlag == HistogramWidgetPrivate::HistogramNone + || ( !d->blinkComputation && ( d->clearFlag == HistogramWidgetPrivate::HistogramStarted || d->clearFlag == HistogramWidgetPrivate::HistogramDataLoading ) ) ) + { + QPainter p1( this ); + + p1.fillRect( 0, 0, maxX, maxY, palette().color( QPalette::Disabled, QPalette::Window ) ); + p1.setPen( QPen( palette().color( QPalette::Disabled, QPalette::WindowText ), 1, Qt::SolidLine ) ); + p1.drawRect( 0, 0, maxX, maxY ); + + return; + } + // Image data is loading or histogram is being computed: + // Draw message. + else if( d->blinkComputation && ( d->clearFlag == HistogramWidgetPrivate::HistogramStarted || d->clearFlag == HistogramWidgetPrivate::HistogramDataLoading ) ) + { + QPainter p1( this ); + + p1.fillRect( 0, 0, maxX, maxY, Qt::white ); + + if( d->blinkFlag ) + p1.setPen( Qt::green ); + else + p1.setPen( Qt::darkGreen ); + + if( d->clearFlag == HistogramWidgetPrivate::HistogramDataLoading ) + p1.drawText( 0, 0, maxX, maxY, Qt::AlignCenter, tr( "Loading image..." ) ); + else + p1.drawText( 0, 0, maxX, maxY, Qt::AlignCenter, tr( "Histogram\ncalculation\nin progress..." ) ); + + return; + } + // Histogram computation failed: + // Draw message. + else if( d->clearFlag == HistogramWidgetPrivate::HistogramFailed ) + { + QPainter p1( this ); + p1.fillRect( 0, 0, maxX, maxY, Qt::white ); + p1.setPen( Qt::red ); + p1.drawText( 0, 0, maxX, maxY, Qt::AlignCenter, tr( "Histogram\ncalculation\nfailed." ) ); + //bitBlt(this, 0, 0, &pm); + return; + } + + int x, y; + int yr, yg, yb; // For all color channels. + int wWidth = width() - 1; + int wHeight = height() - 1; + double max; + class PlaYUVerFrameStatistics *histogram; + + if( m_renderingType == ImageSelectionHistogram && m_selectionHistogram ) + histogram = m_selectionHistogram; + else + histogram = m_imageHistogram; + + if( !histogram ) + return; + + x = 0; + y = 0; + yr = 0; + yg = 0; + yb = 0; + max = 0.0; + + switch( m_channelType ) + { + case HistogramWidget::GreenChannelHistogram: // Green channel. + max = histogram->getMaximum( PlaYUVerFrameStatistics::GreenChannel ); + break; + + case HistogramWidget::BlueChannelHistogram: // Blue channel. + max = histogram->getMaximum( PlaYUVerFrameStatistics::BlueChannel ); + break; + + case HistogramWidget::RedChannelHistogram: // Red channel. + max = histogram->getMaximum( PlaYUVerFrameStatistics::RedChannel ); + break; + + case HistogramWidget::AlphaChannelHistogram: // Alpha channel. + max = histogram->getMaximum( PlaYUVerFrameStatistics::AlphaChannel ); + break; + + case HistogramWidget::ColorChannelsHistogram: // All color channels. + max = qMax( qMax( histogram->getMaximum( PlaYUVerFrameStatistics::RedChannel ), histogram->getMaximum( PlaYUVerFrameStatistics::GreenChannel ) ), + histogram->getMaximum( PlaYUVerFrameStatistics::BlueChannel ) ); + break; + + case HistogramWidget::LumaHistogram: // Luminance. + max = histogram->getMaximum( PlaYUVerFrameStatistics::LumaChannel ); + break; + } + + switch( m_scaleType ) + { + case HistogramWidget::LinScaleHistogram: + break; + + case HistogramWidget::LogScaleHistogram: + if( max > 0.0 ) + max = log( max ); + else + max = 1.0; + break; + } + + // From Qt 4.0, QWidget automatically double-buffers its painting, + // so there's no need to write double-buffering code in paintEvent() + // to avoid flicker. + QPainter p1( this ); + + // Drawing selection or all histogram values. + + for( x = 0; x < wWidth; x++ ) + { + double value = 0.0; + double value_r = 0.0, value_g = 0.0, value_b = 0.0; // For all channels. + int i, j; + + i = ( x * histogram->getHistogramSegment() ) / wWidth; + j = ( ( x + 1 ) * histogram->getHistogramSegment() ) / wWidth; + + do + { + double v; + double vr, vg, vb; // For all color channels. + + v = 0.0; + vr = 0.0; + vg = 0.0; + vb = 0.0; + + switch( m_channelType ) + { + case HistogramWidget::GreenChannelHistogram: // Green channel. + v = histogram->getValue( PlaYUVerFrameStatistics::GreenChannel, i++ ); + break; + + case HistogramWidget::BlueChannelHistogram: // Blue channel. + v = histogram->getValue( PlaYUVerFrameStatistics::BlueChannel, i++ ); + break; + + case HistogramWidget::RedChannelHistogram: // Red channel. + v = histogram->getValue( PlaYUVerFrameStatistics::RedChannel, i++ ); + break; + + case HistogramWidget::AlphaChannelHistogram: // Alpha channel. + v = histogram->getValue( PlaYUVerFrameStatistics::AlphaChannel, i++ ); + break; + + case HistogramWidget::ColorChannelsHistogram: // All color. + vr = histogram->getValue( PlaYUVerFrameStatistics::RedChannel, i ); + vg = histogram->getValue( PlaYUVerFrameStatistics::GreenChannel, i ); + vb = histogram->getValue( PlaYUVerFrameStatistics::BlueChannel, i++ ); + break; + + case HistogramWidget::LumaHistogram: // Luminance. + v = histogram->getValue( PlaYUVerFrameStatistics::LumaChannel, i++ ); + break; + } + + if( m_channelType != HistogramWidget::ColorChannelsHistogram ) + { + if( v > value ) + value = v; + } + else + { + if( vr > value_r ) + value_r = vr; + if( vg > value_g ) + value_g = vg; + if( vb > value_b ) + value_b = vb; + } + } + while( i < j ); + + if( m_channelType != HistogramWidget::ColorChannelsHistogram ) + { + switch( m_scaleType ) + { + case HistogramWidget::LinScaleHistogram: + y = ( int )( ( wHeight * value ) / max ); + break; + + case HistogramWidget::LogScaleHistogram: + if( value <= 0.0 ) + value = 1.0; + y = ( int )( ( wHeight * log( value ) ) / max ); + break; + + default: + y = 0; + break; + } + } + else + { + switch( m_scaleType ) + { + case HistogramWidget::LinScaleHistogram: + yr = ( int )( ( wHeight * value_r ) / max ); + yg = ( int )( ( wHeight * value_g ) / max ); + yb = ( int )( ( wHeight * value_b ) / max ); + break; + + case HistogramWidget::LogScaleHistogram: + if( value_r <= 0.0 ) + value_r = 1.0; + if( value_g <= 0.0 ) + value_g = 1.0; + if( value_b <= 0.0 ) + value_b = 1.0; + yr = ( int )( ( wHeight * log( value_r ) ) / max ); + yg = ( int )( ( wHeight * log( value_g ) ) / max ); + yb = ( int )( ( wHeight * log( value_b ) ) / max ); + break; + + default: + yr = 0; + yg = 0; + yb = 0; + break; + } + } + + // Drawing the histogram + selection or only the histogram. + + if( m_channelType != HistogramWidget::ColorChannelsHistogram ) + { + if( d->selectMode == true ) // Selection mode enable ? + { + if( x >= ( int )( d->xmin * wWidth ) && x <= ( int )( d->xmax * wWidth ) ) + { + p1.setPen( QPen( Qt::black, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, 0 ); + p1.setPen( QPen( Qt::lightGray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - y ); + } + else + { + p1.setPen( QPen( Qt::black, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - y ); + p1.setPen( QPen( Qt::white, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - y, x, 0 ); + + if( x == wWidth / 4 || x == wWidth / 2 || x == 3 * wWidth / 4 ) + { + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, 0 ); + } + } + } + else + { + p1.setPen( QPen( Qt::black, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - y ); + p1.setPen( QPen( Qt::white, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - y, x, 0 ); + + if( x == wWidth / 4 || x == wWidth / 2 || x == 3 * wWidth / 4 ) + { + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, 0 ); + } + } + } + else // All colors channel + { + if( d->selectMode == true ) // Histogram selection mode enable ? + { + if( x >= ( int )( d->xmin * wWidth ) && x <= ( int )( d->xmax * wWidth ) ) + { + p1.setPen( QPen( Qt::black, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, 0 ); + p1.setPen( QPen( Qt::lightGray, 1, Qt::SolidLine ) ); + + // Witch color must be used on the foreground with all + // colors channel mode? + switch( m_colorType ) + { + case HistogramWidget::RedColor: + p1.drawLine( x, wHeight, x, wHeight - yr ); + break; + + case HistogramWidget::GreenColor: + p1.drawLine( x, wHeight, x, wHeight - yg ); + break; + + default: + p1.drawLine( x, wHeight, x, wHeight - yb ); + break; + } + } + else + { + // Which color must be used on the foreground with all + // colors channel mode? + switch( m_colorType ) + { + case HistogramWidget::RedColor: + p1.setPen( QPen( Qt::green, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yg ); + p1.setPen( QPen( Qt::blue, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yb ); + p1.setPen( QPen( Qt::red, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yr ); + + p1.setPen( QPen( Qt::white, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - qMax( qMax( yr, yg ), yb ), x, 0 ); + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - yg - 1, x, wHeight - yg ); + p1.drawLine( x, wHeight - yb - 1, x, wHeight - yb ); + p1.drawLine( x, wHeight - yr - 1, x, wHeight - yr ); + + break; + + case HistogramWidget::GreenColor: + p1.setPen( QPen( Qt::blue, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yb ); + p1.setPen( QPen( Qt::red, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yr ); + p1.setPen( QPen( Qt::green, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yg ); + + p1.setPen( QPen( Qt::white, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - qMax( qMax( yr, yg ), yb ), x, 0 ); + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - yb - 1, x, wHeight - yb ); + p1.drawLine( x, wHeight - yr - 1, x, wHeight - yr ); + p1.drawLine( x, wHeight - yg - 1, x, wHeight - yg ); + + break; + + default: + p1.setPen( QPen( Qt::red, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yr ); + p1.setPen( QPen( Qt::green, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yg ); + p1.setPen( QPen( Qt::blue, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yb ); + + p1.setPen( QPen( Qt::white, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - qMax( qMax( yr, yg ), yb ), x, 0 ); + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - yr - 1, x, wHeight - yr ); + p1.drawLine( x, wHeight - yg - 1, x, wHeight - yg ); + p1.drawLine( x, wHeight - yb - 1, x, wHeight - yb ); + + break; + } + + if( x == wWidth / 4 || x == wWidth / 2 || x == 3 * wWidth / 4 ) + { + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, 0 ); + } + } + } + else + { + // Which color must be used on the foreground with all + // colors channel mode? + switch( m_colorType ) + { + case HistogramWidget::RedColor: + p1.setPen( QPen( Qt::green, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yg ); + p1.setPen( QPen( Qt::blue, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yb ); + p1.setPen( QPen( Qt::red, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yr ); + + p1.setPen( QPen( Qt::white, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - qMax( qMax( yr, yg ), yb ), x, 0 ); + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - yg - 1, x, wHeight - yg ); + p1.drawLine( x, wHeight - yb - 1, x, wHeight - yb ); + p1.drawLine( x, wHeight - yr - 1, x, wHeight - yr ); + + break; + + case HistogramWidget::GreenColor: + p1.setPen( QPen( Qt::blue, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yb ); + p1.setPen( QPen( Qt::red, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yr ); + p1.setPen( QPen( Qt::green, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yg ); + + p1.setPen( QPen( Qt::white, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - qMax( qMax( yr, yg ), yb ), x, 0 ); + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - yb - 1, x, wHeight - yb ); + p1.drawLine( x, wHeight - yr - 1, x, wHeight - yr ); + p1.drawLine( x, wHeight - yg - 1, x, wHeight - yg ); + + break; + + default: + p1.setPen( QPen( Qt::red, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yr ); + p1.setPen( QPen( Qt::green, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yg ); + p1.setPen( QPen( Qt::blue, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, wHeight - yb ); + + p1.setPen( QPen( Qt::white, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - qMax( qMax( yr, yg ), yb ), x, 0 ); + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight - yr - 1, x, wHeight - yr ); + p1.drawLine( x, wHeight - yg - 1, x, wHeight - yg ); + p1.drawLine( x, wHeight - yb - 1, x, wHeight - yb ); + + break; + } + + if( x == wWidth / 4 || x == wWidth / 2 || x == 3 * wWidth / 4 ) + { + p1.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) ); + p1.drawLine( x, wHeight, x, 0 ); + } + } + } + } + + // Drawing color guide. + + p1.setPen( QPen( Qt::red, 1, Qt::DotLine ) ); + int guidePos; + + if( d->guideVisible ) + { + switch( m_channelType ) + { + case HistogramWidget::RedChannelHistogram: + guidePos = d->colorGuide.red(); + break; + + case HistogramWidget::GreenChannelHistogram: + guidePos = d->colorGuide.green(); + break; + + case HistogramWidget::BlueChannelHistogram: + guidePos = d->colorGuide.blue(); + break; + + case HistogramWidget::LumaHistogram: + guidePos = qMax( qMax( d->colorGuide.red(), d->colorGuide.green() ), d->colorGuide.blue() ); + break; + + default: // Alpha. + guidePos = -1; + break; + } + + if( guidePos != -1 ) + { + int xGuide = ( guidePos * wWidth ) / histogram->getHistogramSegment(); + p1.drawLine( xGuide, 0, xGuide, wHeight ); + + QString string = tr( "x:%1" ).arg( guidePos ); + QFontMetrics fontMt( string ); + QRect rect = fontMt.boundingRect( 0, 0, wWidth, wHeight, 0, string ); + p1.setPen( QPen( Qt::red, 1, Qt::SolidLine ) ); + rect.moveTop( 1 ); + + if( xGuide < wWidth / 2 ) + { + rect.moveLeft( xGuide ); + p1.fillRect( rect, QBrush( QColor( 250, 250, 255 ) ) ); + p1.drawRect( rect ); + rect.moveLeft( xGuide + 3 ); + p1.drawText( rect, Qt::AlignLeft, string ); + } + else + { + rect.moveRight( xGuide ); + p1.fillRect( rect, QBrush( QColor( 250, 250, 255 ) ) ); + p1.drawRect( rect ); + rect.moveRight( xGuide - 3 ); + p1.drawText( rect, Qt::AlignRight, string ); + } + } + } + + if( d->statisticsVisible ) + { + QString tipText, value; + QString cellBeg( "" ); + QString cellMid( "" ); + QString cellEnd( "" ); + tipText = ""; + + tipText += cellBeg + tr( "Mean:" ) + cellMid; + double mean = histogram->getMean( m_channelType, 0, histogram->getHistogramSegment() - 1 ); + tipText += value.setNum( mean, 'f', 1 ) + cellEnd; + + tipText += cellBeg + tr( "Pixels:" ) + cellMid; + double pixels = histogram->getPixels(); + tipText += value.setNum( ( float )pixels, 'f', 0 ) + cellEnd; + + tipText += cellBeg + tr( "Std dev.:" ) + cellMid; + double stddev = histogram->getStdDev( m_channelType, 0, histogram->getHistogramSegment() - 1 ); + tipText += value.setNum( stddev, 'f', 1 ) + cellEnd; + + tipText += cellBeg + tr( "Selected:" ) + cellMid; + double counts = histogram->getCount( m_channelType, 0, histogram->getHistogramSegment() - 1 ); + tipText += value.setNum( ( float )counts, 'f', 0 ) + cellEnd; + + tipText += cellBeg + tr( "Median:" ) + cellMid; + double median = histogram->getMedian( m_channelType, 0, histogram->getHistogramSegment() - 1 ); + tipText += value.setNum( median, 'f', 1 ) + cellEnd; + + tipText += cellBeg + tr( "Percent:" ) + cellMid; + double percentile = ( pixels > 0 ? ( 100.0 * counts / pixels ) : 0.0 ); + tipText += value.setNum( percentile, 'f', 1 ) + cellEnd; + + tipText += "
"; + + setToolTip( tipText ); + } + + // Drawing frame. + + p1.setPen( QPen( Qt::black, 1, Qt::SolidLine ) ); + p1.drawRect( 0, 0, maxX, maxY ); + + //bitBlt(this, 0, 0, &pm); +} + +//////////////////////////////////////////////////////////////////////////////// +// Mouse Events +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::mousePressEvent( QMouseEvent * e ) +{ + if( d->selectMode == true && d->clearFlag == HistogramWidgetPrivate::HistogramCompleted ) + { + if( !d->inSelected ) + { + d->inSelected = true; + update(); + } + + d->xmin = ( ( double )e->pos().x() ) / ( ( double )width() ); + d->xminOrg = d->xmin; + notifyValuesChanged(); + //emit signalValuesChanged( (int)(d->xmin * d->range), ); + d->xmax = 0.0; + } +} + +void HistogramWidget::mouseReleaseEvent( QMouseEvent * ) +{ + if( d->selectMode == true && d->clearFlag == HistogramWidgetPrivate::HistogramCompleted ) + + { + d->inSelected = false; + // Only single click without mouse move? Remove selection. + if( d->xmax == 0.0 ) + { + d->xmin = 0.0; + //emit signalMinValueChanged( 0 ); + //emit signalMaxValueChanged( d->range ); + notifyValuesChanged(); + update(); + } + } +} + +void HistogramWidget::mouseMoveEvent( QMouseEvent * e ) +{ + if( d->selectMode == true && d->clearFlag == HistogramWidgetPrivate::HistogramCompleted ) + { + setCursor( Qt::CrossCursor ); + + if( d->inSelected ) + { + double max = ( ( double )e->pos().x() ) / ( ( double )width() ); + //int max = (int)(e->pos().x()*((float)m_imageHistogram->getHistogramSegment()/(float)width())); + + if( max < d->xminOrg ) + { + d->xmax = d->xminOrg; + d->xmin = max; + //emit signalMinValueChanged( (int)(d->xmin * d->range) ); + } + else + { + d->xmin = d->xminOrg; + d->xmax = max; + } + + notifyValuesChanged(); + //emit signalMaxValueChanged( d->xmax == 0.0 ? d->range : (int)(d->xmax * d->range) ); + + update(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void HistogramWidget::notifyValuesChanged() +{ + emit signalIntervalChanged( ( int )( d->xmin * d->range ), d->xmax == 0.0 ? d->range : ( int )( d->xmax * d->range ) ); +} + +void HistogramWidget::slotMinValueChanged( int min ) +{ + if( d->selectMode == true && d->clearFlag == HistogramWidgetPrivate::HistogramCompleted ) + { + if( min == 0 && d->xmax == 1.0 ) + { + // everything is selected means no selection + d->xmin = 0.0; + d->xmax = 0.0; + } + if( min >= 0 && min < d->range ) + { + d->xmin = ( ( double )min ) / d->range; + } + update(); + } +} + +void HistogramWidget::slotMaxValueChanged( int max ) +{ + if( d->selectMode == true && d->clearFlag == HistogramWidgetPrivate::HistogramCompleted ) + { + if( d->xmin == 0.0 && max == d->range ) + { + // everything is selected means no selection + d->xmin = 0.0; + d->xmax = 0.0; + } + else if( max > 0 && max <= d->range ) + { + d->xmax = ( ( double )max ) / d->range; + } + update(); + } +} + +} // NAMESPACE + diff --git a/source/app/HistogramWidget.h b/source/app/HistogramWidget.h new file mode 100755 index 0000000..ab21bf6 --- /dev/null +++ b/source/app/HistogramWidget.h @@ -0,0 +1,200 @@ +/* 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 HistogramWidget.h + * \brief Function to show image histogram + * Based on the work of Ricardo N. Rocha Sardo in SCode project + */ + +#ifndef __HISTOGRAMWIDGET_H__ +#define __HISTOGRAMWIDGET_H__ + +#include +#include +#include "PlaYUVerFrame.h" +#include "PlaYUVerFrameStatistics.h" + +class QEvent; + +namespace plaYUVer +{ + +class PlaYUVerFrameStatistics; +class HistogramWidgetPrivate; + +class HistogramWidget: public QWidget +{ +Q_OBJECT + +public: + + enum HistogramChannel + { + /** Luminance */ + LumaHistogram = 0, + /** Red channel */ + RedChannelHistogram, + /** Green channel */ + GreenChannelHistogram, + /** Blue channel */ + BlueChannelHistogram, + /** Alpha channel */ + AlphaChannelHistogram, + /** All color channels */ + ColorChannelsHistogram + }; + + enum HistogramScale + { + /** Linear scale */ + LinScaleHistogram = 0, + /** Logarithmic scale */ + LogScaleHistogram + }; + + enum HistogramAllColorMode + { + /** Red color to foreground in All Colors Channel mode */ + RedColor = 0, + /** Green color to foreground in All Colors Channel mode */ + GreenColor, + /** Blue color to foreground in All Colors Channel mode */ + BlueColor + }; + + enum HistogramRendering + { + /** Full image histogram rendering */ + FullImageHistogram = 0, + /** Image selection histogram rendering */ + ImageSelectionHistogram + }; + + enum HistogramOption + { + /** Blink the histogram area during histogram computation */ + BlinkComputation = 0x1, + /** Show image statistics in tooltip */ + ShowStatistics = 0x2, + /** It allows the user to select an area of the histograma */ + SelectMode = 0x4, + /** Show the luminosity channel when applied */ + ShowLumaChannel = 0x8, + /** Set all the options */ + AllOptions = BlinkComputation | ShowStatistics | SelectMode | ShowLumaChannel + };Q_DECLARE_FLAGS( HistogramOptions, HistogramOption ) + +public : + + /** + * Constructor without image data. Needed to use @p updateData() method + * after to create instance. + * @param width of the widget + * @param height of the widget + * @param parent the histogram widget parent + */ + HistogramWidget( int width, int height, QWidget *parent = 0 ); + + void setupWidget( int width, int height, + HistogramOptions options = AllOptions ); + + ~HistogramWidget(); + + /** Stop current histogram computations.*/ + void stopHistogramComputation( void ); + + /** Update full image histogram data methods.*/ + void updateData( uchar *imageData, uint imageWidth, uint imageHeight, + int bitsPerChannel, Int pixel_format, + uchar *selData = 0, uint selWidth = 0, uint selHeight = 0); + + /** Update full image histogram data from SImage.*/ + void updateData( const PlaYUVerFrame &image, const PlaYUVerFrame &selection ); + + /** Update image selection histogram data methods.*/ + void updateSelectionData( uchar *selData, uint selWidth, uint selHeight, + int bitsPerChannel, Int colorSpace ); + + /** Update image selection histogram data from SImage.*/ + void updateSelectionData( const PlaYUVerFrame &imageSelection ); + + /** @see @p HistogramOption */ + void setOptions( HistogramOptions options = AllOptions ); + void setDataLoading(); + void setLoadingFailed(); + void setHistogramGuideByColor( QColor color ); + + void reset(void); + +public: + /** Channel type to draw */ + int m_channelType; + /** Scale to use for drawing */ + int m_scaleType; + /** Color to use for drawing in All Colors Channel mode */ + int m_colorType; + /** Using full image or image selection for histogram rendering */ + int m_renderingType; + + /** Full image */ + PlaYUVerFrameStatistics *m_imageHistogram; + /** Histogram area selection */ + PlaYUVerFrameStatistics *m_selectionHistogram; + + signals: + + void signalIntervalChanged( int min, int max ); + void signalMaximumValueChanged( int ); + void signalHistogramComputationDone( bool ); + void signalHistogramComputationFailed( void ); + +public slots: + + void slotMinValueChanged( int min ); + void slotMaxValueChanged( int max ); + +protected slots: + + void slotBlinkTimerDone( void ); + +protected: + + void paintEvent( QPaintEvent * ); + void mousePressEvent( QMouseEvent * e ); + void mouseReleaseEvent( QMouseEvent * e ); + void mouseMoveEvent( QMouseEvent * e ); + +private : + + void customEvent( QEvent *event ); + void notifyValuesChanged(); + +private: + + HistogramWidgetPrivate* d; + +}; + +} + // NAMESPACE + +Q_DECLARE_OPERATORS_FOR_FLAGS( plaYUVer::HistogramWidget::HistogramOptions ) + +#endif // __HISTOGRAMWIDGET_H__ diff --git a/source/app/PropertiesSidebar.cpp b/source/app/PropertiesSidebar.cpp index 23daea2..ba81f3b 100755 --- a/source/app/PropertiesSidebar.cpp +++ b/source/app/PropertiesSidebar.cpp @@ -162,9 +162,148 @@ FramePropertiesSideBar::FramePropertiesSideBar( QWidget* parent ) : // -------------- Variables definition -------------- m_pcFrame = NULL; - // ----------------- GUI definition ----------------- + // Histogram area ----------------------------------------------------- + + QLabel *channelLabel = new QLabel( tr( "Channel:" ) ); + channelLabel->setAlignment( Qt::AlignVCenter | Qt::AlignRight ); + + channelCB = new QComboBox; + channelCB->addItem( tr( "Luminance" ) ); + channelCB->setItemIcon( 0, QIcon( ":/images/channel-luma.png" ) ); + + channelCB->setWhatsThis( tr( "

Select here the histogram channel to display:

" + "Luminance: Display luminance (perceived brightness).

" + "Red: Display the red image channel.

" + "Green: Display the green image channel.

" + "Blue: Display the blue image channel.

" + "Alpha: Display the alpha image channel. " + "This channel corresponds to the transparency value and " + "is supported by some image formats such as PNG or TIFF.

" + "Colors: Display all color channel values at " + "the same time." ) ); + + QString scaleWhatsThis( tr( "

Select here the histogram scale.

" + "If the image's maximal values are small, you can use the " + "linear scale.

Logarithmic scale can be used when the maximal" + " values are big; if it is used, all values (small and large) " + "will be visible on the graph." ) ); + + linHistoButton = new QPushButton; + linHistoButton->setToolTip( tr( "Linear" ) ); + linHistoButton->setIcon( QIcon( ":/images/histogram-lin.png" ) ); + linHistoButton->setWhatsThis( scaleWhatsThis ); + linHistoButton->setCheckable( true ); + linHistoButton->setAutoExclusive( true ); + linHistoButton->setChecked( true ); + + logHistoButton = new QPushButton; + logHistoButton->setToolTip( tr( "Logarithmic" ) ); + logHistoButton->setIcon( QIcon( ":/images/histogram-log.png" ) ); + logHistoButton->setWhatsThis( scaleWhatsThis ); + logHistoButton->setCheckable( true ); + logHistoButton->setAutoExclusive( true ); + + scaleButtonGroup = new QButtonGroup( this ); + scaleButtonGroup->addButton( linHistoButton, HistogramWidget::LinScaleHistogram ); + scaleButtonGroup->addButton( logHistoButton, HistogramWidget::LogScaleHistogram ); + + QHBoxLayout *scaleLayout = new QHBoxLayout; + scaleLayout->addWidget( linHistoButton ); + scaleLayout->addWidget( logHistoButton ); + + colorsLabel = new QLabel( tr( "Colors:" ) ); + colorsLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter ); + + colorsCB = new QComboBox; + colorsCB->addItem( tr( "Red" ) ); + colorsCB->addItem( tr( "Green" ) ); + colorsCB->addItem( tr( "Blue" ) ); + colorsCB->setEnabled( false ); + colorsCB->setWhatsThis( tr( "

Select here the main color displayed with Colors Channel mode:" + "

Red: Draw the red image channel in the foreground.

" + "Green: Draw the green image channel in the foreground.

" + "Blue: Draw the blue image channel in the foreground.

" ) ); + + colorsCB->hide(); + colorsLabel->hide(); + + QString regionWhatsThis( tr( "

Select here from which region the histogram will be computed:" + "

Full Image: Compute histogram using the full image." + "

Selection: Compute histogram using the current image " + "selection." ) ); + + fullImageButton = new QPushButton; + fullImageButton->setToolTip( tr( "Full Image" ) ); + fullImageButton->setIcon( QIcon( ":/images/image-full.png" ) ); + fullImageButton->setWhatsThis( regionWhatsThis ); + fullImageButton->setCheckable( true ); + fullImageButton->setAutoExclusive( true ); + fullImageButton->setChecked( true ); + + selectionImageButton = new QPushButton; + selectionImageButton->setToolTip( tr( "Selection" ) ); + selectionImageButton->setIcon( QIcon( ":/images/image-selection.png" ) ); + selectionImageButton->setWhatsThis( regionWhatsThis ); + selectionImageButton->setCheckable( true ); + selectionImageButton->setAutoExclusive( true ); + + renderingButtonGroup = new QButtonGroup( this ); + renderingButtonGroup->addButton( fullImageButton, HistogramWidget::FullImageHistogram ); + renderingButtonGroup->addButton( selectionImageButton, HistogramWidget::ImageSelectionHistogram ); + + QHBoxLayout *regionLayout = new QHBoxLayout; + regionLayout->addWidget( fullImageButton ); + regionLayout->addWidget( selectionImageButton ); QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget( channelLabel, 1, 0 ); + mainLayout->addWidget( channelCB, 1, 1 ); + mainLayout->addLayout( scaleLayout, 1, 3 ); + mainLayout->addWidget( colorsLabel, 2, 0 ); + mainLayout->addWidget( colorsCB, 2, 1 ); + mainLayout->addLayout( regionLayout, 2, 3 ); + mainLayout->setColumnStretch( 2, 10 ); + + // ------------------------------------------------------------- + + histogramWidget = new HistogramWidget( 256, 140 ); + histogramWidget->setOptions( HistogramWidget::BlinkComputation | HistogramWidget::SelectMode | HistogramWidget::ShowLumaChannel ); + histogramWidget->setWhatsThis( tr( "

This is the histogram drawing of " + "the selected image channel" ) ); + + QVBoxLayout *histogramLayout = new QVBoxLayout; + histogramLayout->setSpacing( 1 ); + histogramLayout->addWidget( histogramWidget ); + + mainLayout->addLayout( histogramLayout, 3, 0, 1, 4 ); + + // ------------------------------------------------------------- + + QLabel *rangeLabel = new QLabel( tr( "Range:" ) ); + rangeLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + + minInterv = new QSpinBox; + minInterv->setRange( 0, 255 ); + minInterv->setSingleStep( 1 ); + minInterv->setValue( 0 ); + minInterv->setWhatsThis( tr( "

Select here the minimal intensity " + "value of the histogram selection." ) ); + + maxInterv = new QSpinBox; + maxInterv->setRange( 0, 255 ); + maxInterv->setSingleStep( 1 ); + maxInterv->setValue( 0 ); + minInterv->setWhatsThis( tr( "

Select here the maximal intensity value" + " of the histogram selection." ) ); + + QHBoxLayout *rangeLayout = new QHBoxLayout; + rangeLayout->addWidget( rangeLabel ); + rangeLayout->addWidget( minInterv ); + rangeLayout->addWidget( maxInterv ); + + mainLayout->addLayout( rangeLayout, 4, 0, 1, 4 ); + + // ----------------- GUI definition ----------------- QLabel *pixelsLabel = new QLabel( tr( "Pixels:" ) ); pixelsLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); @@ -222,10 +361,27 @@ FramePropertiesSideBar::FramePropertiesSideBar( QWidget* parent ) : setLayout( mainLayout ); setEnabled( false ); + connect( channelCB, SIGNAL( activated(int) ), this, SLOT( slotChannelChanged(int) ) ); + connect( scaleButtonGroup, SIGNAL( buttonClicked(int) ), this, SLOT( slotScaleChanged(int) ) ); + connect( colorsCB, SIGNAL( activated(int) ), this, SLOT( slotColorsChanged(int) ) ); + connect( renderingButtonGroup, SIGNAL( buttonClicked(int) ), this, SLOT( slotRenderingChanged(int) ) ); + connect( histogramWidget, SIGNAL( signalIntervalChanged( int, int ) ), this, SLOT( slotUpdateInterval(int, int) ) ); + connect( histogramWidget, SIGNAL( signalMaximumValueChanged(int) ), this, SLOT( slotUpdateIntervRange(int) ) ); + connect( histogramWidget, SIGNAL( signalHistogramComputationDone(bool) ), this, SLOT( slotRefreshOptions(bool) ) ); + connect( histogramWidget, SIGNAL( signalHistogramComputationFailed(void) ), this, SLOT( slotHistogramComputationFailed(void) ) ); + connect( minInterv, SIGNAL( valueChanged (int) ), this, SLOT( slotMinValueChanged(int) ) ); + connect( maxInterv, SIGNAL( valueChanged (int) ), this, SLOT( slotMaxValueChanged(int) ) ); + } FramePropertiesSideBar::~FramePropertiesSideBar() { + // If there is a currently histogram computation when dialog is closed, + // stop it before the image data are deleted automatically! + histogramWidget->stopHistogramComputation(); + + if( histogramWidget ) + delete histogramWidget; } @@ -246,16 +402,309 @@ Void FramePropertiesSideBar::setData( PlaYUVerFrame* pcFrame ) if( !pcFrame ) { + // Remove the histogram data from memory + histogramWidget->reset(); setEnabled( false ); return; } else { - m_pcFrame = pcFrame; + if( ( PlaYUVerFrame::isRGBorYUVorGray( pcFrame->getPelFormat() ) == PlaYUVerFrame::COLOR_RGB + || PlaYUVerFrame::isRGBorYUVorGray( pcFrame->getPelFormat() ) == PlaYUVerFrame::COLOR_ARGB ) && ( channelCB->count() == 1 ) ) + { + channelCB->addItem( tr( "Red" ) ); + channelCB->setItemIcon( 1, QIcon( ":/images/channel-red.png" ) ); + channelCB->addItem( tr( "Green" ) ); + channelCB->setItemIcon( 2, QIcon( ":/images/channel-green.png" ) ); + channelCB->addItem( tr( "Blue" ) ); + channelCB->setItemIcon( 3, QIcon( ":/images/channel-blue.png" ) ); + + if( PlaYUVerFrame::isRGBorYUVorGray( pcFrame->getPelFormat() ) == PlaYUVerFrame::COLOR_ARGB ) + { + channelCB->addItem( tr( "Alpha" ) ); + channelCB->setItemIcon( 4, QIcon( ":/images/channel-alpha.png" ) ); + } + + channelCB->addItem( QIcon( ":/images/channel-all.png" ), tr( "Colors" ) ); + colorsCB->show(); + colorsLabel->show(); + } + else if( channelCB->count() != 1 ) + { + channelCB->clear(); + channelCB->addItem( tr( "Luminance" ) ); + channelCB->setItemIcon( 0, QIcon( ":/images/channel-luma.png" ) ); + colorsCB->hide(); + colorsLabel->hide(); + } + setEnabled( true ); + + m_pcFrame = pcFrame; + + if( !m_pcFrame->isValid() ) + { + // If a selection area is done in Image Editor and if the current + // image is the same in Image Viewer, then compute too the histogram + // for this selection. + histogramWidget->updateData( *m_pcFrame, PlaYUVerFrame() ); + fullImageButton->hide(); + selectionImageButton->hide(); + updateInformations(); + } + else + { + histogramWidget->setLoadingFailed(); + slotHistogramComputationFailed(); + } } } +//////////////////////////////////////////////////////////////////////////////// +// Set Selection +//////////////////////////////////////////////////////////////////////////////// + +Void FramePropertiesSideBar::setSelection( const QRect &selectionArea ) +{ + // This is necessary to stop computation because image.bits() is + // currently used by threaded histogram algorithm. + + m_cSelectionArea = selectionArea; + + if( selectionArea.isValid() ) + { + + histogramWidget->stopHistogramComputation(); + + m_pcFrame->CopyFrom( m_pcFrameSelection ); +// histogramWidget->updateSelectionData( imageSelection.bits(), +// imageSelection.width(), +// imageSelection.height(), +// imageSelection.bitsPerChannel(), +// imageSelection.colorSpace() ); + histogramWidget->updateSelectionData( *m_pcFrameSelection ); + fullImageButton->show(); + selectionImageButton->show(); + selectionImageButton->click(); + } + else + { + fullImageButton->hide(); + selectionImageButton->hide(); + slotRenderingChanged( HistogramWidget::FullImageHistogram ); + } +} +//////////////////////////////////////////////////////////////////////////////// +// SLOTS +//////////////////////////////////////////////////////////////////////////////// + +void FramePropertiesSideBar::slotRefreshOptions( bool /*depth*/) +{ + slotChannelChanged( channelCB->currentIndex() ); + + if( linHistoButton->isChecked() ) + slotScaleChanged( HistogramWidget::LinScaleHistogram ); + else if( logHistoButton->isChecked() ) + slotScaleChanged( HistogramWidget::LogScaleHistogram ); + + slotColorsChanged( colorsCB->currentIndex() ); + + if( m_cSelectionArea.isValid() ) + { + if( fullImageButton->isChecked() ) + slotRenderingChanged( HistogramWidget::FullImageHistogram ); + else if( selectionImageButton->isChecked() ) + slotRenderingChanged( HistogramWidget::ImageSelectionHistogram ); + + } +} + +void FramePropertiesSideBar::slotHistogramComputationFailed() +{ + m_pcFrame = NULL; + m_pcFrameSelection = NULL; +} + +void FramePropertiesSideBar::slotChannelChanged( int channel ) +{ + switch( channel ) + { + case RedChannel: + histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + colorsCB->setEnabled( false ); + break; + case GreenChannel: + histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + colorsCB->setEnabled( false ); + break; + case BlueChannel: + histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + colorsCB->setEnabled( false ); + break; + case AlphaChannel: + histogramWidget->m_channelType = HistogramWidget::AlphaChannelHistogram; + colorsCB->setEnabled( false ); + break; + case ColorChannels: + histogramWidget->m_channelType = HistogramWidget::ColorChannelsHistogram; + colorsCB->setEnabled( true ); + break; + default: // Luminance. + histogramWidget->m_channelType = HistogramWidget::LumaHistogram; + colorsCB->setEnabled( false ); + break; + } + histogramWidget->update(); + updateStatistiques(); +} + +void FramePropertiesSideBar::slotScaleChanged( int scale ) +{ + histogramWidget->m_scaleType = scale; + histogramWidget->update(); +} + +void FramePropertiesSideBar::slotColorsChanged( int color ) +{ + switch( color ) + { + case AllColorsGreen: + histogramWidget->m_colorType = HistogramWidget::GreenColor; + break; + case AllColorsBlue: + histogramWidget->m_colorType = HistogramWidget::BlueColor; + break; + default: // Red. + histogramWidget->m_colorType = HistogramWidget::RedColor; + break; + } + + histogramWidget->update(); + updateStatistiques(); +} + +void FramePropertiesSideBar::slotRenderingChanged( int rendering ) +{ + histogramWidget->m_renderingType = rendering; + histogramWidget->update(); + updateStatistiques(); +} + +void FramePropertiesSideBar::slotMinValueChanged( int min ) +{ + // Called when user changes values of spin box. + // Communicate the change to histogram widget. + + // make the one control "push" the other + if( min == maxInterv->value() + 1 ) + maxInterv->setValue( min ); + + maxInterv->setMinimum( min - 1 ); + histogramWidget->slotMinValueChanged( min ); + updateStatistiques(); +} + +void FramePropertiesSideBar::slotMaxValueChanged( int max ) +{ + if( max == minInterv->value() - 1 ) + minInterv->setValue( max ); + + minInterv->setMaximum( max + 1 ); + histogramWidget->slotMaxValueChanged( max ); + updateStatistiques(); +} + +void FramePropertiesSideBar::slotUpdateInterval( int min, int max ) +{ + // Called when value is set from within histogram widget. + // Block signals to prevent slotMinValueChanged and + // slotMaxValueChanged being called. + minInterv->blockSignals( true ); + minInterv->setMaximum( max + 1 ); + minInterv->setValue( min ); + minInterv->blockSignals( false ); + + maxInterv->blockSignals( true ); + maxInterv->setMinimum( min - 1 ); + maxInterv->setValue( max ); + maxInterv->blockSignals( false ); + + updateStatistiques(); +} + +void FramePropertiesSideBar::slotUpdateIntervRange( int range ) +{ + maxInterv->blockSignals( true ); + maxInterv->setMaximum( range ); + maxInterv->blockSignals( false ); +} +//////////////////////////////////////////////////////////////////////////////// +// Informations +//////////////////////////////////////////////////////////////////////////////// + +void FramePropertiesSideBar::updateInformations() +{ +#if 0 + QString value; + switch( image.colorMode() ) + { + case SImage::BlackWhite: + value = tr( "Black and White" ); + break; + case SImage::Gray: + value = tr( "Grayscale" ); + break; + default: + value = tr( "Color" ); + break; + } + colorModeValueLabel->setText( value ); + value = tr( "%n bit(s)", "", image.depth() ); + depthValueLabel->setText( value ); + alphaValueLabel->setText( image.hasAlpha() ? tr( "Yes" ) : tr( "No" ) ); +#endif +} +//////////////////////////////////////////////////////////////////////////////// +// Statistiques +//////////////////////////////////////////////////////////////////////////////// + +void FramePropertiesSideBar::updateStatistiques() +{ + QString value; + + int min = minInterv->value(); + int max = maxInterv->value(); + int channel = channelCB->currentIndex(); + + if( channel == HistogramWidget::ColorChannelsHistogram ) + channel = colorsCB->currentIndex() + 1; + + PlaYUVerFrameStatistics *histogram; + + if( histogramWidget->m_renderingType == HistogramWidget::FullImageHistogram ) + histogram = histogramWidget->m_imageHistogram; + else + histogram = histogramWidget->m_selectionHistogram; + + double mean = histogram->getMean( channel, min, max ); + labelMeanValue->setText( value.setNum( mean, 'f', 1 ) ); + + double pixels = histogram->getPixels(); + labelPixelsValue->setText( value.setNum( ( float )pixels, 'f', 0 ) ); + + double stddev = histogram->getStdDev( channel, min, max ); + labelStdDevValue->setText( value.setNum( stddev, 'f', 1 ) ); + + double counts = histogram->getCount( channel, min, max ); + labelCountValue->setText( value.setNum( ( float )counts, 'f', 0 ) ); + + double median = histogram->getMedian( channel, min, max ); + labelMedianValue->setText( value.setNum( median, 'f', 1 ) ); + + double percentile = ( pixels > 0 ? ( 100.0 * counts / pixels ) : 0.0 ); + labelPercentileValue->setText( value.setNum( percentile, 'f', 1 ) ); +} + static inline QSize bestSize( QSize currSize ) { QSize bestSize( 180, currSize.height() ); diff --git a/source/app/PropertiesSidebar.h b/source/app/PropertiesSidebar.h index 57e9e5b..5cfd0f3 100755 --- a/source/app/PropertiesSidebar.h +++ b/source/app/PropertiesSidebar.h @@ -33,6 +33,7 @@ #endif #include "InputStream.h" #include "PlaYUVerFrame.h" +#include "HistogramWidget.h" namespace plaYUVer { @@ -69,16 +70,80 @@ public: QSize sizeHint() const; Void setData( PlaYUVerFrame* m_pcStream ); + Void setSelection( const QRect &selectionArea ); private: - PlaYUVerFrame *m_pcFrame; + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel, + AlphaChannel, + ColorChannels + }; + enum AllColorsColorType + { + AllColorsRed=0, + AllColorsGreen, + AllColorsBlue + }; + + PlaYUVerFrame *m_pcFrame; + PlaYUVerFrame *m_pcFrameSelection; + Bool inLoadingProcess; + + QComboBox *channelCB; + QComboBox *colorsCB; + QComboBox *renderingCB; + + QPushButton *linHistoButton; + QPushButton *logHistoButton; + QPushButton *fullImageButton; + QPushButton *selectionImageButton; + + QButtonGroup *scaleButtonGroup; + QButtonGroup *renderingButtonGroup; + + QSpinBox *minInterv; + QSpinBox *maxInterv; + + QLabel *colorsLabel; QLabel *labelMeanValue; QLabel *labelPixelsValue; QLabel *labelStdDevValue; QLabel *labelCountValue; QLabel *labelMedianValue; QLabel *labelPercentileValue; + QLabel *formatValueLabel; + QLabel *sizeValueLabel; + QLabel *colorSpaceValueLabel; + QLabel *colorModeValueLabel; + QLabel *depthValueLabel; + QLabel *alphaValueLabel; + + QRect m_cSelectionArea; + + HistogramWidget *histogramWidget; + +private: + void updateInformations(); + void updateStatistiques(); + +private Q_SLOTS: + void slotRefreshOptions( bool sixteenBit ); + void slotHistogramComputationFailed( void ); + void slotChannelChanged( int channel ); + void slotScaleChanged( int scale ); + void slotColorsChanged( int color ); + void slotRenderingChanged( int rendering ); + void slotMinValueChanged( int ); + void slotMaxValueChanged( int ); + + void slotUpdateInterval( int min, int max ); + void slotUpdateIntervRange( int range ); + }; } // NAMESPACE diff --git a/source/lib/PlaYUVerFrameStatistics.cpp b/source/lib/PlaYUVerFrameStatistics.cpp new file mode 100755 index 0000000..763e8aa --- /dev/null +++ b/source/lib/PlaYUVerFrameStatistics.cpp @@ -0,0 +1,578 @@ +/* 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 PlaYUVerFrameStatistics.cpp + * \brief Measure PlaYUVerFrame statistics + * Based on the work of Ricardo N. Rocha Sardo in SCode project + */ + +#include +#include +#include + +#include +#include "PlaYUVerFrameStatistics.h" + +namespace plaYUVer +{ + +//////////////////////////////////////////////////////////////////////////////// +// Private Data Class +//////////////////////////////////////////////////////////////////////////////// +class PlaYUVerFrameStatisticsPrivate +{ + +public: + /* + // Using a structure instead a class is more fast + // (access with memset() and bytes manipulation). + + struct double_packet + { + double luma; + double red; + double green; + double blue; + double alpha; + }; + */ +public: + + PlaYUVerFrameStatisticsPrivate() + { + parent = 0; + imageData = 0; + histogram = 0; + runningFlag = true; + } + + /** The histogram data.*/ + double *histogram; + + /** 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.*/ + uchar *imageData; + uint imageWidth; + uint imageHeight; + int imageChannels; + Int imageColorSpace; + + /** Numbers of histogram segments dependaing of image bytes depth*/ + int histoSegments; + + /** To post event from thread to parent.*/ + QObject *parent; + + /** Used to stop thread during calculations.*/ + bool runningFlag; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Constructors and Destructor +//////////////////////////////////////////////////////////////////////////////// + +PlaYUVerFrameStatistics::PlaYUVerFrameStatistics( const PlaYUVerFrame& image, QObject *parent, Options options ) : + QThread() +{ + setup( image.getQImageBuffer(), image.getWidth(), image.getHeight(), image.getBitsChannel(), image.getPelFormat(), parent, options ); +} + +PlaYUVerFrameStatistics::PlaYUVerFrameStatistics( uchar *data, uint width, uint height, int bitsPerChannel, Int pixel_format, QObject *parent, Options options ) : + QThread() +{ + setup( data, width, height, bitsPerChannel, pixel_format, parent, options ); +} + +PlaYUVerFrameStatistics::~PlaYUVerFrameStatistics() +{ + stopCalcHistogramValues(); + + if( d->histogram ) + delete[] d->histogram; + + delete d; +} + +//////////////////////////////////////////////////////////////////////////////// +// Setup Function +//////////////////////////////////////////////////////////////////////////////// +void PlaYUVerFrameStatistics::setup( uchar *data, uint width, uint height, int bitsPerChannel, Int pixel_format, QObject *parent, Options options ) +{ + d = new PlaYUVerFrameStatisticsPrivate; + d->imageData = data; + d->imageWidth = width; + d->imageHeight = height; + d->parent = parent; + d->histoSegments = ( bitsPerChannel == 16 ) ? 65536 : 256; + d->imageColorSpace = PlaYUVerFrame::isRGBorYUVorGray( pixel_format ); + + if( options == CalcLumaWhenRGB ) + d->calcLuma = true; + else + d->calcLuma = false; + + switch( d->imageColorSpace ) + { + case PlaYUVerFrame::COLOR_GRAY: + d->imageChannels = 1; + break; + case PlaYUVerFrame::COLOR_RGB: + case PlaYUVerFrame::COLOR_YUV: + d->imageChannels = 3; + break; + case PlaYUVerFrame::COLOR_ARGB: + d->imageChannels = 4; + break; + default: + d->imageChannels = 1; + } + + if( d->calcLuma && ( d->imageColorSpace == PlaYUVerFrame::COLOR_RGB ) ) + d->histoChannels = d->imageChannels + 1; + else + d->histoChannels = d->imageChannels; + + if( d->imageData && d->imageWidth && d->imageHeight ) + { + if( d->parent ) + start(); + else + run(); //Lukas - substitui calcHistogramValues por run() + } + else + { + if( d->parent ) + postProgress( false, false ); + } +} +//////////////////////////////////////////////////////////////////////////////// +// Accessing Properties +//////////////////////////////////////////////////////////////////////////////// +int PlaYUVerFrameStatistics::getHistogramSegment( void ) +{ + return d->histoSegments; +} + +int PlaYUVerFrameStatistics::getRealHistoChannel( int channel ) +{ + int histoChannel; + + switch( d->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 && d->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 && d->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 != YChannel && channel != CbChannel && channel != CrChannel ) + { + return -1; + } + else + { + if( channel == YChannel ) + histoChannel = 0; + + if( channel == CbChannel ) + histoChannel = 1; + + if( channel == CrChannel ) + histoChannel = 2; + } + break; + default: + histoChannel = -1; + } + return histoChannel; +} +//////////////////////////////////////////////////////////////////////////////// +// Progress Indicators +//////////////////////////////////////////////////////////////////////////////// +void PlaYUVerFrameStatistics::postProgress( bool starting, bool success ) +{ + EventData *eventData = new EventData(); + eventData->starting = starting; + eventData->success = success; + eventData->histogram = this; + QCoreApplication::postEvent( d->parent, eventData ); +} + +void PlaYUVerFrameStatistics::stopCalcHistogramValues( void ) +{ + d->runningFlag = false; + wait(); +} + +//////////////////////////////////////////////////////////////////////////////// +// List of threaded operations +//////////////////////////////////////////////////////////////////////////////// +void PlaYUVerFrameStatistics::run() +{ + calcHistogramValues(); +} + +void PlaYUVerFrameStatistics::calcHistogramValues() +{ + register uint i; + register int j; + + if( d->parent ) + postProgress( true, false ); + + d->histogram = new double[d->histoSegments * d->histoChannels]; + +// Initialize the histogram + memset( d->histogram, 0, d->histoSegments * d->histoChannels * sizeof(double) ); + + if( !d->histogram ) + { + qWarning( ) << "PlaYUVerFrameStatistics::calcHistogramValuess: " + "Unable to allocate memory!" + << endl; + + if( d->parent ) + postProgress( false, false ); + + return; + } + +// 16 bits image. + if( d->histoSegments == 65536 ) + { + unsigned short *data = ( unsigned short* )d->imageData; + unsigned short luma; + + for( i = 0; ( i < d->imageHeight * d->imageWidth ) && d->runningFlag; i++ ) + { + for( j = 0; j < d->imageChannels; j++ ) + { + d->histogram[data[j] + j * d->histoSegments]++; + } + + // Calc the luminosity when it's applicable + if( d->calcLuma && d->imageColorSpace == PlaYUVerFrame::COLOR_RGB ) + { + luma = ( unsigned short )( 0.299 * data[0] + 0.587 * data[1] + 0.114 * data[2] ); + d->histogram[luma + j * d->histoSegments]++; + } + if( d->calcLuma && d->imageColorSpace == PlaYUVerFrame::COLOR_ARGB ) + { + luma = ( unsigned short )( 0.299 * data[1] + 0.587 * data[2] + 0.114 * data[3] ); + d->histogram[luma + j * d->histoSegments]++; + } + + data += d->imageChannels; + } + } +// 8 bits images. + else + { + uchar *data = d->imageData; + uchar luma; + for( i = 0; ( i < d->imageHeight * d->imageWidth ) && d->runningFlag; i++ ) + { + for( j = 0; j < d->imageChannels; j++ ) + { + d->histogram[data[j] + j * d->histoSegments]++; + } + + // Calc the luminosity when it's applicable + if( d->calcLuma && d->imageColorSpace == PlaYUVerFrame::COLOR_RGB ) + { + luma = ( uchar )( 0.299 * data[0] + 0.587 * data[1] + 0.114 * data[2] ); + d->histogram[luma + j * d->histoSegments]++; + } + if( d->calcLuma && d->imageColorSpace == PlaYUVerFrame::COLOR_ARGB ) + { + luma = ( uchar )( 0.299 * data[1] + 0.587 * data[2] + 0.114 * data[3] ); + d->histogram[luma + j * d->histoSegments]++; + } + + data += d->imageChannels; + } + } + + if( d->parent && d->runningFlag ) + postProgress( false, true ); +} + +double PlaYUVerFrameStatistics::getCount( int channel, int start, int end ) +{ + int i; + int histoChannel; + int indexStart; + double count = 0.0; + + if( !d->histogram || start < 0 || end > d->histoSegments - 1 || start > end ) + { + return 0.0; + } + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * d->histoSegments; + + for( i = start; i <= end; i++ ) + { + count += d->histogram[indexStart + i]; + } + return count; +} + +double PlaYUVerFrameStatistics::getPixels() +{ + if( !d->histogram ) + { + return 0.0; + } + + return ( d->imageWidth * d->imageHeight ); +} + +double PlaYUVerFrameStatistics::getMean( int channel, int start, int end ) +{ + int i; + int indexStart; + int histoChannel; + double mean = 0.0; + double count; + + if( !d->histogram || start < 0 || end > d->histoSegments - 1 || start > end ) + { + return 0.0; + } + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * d->histoSegments; + + for( i = start; i <= end; i++ ) + { + mean += i * d->histogram[indexStart + i]; + } + + count = getCount( channel, start, end ); + + if( count > 0.0 ) + { + return mean / count; + } + + return mean; +} + +int PlaYUVerFrameStatistics::getMedian( int channel, int start, int end ) +{ + int i; + int histoChannel; + int indexStart; + double sum = 0.0; + double count; + + if( !d->histogram || start < 0 || end > d->histoSegments - 1 || start > end ) + { + return 0; + } + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0; + } + + indexStart = histoChannel * d->histoSegments; + + count = getCount( channel, start, end ); + + for( i = start; i <= end; i++ ) + { + sum += d->histogram[indexStart + i]; + if( sum * 2 > count ) + return i; + } + + return 0; +} + +double PlaYUVerFrameStatistics::getStdDev( int channel, int start, int end ) +{ + int i; + int histoChannel; + int indexStart; + double dev = 0.0; + double count; + double mean; + + if( !d->histogram || start < 0 || end > d->histoSegments - 1 || start > end ) + { + return 0.0; + } + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * d->histoSegments; + 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 ) * d->histogram[indexStart +i]; + } + + return sqrt( dev / count ); + + -----------------------*/ + + for( i = start; i <= end; i++ ) + { + dev += ( i * i ) * d->histogram[indexStart + i]; + } + + return sqrt( ( dev - count * mean * mean ) / ( count - 1 ) ); + +} + +double PlaYUVerFrameStatistics::getValue( int channel, int bin ) +{ + double value; + int histoChannel; + int indexStart; + + if( !d->histogram || bin < 0 || bin > d->histoSegments - 1 ) + return 0.0; + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * d->histoSegments; + + value = d->histogram[indexStart + bin]; + + return value; +} + +double PlaYUVerFrameStatistics::getMaximum( int channel ) +{ + double max = 0.0; + int x; + int histoChannel; + int indexStart; + + if( !d->histogram ) + return 0.0; + + histoChannel = getRealHistoChannel( channel ); + + if( histoChannel == -1 ) + { + return 0.0; + } + + indexStart = histoChannel * d->histoSegments; + + for( x = 0; x < d->histoSegments; x++ ) + { + if( d->histogram[indexStart + x] > max ) + { + max = d->histogram[indexStart + x]; + } + } + + return max; +} + +} // NAMESPACE + diff --git a/source/lib/PlaYUVerFrameStatistics.h b/source/lib/PlaYUVerFrameStatistics.h new file mode 100755 index 0000000..71c8afe --- /dev/null +++ b/source/lib/PlaYUVerFrameStatistics.h @@ -0,0 +1,135 @@ +/* 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 PlaYUVerFrameStatistics.h + * \brief Measure PlaYUVerFrame statistics + * Based on the work of Ricardo N. Rocha Sardo in SCode project + */ + +#ifndef __PLAYUVERFRAMESTATISTICS_H_ +#define __PLAYUVERFRAMESTATISTICS_H_ + +#include +#include "PlaYUVerFrame.h" + +class QObject; + +namespace plaYUVer +{ + +class PlaYUVerFrameStatisticsPrivate; + +/** + * Esta classe permite obter o histograma de uma imagem assim como algumas + * estatísticas. + */ +class PlaYUVerFrameStatistics: public QThread +{ + +public: + + enum StatisticsChannel + { + LumaChannel = 0, + RedChannel, + GreenChannel, + BlueChannel, + AlphaChannel, + YChannel, + CbChannel, + CrChannel + }; + + enum Options + { + CalcLumaWhenRGB = 0 + }; + + /** + * Custom Event + */ + class EventData: public QEvent + { + + public: + + EventData() : + QEvent( QEvent::User ) + { + starting = false; + success = false; + histogram = 0; + } + + bool starting; + bool success; + PlaYUVerFrameStatistics *histogram; + }; + +public: + + PlaYUVerFrameStatistics( uchar *data, uint width, uint height, int bitsPerChannel, Int pixel_format, QObject *parent = 0, Options options = CalcLumaWhenRGB ); + + PlaYUVerFrameStatistics( const PlaYUVerFrame& image, QObject *parent = 0, Options options = CalcLumaWhenRGB ); + + ~PlaYUVerFrameStatistics(); + + void setup( uchar *data, uint width, uint height, int bitsPerChannel, Int pixel_format, QObject *parent = 0, Options options = CalcLumaWhenRGB ); + + /** Method to stop threaded computations.*/ + void stopCalcHistogramValues( void ); + + // Methods for to manipulate the histogram data. + + double getCount( int channel, int start, int end ); + double getMean( int channel, int start, int end ); + /** + * @return Number of image pixels + */ + double getPixels(); + double getStdDev( int channel, int start, int end ); + double getValue( int channel, int bin ); + double getMaximum( int channel ); + + /** + * @return Number of histogram segments: 256 or 65536 for 8 or 16 bits per + * sample. + */ + int getHistogramSegment( void ); + int getMedian( int channel, int start, int end ); + +private: + + PlaYUVerFrameStatisticsPrivate *d; + +private: + + int getRealHistoChannel( int channel ); + void calcHistogramValues(); + void postProgress( bool starting, bool success ); + +protected: + + virtual void run(); +}; + +} // NAMESPACE + +#endif // __PLAYUVERFRAMESTATISTICS_H_