Bilateral alpha omega: calculating LUT argument with few arithmetic operations
11233994 · 2022-01-25
Assignee
Inventors
Cpc classification
H04N19/167
ELECTRICITY
H04N19/198
ELECTRICITY
H04N19/635
ELECTRICITY
H04N19/86
ELECTRICITY
International classification
H04N19/167
ELECTRICITY
H04N19/635
ELECTRICITY
H04N19/80
ELECTRICITY
Abstract
According to a first aspect, a method is provided. The method includes obtaining an M×N array of pixel values an image; determining a weight selection value for position x,y in the M×N array; and using the weight selection value to obtain a weight value for use in a filter for filtering the image. Determining the weight selection value for position x,y (omega.sub.x,y) includes: a) retrieving a previously determined weight selection value for position x,y−1 (omega.sub.x,y−1); b) retrieving a previously determined alpha value (a) for position x,y−1; c) calculating a delta value (d); and d) calculating omega.sub.x,y=omega.sub.x,y−1−a+d. Calculating d includes: i) retrieving a first previously determined value (omega_row); i) retrieving a second previously determined value (alpha_row); and ii) calculating d=omega_row−alpha_row+abs(A.sub.x+1,y+1−A.sub.x+1,y+2), wherein A.sub.x+1,y+1 is the value stored in position x+1,y+1 of the array and A.sub.x+1,y+2 is the value stored in position x+1,y+2 of the array.
Claims
1. A method comprising: obtaining an M×N array of pixel values an image; determining, based on lookup tables, a weight selection value for position x,y in the M×N array; and using the weight selection value to obtain a weight value for use in a filter for bilateral filtering the image, wherein determining the weight selection value for position x,y,omega.sub.x,y, comprises: a) retrieving a previously determined weight selection value for position x,y−1, omega.sub.x,y−1; b) retrieving a previously determined alpha value, a, for position x,y−1; c) calculating a delta value, d; and d) calculating omega.sub.x,y=omega.sub.x,y−1−a+d, wherein calculating d comprises: i) retrieving a first previously determined value, omega_row, wherein omega_row is equal to [abs(A.sub.x−2,y+1−A.sub.x−2,y+2)+abs(A.sub.x−1,y+1−A.sub.x−2,y+2)+abs(A.sub.x,y+1−A.sub.x,y+2)]; i) retrieving a second previously determined value, alpha_row, wherein alpha_row is equal to abs(A.sub.x−2,y+1−A.sub.x−2,y+2); and ii) calculating d=omega_row−alpha_row+abs(A.sub.x+1,y+1−A.sub.x+1,y+2), wherein A.sub.x+1,y+1 is the value stored in position x+1,y+1 of the array, A.sub.x+1,y+2 is the value stored in position x+1,y+2 of the array, A.sub.x−2,y+1 is the value stored in position x−2,y+1 of the array, A.sub.x−2,y+2 is the value stored in position x−2,y+2 of the array, A.sub.x−1,y+1 is the value stored in position x−1,y+1 of the array, A.sub.x,y+1 is the value stored in position x,y+1 of the array and A.sub.x,y+2 is the value stored in position x,y+2 of the array.
2. A computer program product comprising a non-transitory computer readable medium storing a computer program comprising instructions which when executed by processing circuitry of a device causes the device to perform the method of claim 1.
3. An encoder (602) comprising: an obtaining unit (604) configured to obtain an M×N array of pixel values an image; a determining unit (606) configured to determine, based on lookup tables, a weight selection value for position x,y in the M×N array; and a using unit (608) configured to use the weight selection value to obtain a weight value for use in a filter for bilateral filtering the image, wherein determining the weight selection value for position x,y, omega.sub.x,y, comprises: a) retrieving by a retrieving unit (610) a previously determined weight selection value for position x,y−1, omega.sub.x,y−1; b) retrieving by the retrieving unit (610) a previously determined alpha value, a, for position x,y−1; c) calculating by a calculating unit (612) a delta value, d; and d) calculating by the calculating unit (612) omega.sub.x,y=omega.sub.x,y−1−a+d, wherein calculating d comprises: i) retrieving by the retrieving unit (610) a first previously determined value, omega_row, wherein omega_row is equal to [abs(A.sub.x−2,y+1−A.sub.x−2,y+2)+abs(A.sub.x−1,y+1−A.sub.x−2,y+2)+abs(A.sub.x,y+1−A.sub.x,y+2)]; i) retrieving by the retrieving unit (610) a second previously determined value, alpha_row, wherein alpha_row is equal to abs(A.sub.x−2,y+1−A.sub.x−2,y+2); and ii) calculating by the calculating unit (612) d=omega_row−alpha_row+abs(A.sub.x+1,y+1−A.sub.x+1,y+2), wherein A.sub.x+1,y+1 is the value stored in position x+1,y+1 of the array, A.sub.x+1,y+2 is the value stored in position x+1,y+2 of the array, A.sub.x−2,y+1 is the value stored in position x−2,y+1 of the array, A.sub.x−2,y+2 is the value stored in position x−2,y+2 of the array, A.sub.x−1,y+1 is the value stored in position x−1,y+1 of the array, A.sub.x,y+1 is the value stored in position x,y+1 of the array and A.sub.x,y+2 is the value stored in position x,y+2 of the array.
4. A decoder (602) comprising: an obtaining unit (604) configured to obtain an M×N array of pixel values an image; a determining unit (606) configured to determine, based on lookup tables, a weight selection value for position x,y in the M×N array; and a using unit (608) configured to use the weight selection value to obtain a weight value for use in a filter for bilateral filtering the image, wherein determining the weight selection value for position x,y, omega.sub.x,y, comprises: a) retrieving by a retrieving unit (610) a previously determined weight selection value for position x,y−1, omega.sub.x,y−1; b) retrieving by the retrieving unit (610) a previously determined alpha value, a, for position x,y−1; c) calculating by a calculating unit (612) a delta value, d; and d) calculating by the calculating unit (612) omega.sub.x,y=omega.sub.x,y−1−a+d, wherein calculating d comprises: i) retrieving by the retrieving unit (610) a first previously determined value, omega_row, wherein omega_row is equal to [abs(A.sub.x−2,y+1−A.sub.x−2,y+2)+abs(A.sub.x−1,y+1−A.sub.x−2,y+2)+abs(A.sub.x,y+1−A.sub.x,y+2)]; i) retrieving by the retrieving unit (610) a second previously determined value alpha_row, alpha_row is equal to abs(A.sub.x−2,y+1−A.sub.x−2,y+2); and ii) calculating by the calculating unit (612) d=omega_row−alpha_row+abs(A.sub.x+1,y+1−A.sub.x+1,y+2), wherein A.sub.x+1,y+1 is the value stored in position x+1,y+1 of the array, A.sub.x+1,y+2 is the value stored in position x+1,y+2 of the array, A.sub.x−2,y+1 is the value stored in position x−2,y+1 of the array, A.sub.x−2,y+2 is the value stored in position x−2,y+2 of the array, A.sub.x−1,y+1 is the value stored in position x−1,y+1 of the array, A.sub.x,y+1 is the value stored in position x,y+1 of the array and A.sub.x,y+2 is the value stored in position x,y+2 of the array.
Description
BRIEF DESCRIPTION OF THE DRAWINGS
(1) The accompanying drawings, which are incorporated herein and form part of the specification, illustrate various embodiments.
(2)
(3)
(4)
(5)
(6)
(7)
(8)
DETAILED DESCRIPTION
(9) Throughout this description we will use filtering of intensity values as an example. This traditionally refers to the Y in YCbCr. However, it should be noted that this filtering can also be used for chroma values such as Cb and Cr, or any other components from other color spaces such as ICTCP, Lab, Y′u′v′ etc. We should also use the terms “pixel” and “sample” interchangeably.
(10) The filter from [2] is also described in JVET-0274, which has been made publicly available in [3].
(11) Assume that we want to filter the sample a.sub.3,3 shown below. Sample a.sub.3,3 is surrounded by other samples a.sub.i,j that form a block of samples.
(12) TABLE-US-00001 a.sub.1,1 a.sub.1,2 a.sub.1,3 a.sub.1,4 a.sub.1,5 . . . a.sub.2,1 a.sub.2,2 a.sub.2,3 a.sub.2,4 a.sub.2,5 a.sub.3,1 a.sub.3,2 a.sub.3,3 a.sub.3,4 a.sub.3,5 a.sub.4,1 a.sub.4,2 a.sub.4,3 a.sub.4,4 a.sub.4.5 a.sub.5,1 a.sub.5,2 a.sub.5,3 a.sub.5,4 a.sub.5,5 . . . . . .
(13) If this were the filter from [1] the right weight would be calculated from deltaR, which equals deltaR=ΔI.sub.R−a.sub.3,4−a.sub.3,3, and the absolute value of that would be used for the look-up:
deltaIR=a34−a33;
influenceR=weightLUT[min(maxVal,abs(deltaIR))]*deltaIR;
(14) As shown above, the weight calculation is performed by a look-up table (LUT). In the above calculation, note how only one absdiff operation (i.e. an operation of an absolute value taken of a difference of samples) is performed to get the weight value (influenceR) from the LUT. In contrast, following [2], we instead calculate the average absolute value by
(15)
It is easy to see that this involves nine absdiff calculations. In plain code, it could look like this:
(16) TABLE-US-00002 NL_R = ( (abs(a23−a22) + abs(a24−a23) + abs(a25−a24) + abs(a33−a32) + abs(a34−a33) + abs(a35−a34) + abs(a43−a42) + abs(a44−a43) + abs(a45−a44)) * 114 ) >> 10; deltaIR = a34−a33; influenceR = weightLUT[min(maxVal, abs(NL_R))]*deltaIR;
(17) The following matlab program calculates the sum R (which is equal to 9NL.sub.R) for every pixel at least two steps away from the edge. Note that in matlab, y-coordinates come first.
(18) TABLE-US-00003 A = round(7*rand([8 8])); B = zeros(size(A)); R = zeros(size(A)); for yy = 3:size(A,1)−2 for xx = 3:size(A,2)−2 R(yy,xx) = abs(A(yy−1,xx−1)−A(yy−1,xx )) + abs(A(yy ,xx−1)−A(yy ,xx )) + ... abs(A(yy+1,xx−1)−A(yy+1,xx )) + ... abs(A(yy−1,xx )−A(yy−1,xx+1)) + abs(A(yy ,xx )−A(yy ,xx+1)) + ... abs(A(yy+1,xx )−A(yy+1,xx+1)) + ... abs(A(yy−1,xx+1)−A(yy−1,xx+2)) + abs(A(yy ,xx+1)−A(yy ,xx+2)) + ... abs(A(yy+1,xx+1)−A(yy+1,xx+2)); end; end;
(19) In the code accompanying the contribution JVET-K0274, it was realized that the value of R(x,y) in one point (x,y), can be calculated from the value of R in a point (x−1,y) immediately to the left of it. In the code the value R was named omega, or Ω.
(20) In
(21) The omega value for the light gray pixel 104 may be calculated by summing all the absdiff values represented by the nine rightmost arrows 116-132.
(22) The software associated with JVET-K0274 denotes the sum of the values represented by the leftmost arrows 110-114 by alpha, and the sum of the values represented by the rightmost arrows 128-132 by delta. If the omega for the dark gray pixel 102 is known (omega_old), it is then possible to calculate the omega for the light gray pixel 104 (omega_new) as
omega_new=omega_old−alpha+delta.
(23) Here the previous omega as well as the alpha value is known from previous calculation, and only the delta value (involving three absdiff calculations) is needed to be calculated. Also, rather than throwing away the delta value after using it in the equation above, it is recognized that this value will become the alpha pixel for a pixel two steps to the right. This is solved by moving the delta value to a gamma value, and before that move the gamma value to a beta value, and before that move the beta value to the alpha value. This is reflected in the C++ code from JVET-K0274.
(24) TABLE-US-00004 // calculate non-local values: delta = abs(*(blockCurrentPixelPtr+offsetY0+1) − *(blockCurrentPixelPtr+offsetY0+2)) + abs(*(blockCurrentPixelPtr+1) − *(blockCurrentPixelPtr+2)) + abs(*(blockCurrentPixelPtr− offsetY0+1) − *(blockCurrentPixelPtr−offsetY0+2)); omega = omega − alpha + delta; dNLIR = (omega * 114) >> 10; alpha = beta; beta = gamma; gamma = delta; // end calc rightInfluence = twoSidedSBWeightLookupTablePtr[std::min(theMaxPosAltSB, dNLIR)]*dIR;
(25) As can be seen in the code, three absdiff operations are carried out. The omega value is then divided by nine (approximated by 114/1024 in the code) to calculate the dNLIR value that is used to obtain the weight from the look-up table.
(26) An analogous code is used to calculate the bottomInfluence, where differences between pixels below are carried out instead of difference between pixels to the right. In this case the C++ code from JVET-K0274 looks like this:
(27) TABLE-US-00005 // calc non-local value deltaRowValue = abs(*(blockCurrentPixelPtr−offsetY0−1) − *(blockCurrentPixelPtr− twoOffsetY0−1)) + abs(*(blockCurrentPixelPtr−offsetY0) − *(blockCurrentPixelPtr−twoOffsetY0)) + abs(*(blockCurrentPixelPtr−offsetY0+1) − *(blockCurrentPixelPtr−twoOffsetY0+1)); *(omegaRowPtr) = *(omegaRowPtr) − *(alphaRowPtr) + deltaRowValue; *(alphaRowPtr++) = *(betaRowPtr); *(betaRowPtr++) = *(gammaRowPtr); *(gammaRowPtr++) = deltaRowValue; dNLIB = (*(omegaRowPtr++) * 114) >> 10; blockCurrentPixelPtr++; // end calc bottomInfluence = twoSidedSBWeightLookupTablePtr[std::min(theMaxPosAltSB, dNLIB)]*dIB;
(28) In this case, three line buffers omegaRow, alphaRow, betaRow and gammaRow are used to store the omega, alpha, beta and gamma values. A line buffer here means an array that can hold as many values as the block is wide. The equivalent matlab code would look like this:
(29) TABLE-US-00006 %% pre-populate alpharow, betarow, gammarow and omegarow: B2 = zeros(size(A)); yy = 2; %% pre-populate alpha, beta, gamma and omega: for xx = 3:size(A,2)−2 alpharow(xx) = abs(A(yy−1,xx−1)−A(yy ,xx−1)) + abs(A(yy−1,xx )−A(yy ,xx )) + abs(A(yy−1,xx+1)−A(yy ,xx+1)); betarow(xx) = abs(A(yy ,xx−1)−A(yy+1,xx−1)) + abs(A(yy ,xx )−A(yy+1,xx )) + abs(A(yy ,xx+1)−A(yy+1,xx+1)); gammarow(xx) = abs(A(yy+1,xx−1)−A(yy+2,xx−1)) + abs(A(yy+1,xx )−A(yy+2,xx )) + abs(A(yy+1,xx+1)−A(yy+2,xx+1)); omegarow(xx) = alpharow(xx) + betarow(xx) + gammarow(xx); end; % filter block for yy = 3:size(A,1)−2 %% pre-populate alpha, beta, gamma and omega: xx = 2; alpha = abs(A(yy−1,xx−1)−A(yy−1,xx )) + abs(A(yy ,xx−1)−A(yy ,xx )) + abs(A(yy+1,xx−1)− A(yy+1,xx )); beta = abs(A(yy−1,xx )−A(yy−1,xx+1)) + abs(A(yy ,xx )−A(yy ,xx+1)) + abs(A(yy+1,xx )− A(yy+1,xx+1)); gamma = abs(A(yy−1,xx+1)−A(yy−1,xx+2)) + abs(A(yy ,xx+1)−A(yy ,xx+2)) + abs(A(yy+1,xx+1)− A(yy+1,xx+2)); omega = alpha + beta + gamma; for xx = 3:size(A,2)−2 delta = abs(A(yy−1,xx+1)−A(yy−1, xx+2)) + abs(A(yy ,xx+1)−A(yy ,xx+2)) + abs(A(yy+1,xx+1)−A(yy+1,xx+2)); omega = omega − alpha + delta; alpha = beta; beta = gamma; gamma = delta; R2(yy,xx) = omega; deltarow_value = abs(A(yy+1,xx−1)−A(yy+2,xx−1)) + abs(A(yy+1,xx )−A(yy+2,xx )) + abs(A(yy+1,xx+1)−A(yy+2,xx+1)); omegarow(xx) = omegarow(xx) − alpharow(xx) + deltarow_value; alpharow(xx) = betarow(xx); betarow(xx) = gammarow(xx); gammarow(xx) = deltarow_value; B2(yy,xx) = omegarow(xx); end; end;
(30) As an example, if the A block contains the values
(31) TABLE-US-00007 6 7 4 1 1 5 5 7 4 2 3 3 4 4 3 4 1 4 1 5 2 7 3 6 1 2 0 1 4 0 1 3 5 2 3 5 7 2 3 3 6 6 2 3 5 7 0 6 0 5 2 3 5 5 3 7 5 2 2 1 7 3 2 4
(32) The matlab code will output the following R2 values
(33) TABLE-US-00008 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 21 22 21 0 0 0 0 21 29 28 25 0 0 0 0 18 22 27 28 0 0 0 0 18 17 23 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.
(34) And the following B2 values:
(35) TABLE-US-00009 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 23 29 23 0 0 0 0 21 22 31 28 0 0 0 0 15 15 20 22 0 0 0 0 13 9 17 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
(36) Note that this example only covers pixels two samples away from the border. Samples closer to the border need to be handled in a different manner, for instance by padding the block or accessing samples outside the block.
(37) As can be seen by the matlab code or the C++ code, three absdiff operations per R-value (i.e., per weight) are calculated, and three absdiff operations per B-value. In total this amounts to six absdiff operations per filtered pixel. However, embodiments of the present invention further reduce the total absdiff operations per filtered pixel that are necessary.
(38)
(39) As can be seen, only one value, R_delta_value, needs to be calculated per weight (the other values are stored values). Using R_delta_value, it is possible to update R_omega_row[xx], for example by the equation R_omega_row[xx]=R_omega_row[xx]−R_alpha_row[xx]+R_delta_value. By setting R_delta=R_omega_row[xx], it is then possible to update omega.
(40) As seen in
(41) Note that R_alpha_row, R_beta_row, R_gamma_row and R_omega_row are line buffers, i.e., they can store a full line of the block. Meanwhile R_alpha, R_beta, R_gamma, R_delta, omega but also R_delta_value are scalar values.
(42) The full update step/calculation step is thus contained in the matlab code below:
(43) TABLE-US-00010 R_delta_value = abs(A(yy+1,xx+2) − A(yy+1,xx+1)); % new value R_omega_row(xx) = R_omega_row(xx) − R_alpha_row(xx) + R_delta_value; R_alpha_row(xx) = R_beta_row(xx); R_beta_row(xx) = R_gamma_row(xx); R_gamma_row(xx) = R_delta_value; R_delta = R_omega_row(xx); R_omega = R_omega − R_alpha + R_delta; R_alpha = R_beta; R_beta = R_gamma; R_gamma = R_delta; R3(yy,xx) = R_omega;
(44) A similar technique can also be used when calculating the difference against the pixel below, as can be seen in
(45) Note how only one absdiff operation, namely when calculating B_delta_value, is needed to calculate the B value (also referred to as the B_omega value) for a sample weight. The value B_delta_value is used to update the B_omega_row[xx] value (for example, by B_omega_row[xx]=B_omega_row[xx]−B_alpha_row[xx]+B_delta_value), which is set to B_delta. With B_delta and B_alpha, the B_omega value can be updated as shown.
(46) The necessary matlab code can be written as:
(47) TABLE-US-00011 B_delta_value = abs(A(yy+2,xx+1) − A(yy+1,xx+1)); % new value B_omega_row(xx) = B_omega_row(xx) − B_alpha_row(xx) + B_delta_value; B_alpha_row(xx) = B_beta_row(xx); B_beta_row(xx) = B_gamma_row(xx); B_gamma_row(xx) = B_delta_value; B_delta = B_omega_row(xx); B_omega = B_omega − B_alpha + B_delta; B_alpha = B_beta; B_beta = B_gamma; B_gamma = B_delta; B3(yy,xx) = B_omega;
(48) Combined, the following matlab code calculates both the R and B values for the interior of the block.
(49) TABLE-US-00012 B3 = zeros(size(A)); R3 = zeros(size(A)); for xx = 1:size(A,2)−2 R_alpha_row(xx) = 0; R_beta_row(xx) = 0; R_gamma_row(xx) = abs(A(1,xx+2) − A(1,xx+1)); R_omega_row(xx) = R_alpha_row(xx) + R_beta_row(xx) + R_gamma_row(xx); B_alpha_row(xx) = 0; B_beta_row(xx) = abs(A(1,xx+1)−0); B_gamma_row(xx) = abs(A(2,xx+1) − A(1,xx+1)); B_omega_row(xx) = B_alpha_row(xx) + B_beta_row(xx) + B_gamma_row(xx); end; R_alpha = 0; R_beta = abs(A(1,1) − 0) + abs(A(2,1) − 0); R_gamma = abs(A(1,1) − A(1,2)) + abs(A(2,1) − A(2,2)) R_omega = R_alpha + R_beta + R_gamma; B_alpha = 0; B_beta = 0; B_gamma = abs(A(1,1) − 0 ) + abs(A(2,1) − A(1,1)); B_omega = B_alpha + B_beta + B_gamma; for yy = 1:size(A,1)−2 for xx = 1:size(A,2)−2 R_delta_value = abs(A(yy+1,xx+2) − A(yy+1,xx+1)); % new value R_omega_row(xx) = R_omega_row(xx) − R_alpha_row(xx) + R_delta_value; R_alpha_row(xx) = R_beta_row(xx); R_beta_row(xx) = R_gamma_row(xx); R_gamma_row(xx) = R_delta_value; R_delta = R_omega_row(xx); R_omega = R_omega − R_alpha + R_delta; R3(yy,xx) = R_omega; R_alpha = R_beta; R_beta = R_gamma; R_gamma = R_delta; %% B_delta_value = abs(A(yy+2,xx+1) − A(yy+1,xx+1)); % new value B_omega_row(xx) = B_omega_row(xx) − B_alpha_row(xx) + B_delta_value; B_alpha_row(xx) = B_beta_row(xx); B_beta_row(xx) = B_gamma_row(xx); B_gamma_row(xx) = B_delta_value; B_delta = B_omega_row(xx); B_omega = B_omega − B_alpha + B_delta; B_alpha = B_beta; B_beta = B_gamma; B_gamma = B_delta; B3(yy,xx) = B_omega; end; end;
(50) Using the same input A matrix as above, the matlab code will produce the matrices R3 and B3 as shown below:
(51) TABLE-US-00013 B3= 22 29 26 18 20 22 0 0 25 20 21 19 26 22 0 0 19 14 20 23 29 23 0 0 22 15 21 22 31 28 0 0 26 16 15 15 20 22 0 0 25 17 13 9 17 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 R3= 17 10 8 8 6 8 0 0 11 17 18 20 18 20 0 0 17 17 18 21 22 21 0 0 18 19 21 29 28 25 0 0 18 16 18 22 27 28 0 0 25 20 18 17 23 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
(52) Note that while this is not the same for the first two rows and the first two columns as B2/R2, the samples that are two steps away from the border are the same, and they are the ones that we want to calculate. Hence we have identical results with only two absdiff operations per sample, which is a reduction by a factor of three against JVET-K0247 and a reduction by a factor of nine against the implementation of [2].
(53) It should be noted that the code above starts filtering from the top left sample, even though no filtered output is required for that sample. This simplifies the set-up of the necessary line buffers/arrays R_omega_row( ), R_alpha_row( ) R_beta_row( ), R_gamma_row( ) and the necessary scalar variables R_omega, R_alpha, R_beta and R_gamma. However it involves unnecessary calculations. Hence it is possible to avoid these calculations with a slightly more involved code.
(54)
(55)
(56) In embodiments, omega_row is equal to [abs(A.sub.x−2,y+1−A.sub.x−2,y+2)+abs(A.sub.x−1,y+1−A.sub.x−2,y+2)+abs(A.sub.x,y+1−A.sub.x,y+2)], and alpha_row is equal to [abs(A.sub.x−2,y+1−A.sub.x−2,y+2)].
(57)
(58)
(59) While various embodiments of the present disclosure are described herein, it should be understood that they have been presented by way of example only, and not limitation. Thus, the breadth and scope of the present disclosure should not be limited by any of the above-described exemplary embodiments. Moreover, any combination of the above-described elements in all possible variations thereof is encompassed by the disclosure unless otherwise indicated herein or otherwise clearly contradicted by context.
(60) Additionally, while the processes described above and illustrated in the drawings are shown as a sequence of steps, this was done solely for the sake of illustration. Accordingly, it is contemplated that some steps may be added, some steps may be omitted, the order of the steps may be re-arranged, and some steps may be performed in parallel.