TurboWavelets.Net
Compact C# Implementation for very fast and flexible wavelet transformations
TurboWavelets/Wavelet2D.cs
00001 // 
00002 // Wavelet2D.cs
00003 //  
00004 // Author:
00005 //       Stefan Moebius
00006 // Date:
00007 //       2016-04-24
00008 // 
00009 // Copyright (c) 2016 Stefan Moebius
00010 // 
00011 // Permission is hereby granted, free of charge, to any person obtaining a copy
00012 // of this software and associated documentation files (the "Software"), to deal
00013 // in the Software without restriction, including without limitation the rights
00014 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00015 // copies of the Software, and to permit persons to whom the Software is
00016 // furnished to do so, subject to the following conditions:
00017 // 
00018 // The above copyright notice and this permission notice shall be included in
00019 // all copies or substantial portions of the Software.
00020 // 
00021 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00022 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00023 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00024 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00025 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00026 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00027 // THE SOFTWARE.
00028 
00047 using System;
00048 using System.Threading;
00049 using System.Threading.Tasks;
00050 using System.Runtime.CompilerServices;
00051 
00052 namespace TurboWavelets
00053 {
00058     public abstract class Wavelet2D
00059     {
00063         public delegate bool ProgressDelegate (float progress);
00064         #region protected attributes
00065 
00066 
00067 
00068         protected int width;
00072         protected int height;
00076         protected int minSize;
00080         protected int allowedMinSize;
00081         #endregion
00082         #region private attributes
00083 
00084 
00085 
00086         private volatile bool enableParallel = true;
00090         private volatile bool enableCacheing = false;
00094         private volatile float[,] cachedArray = null;
00096         //Synchronisaion object used to make all calls thread safe.
00097         //Note than using [MethodImpl(MethodImplOptions.Synchronized)] is not sufficient, as
00098         //the temporary and src array can be used by different methods at the same time
00100         private object threadSync = new object ();
00104         private ProgressDelegate progressDelegate;
00108         private volatile bool progressAbort;
00112         private object progressSync = null;
00116         private long progressValue;
00120         private long progressMax;
00121         #endregion
00122         #region public constructors
00123 
00124 
00125 
00126 
00127 
00128 
00129 
00130 
00131 
00132 
00133         public Wavelet2D (int minSize, int allowedMinSize, int width, int height)
00134         {
00135             if (allowedMinSize < 1) {
00136                 throw new ArgumentException ("allowedMinSize cannot be less than one");
00137             }
00138             if (minSize < allowedMinSize) {
00139                 throw new ArgumentException ("minSize cannot be smaller than " + allowedMinSize);
00140             }
00141             if (width < minSize || height < minSize) {
00142                 throw new ArgumentException ("width and height must be greater or equal to " + minSize);
00143             }
00144             this.width = width;
00145             this.height = height;
00146             this.minSize = minSize;
00147             this.allowedMinSize = allowedMinSize;
00148         }
00149         #endregion
00150         #region public properties
00151 
00152 
00153 
00154         public int Width {
00155             get {
00156                 return width;
00157             }
00158         }
00159 
00163         public int Height {
00164             get {
00165                 return height;
00166             }
00167         }
00168 
00172         public bool EnableCaching {
00173             get {
00174                 return enableCacheing;
00175             }
00176             set {
00177                 enableCacheing = value;
00178                 if (!value) {
00179                     FlushCache ();
00180                 }
00181             }
00182         }
00183 
00187         public bool EnableParallel {
00188             get {
00189                 return enableParallel;
00190             }
00191             set {
00192                 enableParallel = value;
00193             }
00194         }
00195 
00199         public void FlushCache ()
00200         {
00201             lock (threadSync) {
00202                 cachedArray = null;
00203             }
00204         }
00205         #endregion
00206         #region private methods for progress handling
00207 
00208 
00209 
00210 
00211 
00212 
00213 
00214 
00215         private void initProgress (ProgressDelegate progressDelegate, long maxValue = 0)
00216         {
00217             if (progressDelegate != null) {
00218                 this.progressSync = new object ();
00219             } else {
00220                 this.progressSync = null;
00221             }
00222             int w = width, h = height;
00223             this.progressMax = maxValue;
00224             //Calculate the exact maximal value for the progress value if not declared (used by TransformIsotropic2D() and BacktransformIsotropic2D())          
00225             if (this.progressMax == 0) {
00226                 while ((w >= minSize) && (h >= minSize)) {
00227                     this.progressMax += 2 * w * h;
00228                     w = -(-w >> 1);
00229                     h = -(-h >> 1);
00230                 }
00231             }
00232             this.progressDelegate = progressDelegate;
00233             this.progressValue = 0;
00234             this.progressAbort = false;
00235         }
00236 
00244         private bool updateProgress (long increment)
00245         {
00246             bool abort = false;
00247             if (progressSync != null) {
00248                 lock (progressSync) {
00249                     if (!progressAbort) {
00250                         progressValue += increment;
00251                         if (progressValue > progressMax) {
00252                             progressValue = progressMax;
00253                         }
00254                         progressAbort = progressDelegate ((float)progressValue / (float)progressMax * 100.0f);
00255                     } else {
00256                         //Make sure delegate not called after abort
00257                     }
00258                     abort = progressAbort;
00259                 }
00260             }
00261             return abort;
00262         }
00263         #endregion
00264         #region private helper methods
00265 
00266 
00267 
00268         protected float[,] getTempArray ()
00269         {
00270             float[,] tmp = cachedArray;
00271             if (tmp == null) {
00272                 //Note: if we do transform the cols and rows in sequentally (not in parallel) we
00273                 //do not need an temporary array of the same size as the source array.
00274                 //Insead a one dimensional array (with the maximum of width and height as length
00275                 //would be sufficient. However we do not use this fact here, as
00276                 //different implementations of TransformCol(), TransformRow()... would be required
00277                 tmp = new float[width, height];
00278                 if (enableCacheing) {
00279                     cachedArray = tmp;
00280                 }
00281             }
00282             return tmp;
00283         }
00284 
00291         private void checkArrayArgument (float[,] src, string name)
00292         {
00293             if (src == null) {
00294                 throw new ArgumentException (name + " cannot be null");
00295             }
00296             if (src.GetLength (0) < width) {
00297                 throw new ArgumentException ("first dimension of " + name + " cannot be smaller than " + width);
00298             }
00299             if (src.GetLength (1) < height) {
00300                 throw new ArgumentException ("second dimension of " + name + " cannot be smaller than " + height);
00301             }
00302         }
00303 
00318         private void ModfiyBlock (float[,] src, int n, float[] scaleFactorsMajors, float scaleFactorsMinors, int gridSize, int startX, int startY)
00319         {
00320             //Note: ModfiyBlock should not be called directly, as
00321             //it is not thread safe. The critical section must be started
00322             //in the calling method
00323             int endX = startX + gridSize;
00324             int endY = startY + gridSize;
00325             if (endX > width) {
00326                 endX = width;
00327             }
00328             if (endY > height) {
00329                 endY = height;
00330             }
00331             bool[,] keep = new bool[gridSize, gridSize];
00332             float[,] tmpBlock = new float[gridSize, gridSize];
00333 
00334             for (int y = startY; y < endY; y++) {
00335                 for (int x = startX; x < endX; x++) {
00336                     float val = src [x, y];
00337                     if (val < 0) {
00338                         val = -val;
00339                     }
00340                     tmpBlock [x - startX, y - startY] = val;
00341                 }
00342             }
00343             for (int k = 0; k < n; k++) {
00344                 float max = -1.0f;
00345                 int maxIdxX = -1, maxIdxY = -1;
00346                 for (int y = 0; y <  gridSize; y++) {
00347                     for (int x = 0; x <  gridSize; x++) {
00348                         if (!keep [x, y])
00349                         if (tmpBlock [x, y] > max) {
00350                             max = tmpBlock [x, y];
00351                             maxIdxX = x;
00352                             maxIdxY = y;
00353                         }
00354                     }
00355                 }
00356                 keep [maxIdxX, maxIdxY] = true;
00357                 //Scale all major coefficients (with greater amplitutes)
00358                 //by the coresponding scale factor 
00359                 if (scaleFactorsMajors != null) {
00360                     int x = startX + maxIdxX;
00361                     int y = startY + maxIdxY;
00362                     //x and y can be out of bounds!
00363                     if (x > endX - 1) {
00364                         x = endX - 1;
00365                     }
00366                     if (y > endY - 1) {
00367                         y = endY - 1;
00368                     }
00369                     src [x, y] *= scaleFactorsMajors [k];
00370                 }
00371             }
00372             //all minor coefficients (with small amplitutes)
00373             //are multiplied by a certain factor (for denoising typically zero)
00374             for (int y = startY; y < endY; y++) {
00375                 for (int x = startX; x < endX; x++) {
00376                     if (!keep [x - startX, y - startY])
00377                         src [x, y] *= scaleFactorsMinors;
00378                 }
00379             }
00380         }
00381 
00383         private void ModifyCoefficients (float[,] src, int n, float[] scaleFactorsMajors, float scaleFactorsMinors, int gridSize, ProgressDelegate progressDelegate)
00384         {
00385             //Note: ModifyCoefficients should not be called directly, as
00386             //it is not thread safe. The critical section must be started
00387             //in the calling method
00388             checkArrayArgument (src, "src");
00389             if (scaleFactorsMajors != null) {
00390                 if (scaleFactorsMajors.Length != n) {
00391                     throw new ArgumentException ("scaleFactorsMajors must be null or the length must be of dimension n (" + n + ")");
00392                 }
00393             }
00394             if (gridSize < 1) {
00395                 throw new ArgumentException ("gridSize (" + gridSize + ") cannot be smaller than 1");
00396             }
00397             if (n < 0) {
00398                 throw new ArgumentException ("n (" + n + ") cannot be negative");
00399             }
00400             if (n > gridSize * gridSize) {
00401                 throw new ArgumentException ("n (" + n + ") cannot be greater than " + gridSize + "*" + gridSize);
00402             }
00403             int w = width / gridSize;
00404             if ((width % gridSize) != 0) {
00405                 w++;
00406             }
00407             int h = height / gridSize;
00408             if ((height % gridSize) != 0) {
00409                 h++;
00410             }
00411             int numBlocks = w * h;
00412             initProgress (progressDelegate, numBlocks);
00413 
00414             if (this.enableParallel) {
00415                 Parallel.For (0, numBlocks, (block, loopState) => 
00416                 {
00417                     int startX = (block % w) * gridSize;
00418                     int startY = (block / w) * gridSize;
00419                     ModfiyBlock (src, n, scaleFactorsMajors, scaleFactorsMinors, gridSize, startX, startY);
00420                     if (updateProgress (1)) {
00421                         loopState.Stop ();
00422                     }
00423                 });
00424             } else {
00425                 for (int block = 0; block < numBlocks; block++) {
00426                     int startX = (block % w) * gridSize;
00427                     int startY = (block / w) * gridSize;
00428                     ModfiyBlock (src, n, scaleFactorsMajors, scaleFactorsMinors, gridSize, startX, startY);
00429                     if (updateProgress (1)) {
00430                         break;
00431                     }
00432                 }
00433             }
00434         }
00435 
00436         private void getBlockCoefficientsRange (float[,] src, int offsetX, int offsetY, int width, int height, out float min, out float max, bool enableParallel, bool enableProgress, ProgressDelegate progressDelegate)
00437         {
00438             float minVal = float.MaxValue;
00439             float maxVal = float.MinValue;
00440             if (enableParallel) {
00441                 object sync = new object ();
00442                 Parallel.For (0, height, (y, loopState) => 
00443                 {
00444                     float thrdMinVal = float.MaxValue;
00445                     float thrdMaxVal = float.MinValue;
00446                     for (int x = 0; x < width; x++) {
00447                         float val = src [x, y];
00448                         if (val < 0) {
00449                             val = -val;
00450                         }
00451                         if (val < thrdMinVal) {
00452                             thrdMinVal = val;
00453                         } else if (val > thrdMaxVal) {
00454                             thrdMaxVal = val;
00455                         }
00456                     }
00457                     lock (sync) {
00458                         if (thrdMinVal < minVal) {
00459                             minVal = thrdMinVal;
00460                         }
00461                         if (thrdMaxVal > maxVal) {
00462                             maxVal = thrdMaxVal;
00463                         }
00464                     }
00465                     if (enableProgress) {
00466                         if (updateProgress (1)) {
00467                             loopState.Stop ();
00468                         }
00469                     }
00470                 });
00471             } else {
00472                 for (int y = 0; y < height; y++) {
00473                     for (int x = 0; x < width; x++) {
00474                         float val = src [x, y];
00475                         if (val < 0) {
00476                             val = -val;
00477                         }
00478                         if (val < minVal) {
00479                             minVal = val;
00480                         } else if (val > maxVal) {
00481                             maxVal = val;
00482                         }
00483                     }
00484                     if (enableProgress) {
00485                         if (updateProgress (1)) {
00486                             break;
00487                         }
00488                     }
00489                 }
00490             }
00491             min = minVal;
00492             max = maxVal;
00493         }
00494         #endregion
00495         #region virtual methods which get overwritten by derived class
00496 
00497 
00498 
00499 
00500 
00501 
00502 
00503         virtual protected void TransformRow (float[,] src, float[,] dst, int y, int length)
00504         {
00505             //will be overwritten by method of derived class...
00506         }
00507 
00515         virtual protected void TransformCol (float[,] src, float[,] dst, int x, int length)
00516         {
00517             //will be overwritten by method of derived class...
00518         }
00519 
00527         virtual protected void InvTransformRow (float[,] src, float[,] dst, int y, int length)
00528         {
00529             //will be overwritten by method of derived class...
00530         }
00531 
00539         virtual protected void InvTransformCol (float[,] src, float[,] dst, int x, int length)
00540         {
00541             //will be overwritten by method of derived class...
00542         }
00543         #endregion
00544         virtual protected void TransformRows (float[,] src, float[,] dst, int w, int h)
00545         {
00546             if (enableParallel) {
00547                 Parallel.For (0, h, (y, loopState) => 
00548                 {
00549                     if (updateProgress (w)) {
00550                         loopState.Stop ();
00551                     }
00552                     TransformRow (src, dst, y, w);
00553                 });
00554             } else {
00555                 for (int y = 0; y < h; y++) {
00556                     if (updateProgress (w)) {
00557                         break;
00558                     }
00559                     TransformRow (src, dst, y, w);
00560                 }
00561             }
00562         }
00563 
00564         virtual protected void TransformCols (float[,] src, float[,] dst, int w, int h)
00565         {
00566             if (enableParallel) {
00567                 Parallel.For (0, w, (x, loopState) => 
00568                 {
00569                     if (updateProgress (h)) {
00570                         loopState.Stop ();
00571                     }
00572                     TransformCol (src, dst, x, h);
00573                 });
00574             } else {
00575                 for (int x = 0; x < w; x++) {
00576                     if (updateProgress (h)) {
00577                         break;
00578                     }
00579                     TransformCol (src, dst, x, h);
00580                 }
00581             }
00582         }
00583 
00584         virtual protected void InvTransformRows (float[,] src, float[,] dst, int w, int h)
00585         {
00586             if (enableParallel) {
00587                 Parallel.For (0, h, (y, loopState) => 
00588                 {
00589                     if (updateProgress (w)) {
00590                         loopState.Stop ();
00591                     }
00592                     InvTransformRow (src, dst, y, w);
00593                 });
00594             } else {
00595                 for (int y = 0; y < h; y++) {
00596                     if (updateProgress (w)) {
00597                         break;
00598                     }
00599                     InvTransformRow (src, dst, y, w);
00600                 }
00601             }
00602         }
00603 
00604         virtual protected void InvTransformCols (float[,] src, float[,] dst, int w, int h)
00605         {
00606             if (enableParallel) {
00607                 Parallel.For (0, w, (x, loopState) => 
00608                 {
00609                     if (updateProgress (h)) {
00610                         loopState.Stop ();
00611                     }
00612                     InvTransformCol (src, dst, x, h);
00613                 });
00614             } else {
00615                 for (int x = 0; x < w; x++) {
00616                     if (updateProgress (h)) {
00617                         break;
00618                     }
00619                     InvTransformCol (src, dst, x, h);
00620                 }
00621             }
00622         }
00623 
00629         virtual public void TransformIsotropic2D (float[,] src, ProgressDelegate progressDelegate = null)
00630         {
00631             lock (threadSync) {
00632                 checkArrayArgument (src, "src");
00633                 float[,] tmp = getTempArray ();
00634                 int w = width, h = height;
00635                 initProgress (progressDelegate);
00636                 while ((w >= minSize) && (h >= minSize) && (!updateProgress(0))) {
00637                     TransformRows (src, tmp, w, h);
00638                     TransformCols (tmp, src, w, h);
00639                     // shift always rounds down (towards negative infinity)
00640                     //However, for odd lengths we have one low-pass value more than
00641                     //high-pass values. By shifting the negative value and negating the result
00642                     //we get the desired result.
00643                     w = -(-w >> 1);
00644                     h = -(-h >> 1);
00645                 }
00646             }
00647         }
00648 
00653         virtual public void BacktransformIsotropic2D (float[,] src, ProgressDelegate progressDelegate = null)
00654         {
00655             lock (threadSync) {
00656                 checkArrayArgument (src, "src");
00657                 //Calculate the integral digits of log to the base two of the maximum of "width" and "height" 
00658                 //The resulting number of "width | height" cannot have a greater log to the base 2 (integral digits)
00659                 //than the greater of both values.
00660                 int log2 = 1;
00661                 int test = 1;
00662                 while (test < (width | height)) {
00663                     test <<= 1;
00664                     log2++;
00665                 }
00666                 float[,] tmp = getTempArray ();
00667                 int i = 1;
00668                 initProgress (progressDelegate);
00669                 while ((i <= log2) && (!updateProgress(0))) {
00670                     //Shift always rounds down (towards negative infinity)
00671                     //However, for odd lengths we have one more low-pass value than
00672                     //high-pass values. By shifting the negative value and negating the result
00673                     //we get the desired result.
00674                     int w = -(-width >> (log2 - i));
00675                     int h = -(-height >> (log2 - i));
00676 
00677                     if ((w >= minSize) && (h >= minSize)) {
00678                         InvTransformCols (src, tmp, w, h);
00679                         InvTransformRows (tmp, src, w, h);
00680                     }
00681                     i++;
00682                 }
00683             }
00684         }
00685 
00690         virtual public void ScaleCoefficients (float[,] src, float[] scaleFactors, int gridSize, ProgressDelegate progressDelegate = null)
00691         {       
00692             lock (threadSync) {
00693                 if (scaleFactors == null) {
00694                     throw new ArgumentException ("scaleFactors cannot be null");
00695                 }
00696                 if (scaleFactors.Length > gridSize * gridSize) {
00697                     throw new ArgumentException ("scaleFactors lenght cannot be greater than " + gridSize * gridSize);
00698                 }
00699                 ModifyCoefficients (src, scaleFactors.Length, scaleFactors, 1.0f, gridSize, progressDelegate);
00700             }
00701         }
00702 
00707         virtual public void CropCoefficients (float[,] src, int n, int gridSize, ProgressDelegate progressDelegate = null)
00708         {
00709             lock (threadSync) {
00710                 ModifyCoefficients (src, n, null, 0.0f, gridSize, progressDelegate);
00711             }
00712         }
00713 
00718         virtual public void CropCoefficients (float[,] src, float minAbsoluteValue, ProgressDelegate progressDelegate = null)
00719         {
00720             lock (threadSync) {
00721                 checkArrayArgument (src, "src");
00722                 initProgress (progressDelegate, height);
00723                 if (enableParallel) {
00724                     Parallel.For (0, height, (y, loopState) => 
00725                     {
00726                         for (int x = 0; x < width; x++) {
00727                             float val = src [x, y];
00728                             if ((val < minAbsoluteValue) && (-val < minAbsoluteValue)) { //Same as Math.Abs(val) < minAbsoluteValue
00729                                 src [x, y] = 0.0f;
00730                             }
00731                         }
00732                         if (updateProgress (1)) {
00733                             loopState.Stop ();
00734                         }
00735                     });
00736                 } else {
00737                     for (int y = 0; y < height; y++) {
00738                         for (int x = 0; x < width; x++) {
00739                             float val = src [x, y];
00740                             if ((val < minAbsoluteValue) && (-val < minAbsoluteValue)) { //Same as Math.Abs(val) < minAbsoluteValue
00741                                 src [x, y] = 0.0f;
00742                             }
00743                         }
00744                         if (updateProgress (1)) {
00745                             break;
00746                         }
00747                     }
00748                 }
00749             }
00750         }
00751 
00756         virtual public void getCoefficientsRange (float[,] src, out float min, out float max, ProgressDelegate progressDelegate = null)
00757         {
00758             lock (threadSync) {
00759                 checkArrayArgument (src, "src");
00760                 initProgress (progressDelegate, height);
00761                 getBlockCoefficientsRange (src, 0, 0, width, height, out min, out max, enableParallel, true, progressDelegate);
00762             }
00763         }
00764 
00765 /*
00766         virtual public float getQuantil(float[,] src, float p)
00767         {
00768             int numCoeffs = p * width * height;
00769             int gridSize = (int)(Math.Sqrt(numCoeffs) + 1);
00770 
00771             int w = width / gridSize;
00772             if ((width % gridSize) != 0) {
00773                 w++;
00774             }
00775             int h = height / gridSize;
00776             if ((height % gridSize) != 0) {
00777                 h++;
00778             }
00779 
00780             float[,] minValues = new float[w, h];
00781             float[,] maxValues = new float[w, h];
00782             int numBlocks = w * h;
00783             initProgress (progressDelegate, numBlocks);
00784 
00785             if (this.enableParallel) {
00786                 Parallel.For (0, numBlocks, (block, loopState) => 
00787                 {
00788                     int startX = (block % w) * gridSize;
00789                     int startY = (block / w) * gridSize;
00790                     int endX = startX + gridSize;
00791                     int endY = startY + gridSize;
00792 
00793                     getBlockCoefficientsRange (src, startX, startY, endX - startX, endY - startY, minValues[], max, enableParallel, progressDelegate);
00794 
00795                     ModfiyBlock (src, n, scaleFactorsMajors, scaleFactorsMinors, gridSize, startX, startY);
00796                     if (updateProgress (1)) {
00797                         loopState.Stop ();
00798                     }
00799                 });
00800             } else {
00801                 for (int block = 0; block < numBlocks; block++) {
00802                     int startX = (block % w) * gridSize;
00803                     int startY = (block / w) * gridSize;
00804                     ModfiyBlock (src, n, scaleFactorsMajors, scaleFactorsMinors, gridSize, startX, startY);
00805                     if (updateProgress (1)) {
00806                         break;
00807                     }
00808                 }
00809             }
00810         }
00811 */
00812     }
00813 }
 All Classes Namespaces Functions Variables Properties