From 5be653bc3aa28d35bd0db4490f9e85d1afcbed4d Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 Jan 2009 21:02:01 +0000 Subject: [PATCH] Basic elevation optimisation channel --- NOTES | 23 +++ geo/CMakeLists.txt | 2 + geo/tcElevation.cpp | 2 +- geo/tcElevationOptimization.cpp | 408 ++++++++++++++++++++++++++++++++++++++++ geo/tcElevationOptimization.h | 145 ++++++++++++++ geo/tcGeo.h | 4 + geo/tcLandsatData.cpp | 6 + geo/tcPixelData.cpp | 8 +- geo/tcPixelData.h | 108 ++++++++--- geo/tcPixelOp.cpp | 119 ++++++++++++ geo/tcPixelOp.h | 84 +++++++++ geo/tcShadowDepth.cpp | 1 + 12 files changed, 883 insertions(+), 27 deletions(-) create mode 100644 geo/tcElevationOptimization.cpp create mode 100644 geo/tcElevationOptimization.h create mode 100644 geo/tcPixelOp.cpp create mode 100644 geo/tcPixelOp.h diff --git a/NOTES b/NOTES index 36b261c..5d928e0 100644 --- a/NOTES +++ b/NOTES @@ -230,6 +230,29 @@ Monday 26th Jan if in shadow: distance to and current elevation at shadow entrance and exit in light direction +Tue 27th Jan + now we have shadow depth channel in each direction + new channel to optimise elevation + must be able to take multiple datasets + also adjusts corrected elevation values in DEM + inputs + previous elevation (internal) + shadow classification, shadow depths + +Thur 29th Jan + We really need to know distances accurately + coordinate spaces + geographical + can calculate short distances easily, given radius + can convert to texture space + texture space + can convert to geographical + can't directly calculate distances + ideally need to optimise in a geographical texture space + resample shadow + 19:13 - tried with roughness, variance, lit facing and travision elev + hard to get coefficients right so that it does the smoothing + fix list [ ] seems - not important [ ] when creating texture for region, don't up sample, select larger area diff --git a/geo/CMakeLists.txt b/geo/CMakeLists.txt index 2ea991c..a5be968 100644 --- a/geo/CMakeLists.txt +++ b/geo/CMakeLists.txt @@ -27,10 +27,12 @@ set(tecorrec_geo_SRCS tcIlluminantDiscontinuity.cpp tcShadowClassification.cpp tcNegativeProduct.cpp + tcPixelOp.cpp tcShadowDepth.cpp tcLambertianShading.cpp tcRaytracedShadowMap.cpp tcElevation.cpp + tcElevationOptimization.cpp tcGeoImageData.cpp tcSensor.cpp tcSpectrum.cpp diff --git a/geo/tcElevation.cpp b/geo/tcElevation.cpp index ec6123c..8b6cd6d 100644 --- a/geo/tcElevation.cpp +++ b/geo/tcElevation.cpp @@ -74,7 +74,7 @@ tcAbstractPixelData* tcElevation::loadPortion(double x1, double y1, double x2, d // Get some elevation data bool accurate; float altitude = altitudeAt(geoCoord, false, &accurate); - if (!accurate) + if (false && !accurate) { altitude = 0.0f; } diff --git a/geo/tcElevationOptimization.cpp b/geo/tcElevationOptimization.cpp new file mode 100644 index 0000000..c07a9f9 --- /dev/null +++ b/geo/tcElevationOptimization.cpp @@ -0,0 +1,408 @@ +/*************************************************************************** + * This file is part of Tecorrec. * + * Copyright 2008 James Hogan * + * * + * Tecorrec 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. * + * * + * Tecorrec 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 Tecorrec. If not, write to the Free Software Foundation, * + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +/** + * @file tcElevationOptimization.cpp + * @brief Optimized elevation. + */ + +#include "tcElevationOptimization.h" + +#include + +/* + * Constructors + destructor + */ + +/// Primary constructor. +tcElevationOptimization::tcElevationOptimization(tcChannel* shadowChannel, tcSrtmModel* dem, tcGeoImageData* imagery) +: tcChannelDem(dem, imagery, + tr("Elevation Optimization"), + tr("Optimizes elevation using image data.")) +, m_shadowChannel(shadowChannel) +, m_shadowData(0) +, m_elevationData(0) +, m_data(0) +{ + m_shadowChannel->addDerivitive(this); +} + +/// Destructor. +tcElevationOptimization::~tcElevationOptimization() +{ + m_shadowChannel->removeDerivitive(this); +} + +/* + * Interface for derived class to implement + */ + +void tcElevationOptimization::roundPortion(double* x1, double* y1, double* x2, double* y2) +{ + m_shadowChannel->roundPortion(x1,y1,x2,y2); +} + +tcAbstractPixelData* tcElevationOptimization::loadPortion(double x1, double y1, double x2, double y2) +{ + Reference channelData = m_shadowChannel->loadPortion(x1, y1, x2, y2); + int width = channelData->width(); + int height = channelData->height(); + m_shadowData = dynamicCast*>(channelData); + if (0 == m_shadowData) + { + return 0; + } + + // Create a new pixel buffer, initialised to altitude + tcPixelData* data = new tcPixelData(width, height); + tcTypedPixelData cacheData(width, height); + + m_elevationData = data; + m_data = &cacheData; + + int maxWidthHeight = qMax(width, height); + double minXY12 = qMin(x2-x1, y2-y1); + + startProcessing("Caching elevation characteristics"); + for (int j = 0; j < height; ++j) + { + progress((float)j/(height-1)); + for (int i = 0; i < width; ++i) + { + int index = j*width + i; + // Transform coordinates + maths::Vector<2,float> coord ( x1 + (x2-x1)*i / (width - 1), + 1.0f - y1 - (y2-y1)*j / (height - 1)); + tcGeo geoCoord = geoAt(coord); + maths::Vector<3,float> light = lightDirectionAt(geoCoord); + PerPixelCache& cache = cacheData.buffer()[index]; + cache.light = light; + + // Find resolution + maths::Vector<2,float> coordx ( x1 + (x2-x1)*(i+1) / (width - 1), + 1.0f - y1 - (y2-y1)*j / (height - 1)); + maths::Vector<2,float> coordy ( x1 + (x2-x1)*i / (width - 1), + 1.0f - y1 - (y2-y1)*(j+1) / (height - 1)); + tcGeo geoCoordx = geoAt(coordx) - geoCoord; + tcGeo geoCoordy = geoAt(coordy) - geoCoord; + // Find approx + cache.resolution[0] = geoCoordx.angle(); + cache.resolution[1] = geoCoordy.angle(); + cache.resolution *= 6378.137e3; + + // Get some elevation data + bool accurate; + float altitude = altitudeAt(geoCoord, false, &accurate); + data->buffer()[index] = altitude; + cache.originalElevation = altitude; + cache.reliability = accurate ? 1 : 0; + + maths::Vector<3,float> lightScaled = light/(maxWidthHeight-1)*minXY12; + tcGeo offset(geoAt(coord + lightScaled.slice<0,2>())); + for (int depthDirection = 0; depthDirection < 2; ++depthDirection) + { + tcGeo pos = geoCoord; + tcGeo delta; + if (depthDirection == TransitionEntrance) + { + delta = offset - geoCoord; + } + else + { + delta = geoCoord - offset; + } + pos = pos + delta; + maths::Vector<2,float> tex = coord; + cache.shadowTransition[depthDirection][0] = i; + cache.shadowTransition[depthDirection][1] = j; + while (tex[0] >= x1 && tex[0] <= x2 && tex[1] >= 1.0f-y2 && tex[1] <= 1.0f-y1) + { + if (channelData->sampleFloat((tex[0]-x1)/(x2-x1), (1.0f-tex[1]-y1)/(y2-y1)) > 0.5f) + { + break; + } + cache.shadowTransition[depthDirection][0] = 0.5f + ( tex[0]-x1)/(x2-x1)*(width -1); + cache.shadowTransition[depthDirection][1] = 0.5f + (1.0f-tex[1]-y1)/(y2-y1)*(height-1); + pos = pos + delta; + tex = textureAt(pos); + } + cache.shadowTransitionDistance[depthDirection] = (pos-geoCoord).angle()*6378.137e3; + } + } + } + + // Now start optimising + startProcessing("Optimizing elevation data"); + const int passes = 200; + for (int i = 0; i < passes; ++i) + { + progress((float)i/passes); + m_weights.variance = 0.001f; + m_weights.roughness = 0.5f*i/passes; + m_weights.litFacing = 30.0f; + m_weights.transitionElev = 30.0f; + optimizeElevations(0, 0, width-1, height-1, 1.0f); + } + + // Downscale the elevation for viewing + startProcessing("Downscaling for viewing"); + for (int j = 0; j < height; ++j) + { + progress((float)j/(height-1)); + for (int i = 0; i < width; ++i) + { + int index = j*width + i; + data->buffer()[index] /= 4000.0f; + } + } + + endProcessing(); + + m_elevationData = 0; + m_data = 0; + + return data; +} + +template +T MySqr(T t) +{ + return t*t; +} + +/* + * Private functions + */ + +/// Calculate the cost for a set of pixels. +float tcElevationOptimization::cost(int x1, int y1, int x2, int y2) const +{ + int width = m_elevationData->width(); + int height = m_elevationData->height(); + if (x1 < 0) + { + x1 = 0; + } + if (x2 >= width) + { + x2 = width-1; + } + if (y1 < 0) + { + y1 = 0; + } + if (y2 >= height) + { + y2 = height-1; + } + + int maxWidthHeight = qMax(width, height); + //double minXY12 = qMin(m_window.x2-m_window.x1, m_window.y2-m_window.y1); + + float costVariance = 0.0f; + int countVariance = 0; + float costRoughness = 0.0f; + int countRoughness = 0; + float costLitFacing = 0.0f; + int countLitFacing = 0; + float costTransitionElev = 0.0f; + int countTransitionElev = 0; + for (int j = y1; j <= y2; ++j) + { + for (int i = x1; i <= x2; ++i) + { + float* elevationPtr = pixelElevation(i, j); + if (0 == elevationPtr) + { + continue; + } + + int index = j*width + i; + + // Basic data retrieval + float elevation = *elevationPtr; + PerPixelCache* cache = &m_data->buffer()[index]; + bool isShadowed = (m_shadowData->buffer()[index] < 0.5f); + bool isShadowEntrance = isShadowed && + cache->shadowTransition[TransitionEntrance][0] == i && + cache->shadowTransition[TransitionEntrance][1] == j; + bool isShadowExit = isShadowed && + cache->shadowTransition[TransitionExit][0] == i && + cache->shadowTransition[TransitionExit][1] == j; + + // Gradient + // Derivitive of gradient + float d2h_dx2 = 0.0f; + float d2h_dy2 = 0.0f; + if (i > 0 && i < width-1) + { + float hp1 = m_elevationData->buffer()[index+1]; + float hm1 = m_elevationData->buffer()[index-1]; + d2h_dx2 = (hp1+hm1)/2 - elevation; + } + if (j > 0 && j < height-1) + { + float hp1 = m_elevationData->buffer()[index+width]; + float hm1 = m_elevationData->buffer()[index-width]; + d2h_dy2 = (hp1+hm1)/2 - elevation; + } + + // Calculate measures relating to the light direction + // Gradient + float hwm1 = m_elevationData->sampleFloat(((float)i - cache->light[0])/(width-1), + ((float)j - cache->light[1])/(height-1)); + float hwp1 = m_elevationData->sampleFloat(((float)i + cache->light[0])/(width-1), + ((float)j + cache->light[1])/(height-1)); + /// @todo Ensure units are right (this is in terms of h metres, w pixels) + float dh_dw = (hwm1 - hwp1)/2 / cache->resolution[0]; // 30 metre resolution? + float d2h_dw2 = (hwm1+hwp1)/2 - elevation; + + // Minimise variance from original value + costVariance += cache->reliability * (elevation-cache->originalElevation)*(elevation-cache->originalElevation); + ++countVariance; + + // Minimise roughness + costRoughness += (d2h_dx2*d2h_dx2 + d2h_dy2*d2h_dy2); + ++countRoughness; + + if (!isShadowed) + { + float facingness = -dh_dw-cache->light[0]; + if (facingness < 0.0f) + { + // Lit pixels must face light + costLitFacing += facingness*facingness; + ++countLitFacing; + } + } + if (isShadowEntrance) + { + if (cache->shadowTransition[TransitionExit][0] >= 0 && + cache->shadowTransition[TransitionExit][0] < width && + cache->shadowTransition[TransitionExit][1] >= 0 && + cache->shadowTransition[TransitionExit][1] < height) + { + int exitIndex = width*cache->shadowTransition[TransitionExit][1] + cache->shadowTransition[TransitionExit][0]; + float exitElevation = m_elevationData->buffer()[exitIndex]; + costTransitionElev += MySqr(exitElevation + cache->shadowTransitionDistance[TransitionExit]*cache->light[2] - elevation); + ++countTransitionElev; + } + } + if (0 && isShadowExit) + { + if (cache->shadowTransition[TransitionEntrance][0] >= 0 && + cache->shadowTransition[TransitionEntrance][0] < width && + cache->shadowTransition[TransitionEntrance][1] >= 0 && + cache->shadowTransition[TransitionEntrance][1] < height) + { + int entranceIndex = width*cache->shadowTransition[TransitionEntrance][1] + cache->shadowTransition[TransitionEntrance][0]; + float entranceElevation = m_elevationData->buffer()[entranceIndex]; + costTransitionElev += MySqr(entranceElevation - cache->shadowTransitionDistance[TransitionEntrance]*cache->light[2] - elevation); + ++countTransitionElev; + } + } + } + } + + return m_weights.variance*costVariance//countVariance + + m_weights.roughness*costRoughness//countRoughness + + m_weights.litFacing*costLitFacing//countLitFacing + + m_weights.transitionElev*costTransitionElev//countTransitionElev + ; +} + +/// Calculate the relative cost of changing a pixel's elevation. +float tcElevationOptimization::costChange(int x, int y, float dh) +{ + float* elev = pixelElevation(x, y); + if (0 == elev) + { + return 0; + } + + // Assume a single change in elevation only affects cost of pixels to effectiveRange from it. + // Now technically this doesn't hold for shadow edges as all pixels across the shadow can be affected. + // If the pixel is a shadow edge, take into account the cost of extra pixels. + static const int effectiveRange = 1; + float currentCost = cost(x-effectiveRange, y-effectiveRange, x+effectiveRange, y+effectiveRange); + float oldElevation = *elev; + *elev += dh; + float newCost = cost(x-effectiveRange, y-effectiveRange, x+effectiveRange, y+effectiveRange); + *elev = oldElevation; + return newCost - currentCost; +} + +/// Optimize a whole range of elevations. +void tcElevationOptimization::optimizeElevations(int x1, int y1, int x2, int y2, float dh) +{ + for (int j = y1; j <= y2; ++j) + { + for (int i = x1; i <= x2; ++i) + { + int index = j*m_data->width() + i; + if (m_data->buffer()[index].reliability == 1) + { + continue; + } + // Find the cost of adjusting the elevation by dh in each direction + float costInc = costChange(i,j, dh); + float costDec = costChange(i,j,-dh); + if (costInc < 0.0f && costInc < costDec) + { + adjustElevation(i,j,dh,true); + } + else if (costDec < 0.0f) + { + adjustElevation(i,j,-dh,true); + } + } + } +} + +/// Get pointer to elevation at a pixel. +float* tcElevationOptimization::pixelElevation(int x, int y) const +{ + if (x < 0 || x >= m_elevationData->width() || + y < 0 || y >= m_elevationData->height()) + { + return 0; + } + int index = y*m_elevationData->width() + x; + return &(m_elevationData->buffer()[index]); +} + +/// Adjust elevation at a pixel. +float tcElevationOptimization::adjustElevation(int x, int y, float dh, bool relative) +{ + float* elev = pixelElevation(x,y); + if (0 != elev) + { + float old = *elev; + if (relative) + { + dh += old; + } + *elev = dh; + return old; + } + else + { + return 0.0f; + } +} diff --git a/geo/tcElevationOptimization.h b/geo/tcElevationOptimization.h new file mode 100644 index 0000000..ec1093b --- /dev/null +++ b/geo/tcElevationOptimization.h @@ -0,0 +1,145 @@ +/*************************************************************************** + * This file is part of Tecorrec. * + * Copyright 2008 James Hogan * + * * + * Tecorrec 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. * + * * + * Tecorrec 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 Tecorrec. If not, write to the Free Software Foundation, * + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef _tcElevationOptimization_h_ +#define _tcElevationOptimization_h_ + +/** + * @file tcElevationOptimization.h + * @brief Optimized elevation. + */ + +#include "tcChannelDem.h" + +/// Optimized elevation. +class tcElevationOptimization : public tcChannelDem +{ + public: + + /* + * Constructors + destructor + */ + + /// Primary constructor. + tcElevationOptimization(tcChannel* shadowChannel, tcSrtmModel* dem, tcGeoImageData* imagery); + + /// Destructor. + virtual ~tcElevationOptimization(); + + protected: + + /* + * Interface for derived class to implement + */ + + // Reimplemented + virtual void roundPortion(double* x1, double* y1, double* x2, double* y2); + + // Reimplemented + virtual tcAbstractPixelData* loadPortion(double x1, double y1, double x2, double y2); + + private: + + /* + * Private functions + */ + + /** Calculate the cost for a set of pixels. + * @param x1 Starting horizontal pixel (left). + * @param y1 Starting vertical pixel (top). + * @param x2 Ending horizontal pixel (right). + * @param y2 Ending horizontal pixel (bottom). + * @return Cost of the chosen pixels. + */ + float cost(int x1, int y1, int x2, int y2) const; + + /** Calculate the relative cost of changing a pixel's elevation. + * @param x Horizontal coordinate of pixel to change. + * @param y Vertical coordinate of pixel to change. + * @param dh Relative change in elevation. + * @return Relative cost of altering the elevation of the specified pixel. + */ + float costChange(int x, int y, float dh); + + /// Optimize a whole range of elevations. + void optimizeElevations(int x1, int y1, int x2, int y2, float dh); + + /// Get pointer to elevation at a pixel. + float* pixelElevation(int x, int y) const; + + /// Adjust elevation at a pixel. + float adjustElevation(int x, int y, float dh, bool relative = false); + + /* + * Types + */ + + /// Shadow transition types. + enum TransitionType { + TransitionEntrance = 0, + TransitionExit = 1 + }; + + /// Per-pixel data to cache. + struct PerPixelCache + { + /// Coordinates of shadow entrance and exit. + maths::Vector<2,int> shadowTransition[2]; + /// Resolution. + maths::Vector<2,float> resolution; + /// Light direction. + maths::Vector<3,float> light; + /// How lit pixel is. + float lit; + /// Original elevation. + float originalElevation; + /// Distance to shadow entrance and exit. + float shadowTransitionDistance[2]; + /// Reliability of original elevation. + unsigned char reliability; + }; + + /* + * Variables + */ + + /// Shadow channel. + tcChannel* m_shadowChannel; + + /// Temporary shadow data. + tcPixelData* m_shadowData; + + /// Temporary elevation data. + tcPixelData* m_elevationData; + + /// Temporary per-pixel data. + tcTypedPixelData* m_data; + + /// Weights. + struct + { + float variance; + float roughness; + float litFacing; + float transitionElev; + } m_weights; + +}; + +#endif diff --git a/geo/tcGeo.h b/geo/tcGeo.h index 1f4fbc9..cc6cbdf 100644 --- a/geo/tcGeo.h +++ b/geo/tcGeo.h @@ -264,6 +264,10 @@ class tcGeo return tcGeo(m_longitude * other[0], m_latitude * other[1]); } + float angle() const + { + return sqrt(m_longitude*m_longitude + m_latitude*m_latitude); + } private: diff --git a/geo/tcLandsatData.cpp b/geo/tcLandsatData.cpp index 9a87bbf..7eb42ec 100644 --- a/geo/tcLandsatData.cpp +++ b/geo/tcLandsatData.cpp @@ -35,7 +35,9 @@ #include "tcLambertianShading.h" #include "tcRaytracedShadowMap.h" #include "tcElevation.h" +#include "tcElevationOptimization.h" #include "tcShadowDepth.h" +#include "tcPixelOp.h" #include #include @@ -164,6 +166,10 @@ tcLandsatData::tcLandsatData(const QString& path, tcSrtmModel* dem) channelManager()->addChannel(raytrace); tcElevation* elevation = new tcElevation(channelManager()->channel(0), dem, this); channelManager()->addChannel(elevation); + tcElevationOptimization* elevationOpt = new tcElevationOptimization(np, dem, this); + channelManager()->addChannel(elevationOpt); + tcPixelOp* elevDiff = new tcPixelOp(tcPixelOp::Diff, QList() << elevationOpt << elevation); + channelManager()->addChannel(elevDiff); /* QList chromaticities; diff --git a/geo/tcPixelData.cpp b/geo/tcPixelData.cpp index d8a847e..25be0d4 100644 --- a/geo/tcPixelData.cpp +++ b/geo/tcPixelData.cpp @@ -98,14 +98,14 @@ template <> GLuint tcPixelData::loadTexture() { GLuint textureId = 0; - if (0 != m_data) + if (0 != buffer()) { glGenTextures(1, &textureId); GLint binding = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D, &binding); glBindTexture(GL_TEXTURE_2D, textureId); if (0 == gluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE, m_size[0], m_size[1], - GL_LUMINANCE, GL_UNSIGNED_BYTE, m_data)) + GL_LUMINANCE, GL_UNSIGNED_BYTE, buffer())) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -125,14 +125,14 @@ template <> GLuint tcPixelData::loadTexture() { GLuint textureId = 0; - if (0 != m_data) + if (0 != buffer()) { glGenTextures(1, &textureId); GLint binding = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D, &binding); glBindTexture(GL_TEXTURE_2D, textureId); if (0 == gluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE, m_size[0], m_size[1], - GL_LUMINANCE, GL_FLOAT, m_data)) + GL_LUMINANCE, GL_FLOAT, buffer())) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); diff --git a/geo/tcPixelData.h b/geo/tcPixelData.h index 7c04603..ce59925 100644 --- a/geo/tcPixelData.h +++ b/geo/tcPixelData.h @@ -72,10 +72,16 @@ class tcAbstractPixelData : public ReferenceCounted void discardTexture(); /// Sample a byte value from the image. - virtual GLubyte sampleUByte(float x, float y) const = 0; + virtual GLubyte sampleUByte(float x, float y) const + { + return 0; + } /// Sample a floating point value from the image. - virtual float sampleFloat(float x, float y) const = 0; + virtual float sampleFloat(float x, float y) const + { + return 0.0f; + } protected: @@ -99,9 +105,9 @@ class tcAbstractPixelData : public ReferenceCounted GLuint m_textureId; }; -/// A block of pixel data. +/// A block of fairly abstract but typed pixel data. template -class tcPixelData : public tcAbstractPixelData +class tcTypedPixelData : public tcAbstractPixelData { public: @@ -110,21 +116,21 @@ class tcPixelData : public tcAbstractPixelData */ /// Default constructor. - tcPixelData() + tcTypedPixelData() : tcAbstractPixelData(0, 0) , m_data(0) { } /// Primary constructor. - tcPixelData(int width, int height) + tcTypedPixelData(int width, int height) : tcAbstractPixelData(width, height) , m_data(new T[width*height]) { } /// Destructor. - virtual ~tcPixelData() + virtual ~tcTypedPixelData() { delete [] m_data; } @@ -139,14 +145,60 @@ class tcPixelData : public tcAbstractPixelData return m_data; } + private: + + /* + * Variables + */ + + /// Parent pixel data into which we are looking. + T* m_data; +}; + +/// A block of pixel data. +template +class tcPixelData : public tcTypedPixelData +{ + private: + + /* + * Types + */ + + // Parent type. + typedef tcTypedPixelData Parent; + + public: + + /* + * Constructors + destructor + */ + + /// Default constructor. + tcPixelData() + : Parent() + { + } + + /// Primary constructor. + tcPixelData(int width, int height) + : Parent(width, height) + { + } + + /// Destructor. + virtual ~tcPixelData() + { + } + + /* + * Accessors + */ + // Reimplemented virtual GLubyte sampleUByte(float x, float y) const { - if (x < 0.0) x = 0.0; - if (x > 1.0) x = 1.0; - if (y < 0.0) y = 0.0; - if (y > 1.0) y = 1.0; - return (GLubyte)m_data[(int)(y*(height()-1) + 0.5f)*width() + (int)(x*(width()-1) + 0.5f)]; + return (GLubyte)tcPixelData::sampleFloat(x,y); } // Reimplemented @@ -156,7 +208,28 @@ class tcPixelData : public tcAbstractPixelData if (x > 1.0) x = 1.0; if (y < 0.0) y = 0.0; if (y > 1.0) y = 1.0; - return (float)m_data[(int)(y*(height()-1) + 0.5f)*width() + (int)(x*(width()-1) + 0.5f)]; + + float dx = x*(Parent::width()-1); + float dy = y*(Parent::height()-1); + int xi = (int)dx; + int yi = (int)dy; + dx -= xi; + dy -= yi; + if (xi >= Parent::width()-1) + { + --xi; + dx = 1.0f; + } + if (yi >= Parent::height()-1) + { + --yi; + dy = 1.0f; + } + + return (float)Parent::buffer()[yi*Parent::width() + xi] * (1.0f - dx) * (1.0f - dy) + + (float)Parent::buffer()[yi*Parent::width() + xi+1] * dx * (1.0f - dy) + + (float)Parent::buffer()[(yi+1)*Parent::width() + xi] * (1.0f - dx) * dy + + (float)Parent::buffer()[(yi+1)*Parent::width() + xi+1] * dx * dy; } protected: @@ -167,15 +240,6 @@ class tcPixelData : public tcAbstractPixelData // Reimplemented virtual GLuint loadTexture(); - - private: - - /* - * Variables - */ - - /// Parent pixel data into which we are looking. - T* m_data; }; #endif diff --git a/geo/tcPixelOp.cpp b/geo/tcPixelOp.cpp new file mode 100644 index 0000000..34a40fb --- /dev/null +++ b/geo/tcPixelOp.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + * This file is part of Tecorrec. * + * Copyright 2008 James Hogan * + * * + * Tecorrec 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. * + * * + * Tecorrec 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 Tecorrec. If not, write to the Free Software Foundation, * + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +/** + * @file tcPixelOp.cpp + * @brief Performs a pixel operation on a set of input channels. + */ + +#include "tcPixelOp.h" + +/* + * Constructors + destructor + */ + +/// Primary constructor. +tcPixelOp::tcPixelOp(Op op, const QList& inputChannels) +: tcChannel(tr("Pixel Operation"), + tr("Some pixel operation")) +, m_inputChannels(inputChannels) +, m_op(op) +{ + Q_ASSERT(m_inputChannels.size() > 0); + for (int c = 0; c < m_inputChannels.size(); ++c) + { + m_inputChannels[c]->addDerivitive(this); + } +} + +/// Destructor. +tcPixelOp::~tcPixelOp() +{ + foreach (tcChannel* channel, m_inputChannels) + { + channel->removeDerivitive(this); + } +} + +/* + * Interface for derived class to implement + */ + +void tcPixelOp::roundPortion(double* x1, double* y1, double* x2, double* y2) +{ + m_inputChannels.first()->roundPortion(x1, y1, x2, y2); +} + +tcAbstractPixelData* tcPixelOp::loadPortion(double x1, double y1, double x2, double y2) +{ + tcPixelData* data = 0; + int width = 0; + int height = 0; + switch (m_op) + { + case Add: + case Subtract: + case Diff: + { + for (int c = 0; c < m_inputChannels.size(); ++c) + { + tcChannel* channel = m_inputChannels[c]; + Reference > channelData = dynamicCast*>(channel->portion(x1, y1, x2, y2)); + if (0 == c) + { + width = channelData->width(); + height = channelData->height(); + data = new tcPixelData(width, height); + memcpy(data->buffer(), channelData->buffer(), sizeof(float)*width*height); + } + else + { + if (m_op == Add) + { + for (int i = 0; i < width*height; ++i) + { + data->buffer()[i] += channelData->buffer()[i]; + } + } + else if (m_op == Subtract) + { + for (int i = 0; i < width*height; ++i) + { + data->buffer()[i] -= channelData->buffer()[i]; + } + } + else if (m_op == Diff) + { + for (int i = 0; i < width*height; ++i) + { + data->buffer()[i] += 0.5f - channelData->buffer()[i]; + } + break; + } + } + } + break; + } + default: + { + break; + } + } + return data; +} diff --git a/geo/tcPixelOp.h b/geo/tcPixelOp.h new file mode 100644 index 0000000..c727f51 --- /dev/null +++ b/geo/tcPixelOp.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * This file is part of Tecorrec. * + * Copyright 2008 James Hogan * + * * + * Tecorrec 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. * + * * + * Tecorrec 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 Tecorrec. If not, write to the Free Software Foundation, * + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef _tcPixelOp_h_ +#define _tcPixelOp_h_ + +/** + * @file tcPixelOp.h + * @brief Performs a pixel operation on a set of input channels. + */ + +#include "tcChannel.h" + +#include + +/// Performs a pixel operation on a set of input channels. +class tcPixelOp : public tcChannel +{ + public: + + /* + * Types + */ + + /// Operations + enum Op + { + Add, + Subtract, + Diff + }; + + /* + * Constructors + destructor + */ + + /// Primary constructor. + tcPixelOp(Op op, const QList& inputChannels); + + /// Destructor. + virtual ~tcPixelOp(); + + protected: + + /* + * Interface for derived class to implement + */ + + // Reimplemented + virtual void roundPortion(double* x1, double* y1, double* x2, double* y2); + + // Reimplemented + virtual tcAbstractPixelData* loadPortion(double x1, double y1, double x2, double y2); + + private: + + /* + * Variables + */ + + /// Input channels. + QList m_inputChannels; + + /// Operation. + Op m_op; +}; + +#endif diff --git a/geo/tcShadowDepth.cpp b/geo/tcShadowDepth.cpp index 438050c..a043e2d 100644 --- a/geo/tcShadowDepth.cpp +++ b/geo/tcShadowDepth.cpp @@ -87,6 +87,7 @@ tcAbstractPixelData* tcShadowDepth::loadPortion(double x1, double y1, double x2, if (litData != 0) for (int j = 0; j < height; ++j) { + progress((float)j/(height-1)); for (int i = 0; i < width; ++i) { //pixelShadowDepth(&data, i, j); -- 2.11.4.GIT