![]() |
TurboWavelets.Net
Compact C# Implementation for very fast and flexible wavelet transformations
|
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 }