From 274cabfa754f8eb4fec20d5b92da9e70ae62885b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 8 Nov 2020 12:41:38 +0100 Subject: [PATCH 1/3] Use Interlocked.Increment during histogram calculation, fixes issue #1416 --- .../GlobalHistogramEqualizationProcessor{TPixel}.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 274376671b..0c5a109a62 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); - // Build the histogram of the grayscale levels + // Build the histogram of the grayscale levels. var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source, this.LuminanceLevels); ParallelRowIterator.IterateRows( this.Configuration, @@ -114,7 +115,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization #endif public void Invoke(int y) { - ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); int levels = this.luminanceLevels; @@ -123,7 +123,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization // TODO: We should bulk convert here. var vector = Unsafe.Add(ref pixelBase, x).ToVector4(); int luminance = ImageMaths.GetBT709Luminance(ref vector, levels); - Unsafe.Add(ref histogramBase, luminance)++; + ref int histogramAtLuminance = ref MemoryMarshal.GetReference(this.histogramBuffer.Slice(luminance)); + Interlocked.Increment(ref histogramAtLuminance); } } } From 5c8f66cd47263928928c5e746b56dcbae7d945b1 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 8 Nov 2020 12:55:53 +0100 Subject: [PATCH 2/3] Add global histogram equalization test which compares result to reference output --- .../HistogramEqualizationTests.cs | 39 +++++++++++++----- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/External | 2 +- .../640px-Unequalized_Hawkes_Bay_NZ.jpg | Bin 0 -> 32428 bytes 4 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 tests/Images/Input/Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 1c1da6f191..4460f04fb1 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization [Theory] [InlineData(256)] [InlineData(65536)] - public void HistogramEqualizationTest(int luminanceLevels) + public void GlobalHistogramEqualization_WithDifferentLumanceLevels(int luminanceLevels) { // Arrange var pixels = new byte[] @@ -45,20 +45,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization var expected = new byte[] { - 0, 12, 53, 32, 146, 53, 174, 53, - 57, 32, 12, 227, 219, 202, 32, 154, - 65, 85, 93, 239, 251, 227, 65, 158, - 73, 146, 146, 247, 255, 235, 154, 130, - 97, 166, 117, 231, 243, 210, 117, 117, - 117, 190, 36, 190, 178, 93, 20, 170, - 130, 202, 73, 20, 12, 53, 85, 194, - 146, 206, 130, 117, 85, 166, 182, 215 + 0, 12, 53, 32, 146, 53, 174, 53, + 57, 32, 12, 227, 219, 202, 32, 154, + 65, 85, 93, 239, 251, 227, 65, 158, + 73, 146, 146, 247, 255, 235, 154, 130, + 97, 166, 117, 231, 243, 210, 117, 117, + 117, 190, 36, 190, 178, 93, 20, 170, + 130, 202, 73, 20, 12, 53, 85, 194, + 146, 206, 130, 117, 85, 166, 182, 215 }; // Act image.Mutate(x => x.HistogramEqualization(new HistogramEqualizationOptions { - LuminanceLevels = luminanceLevels + LuminanceLevels = luminanceLevels, + Method = HistogramEqualizationMethod.Global })); // Assert @@ -75,6 +76,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization } } + [Theory] + [WithFile(TestImages.Jpeg.Baseline.HistogramEqImage, PixelTypes.Rgba32)] + public void GlobalHistogramEqualization_CompareToReferenceOutput(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new HistogramEqualizationOptions + { + Method = HistogramEqualizationMethod.Global, + LuminanceLevels = 256, + }; + image.Mutate(x => x.HistogramEqualization(options)); + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider, extension: "png"); + } + } + [Theory] [WithFile(TestImages.Jpeg.Baseline.LowContrast, PixelTypes.Rgba32)] public void Adaptive_SlidingWindow_15Tiles_WithClipping(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index dce36bb0fb..e4c73928de 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -196,6 +196,7 @@ namespace SixLabors.ImageSharp.Tests public const string YcckSubsample1222 = "Jpg/baseline/ycck-subsample-1222.jpg"; public const string Iptc = "Jpg/baseline/iptc.jpg"; public const string App13WithEmptyIptc = "Jpg/baseline/iptc-psAPP13-wIPTCempty.jpg"; + public const string HistogramEqImage = "Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg"; public static readonly string[] All = { diff --git a/tests/Images/External b/tests/Images/External index cc6465910d..8b43d14d21 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit cc6465910d092319ef9bf4e99698a0649996d3c5 +Subproject commit 8b43d14d21ce9b436af3d12a70d38402cdba176b diff --git a/tests/Images/Input/Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg b/tests/Images/Input/Jpg/baseline/640px-Unequalized_Hawkes_Bay_NZ.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5ec8165e6d7a9c17c0cc4b9181ddfcc99c2e44b6 GIT binary patch literal 32428 zcmV)*K#9Nq*#F=F5K2Z#MgRc;0RTt4U6b8mHWV`VxZFE3+nZEbIE zb1rvjYiVs|WNBe8Z*pfZcWG;BFGgu>Wja-EWpQ<3Y-xIBWM4>OcWY&HUqWGdUrt&s zYH(-&+WDH$IvFfAxBB`7H<|G)qX2mz}CfB^v!0RO}Q8UO$T0RaI40000000000 z000330{{dC2LHqWKo9@|0RsXA1Ox;I0RsdA0s{d85dZ@r5-|ipQDGAzae)Ogk)cAd z!O;{WQexroat1T;6+?oOBvU41G;^Z=+5iXv0RR9$0QC}0#%r9UKNT zuWi8P?fF+n5C*1IUE+ISN99zr47yeWbU%yy*HSl{2hY59^zYiMaT5CYcK|toBOa9y znhY6{wAR~rq93pCQf0i{ZKn*4V8Xf{UF*i;E^Fc)vL%iiaogIxkNRigOTpc|mu)W) zg>@vZtprSw&=7N8+kez7R#jC*Y>@*TiLP7!0QDCbK11Q-2L?>~bgv7>x-FIQSO$sn z9MfIA&J@hC5`Ah;+J0g-o%3DXCl;w`5w6@=5g}B8`qdMN;oX6{_ikb#5^>06)?bJ+ zu7~jfI811QeQJvK8$;h~mu@M7yvY9mki=9tkN*J1QY>k5#$HZDV}5haBNfJ{R^$rk zS5;8}U~?S#)?%av7$8ms3oy39xgi8fV1+0ONYxAGLH2D)5bVRk1Rs??&Gk zg+HBbRlt%5=~S}zZvm%LlepdwN^@ib0I%gpZLs7(6uKvz(D3`3TedCU09-)Pw`%f! zG4R#WuWD69mWuLtdo}}&VV(?#Vj{XO*|~U)SC9zAsUMYeaJG%G!JKlBG|%lx#9tQm zC^IZYMrEsmHxVE<$ZY*k%kzL;oLupuPfjgGW*I_Z&*KtKoq2*~Es(e$M96Q~~ z)3>>bJS%wCRmlLJnH5Ly?XRBVuc;KbV$zpmA$bk`tHZQze-ex0{{S+4tU+l$g1xRG z&7Kt8C?4W~NCfHbdc}Bl@3rve{nA1yZ6^i01Iw4SFA05ZJ3Yl>0+mt>Lq%xfZE+fk zl)60LWsR!*CmnEKo7Vue1iofH0IzhrTM7%18vqj^`x^4N&OEtgwk_LLkXS4eTw>$f z2<1aQ2qX-DqPzIF7x+~~Z7xXI93GY9+Tr!YPD4O@a8fSyGsf!cW){%JqHzkk@ za~ouLrau(16Xu@cBAbZg*22qza}Wh{f9m$jF!0_4bIJCkKNcjBZ-*z`{*_xCXAyB^ zaE@9hm<0VRHQqSe)Obc7Zii}`-r`s|U=+ufJp0l|8tA{y3c_bUwQ_M5oi5xUk~OfA z%O;xBjBCj1D7~Q2Lqlu`rV2=dho7xH1`t+x!5h_QTr!y>aCfR}TZ2n0V-j|#IBR%5 zWsUiYP*s;uHK$1vHRapkuJLWeACmf<2Eb{V6HV~TSBD&z6Zw7kF+0G^*TKGuA&UrOP)!4*V;%ur!7? z$W;XIkw(J{0NVq?YlmeMKHk+fOJj)_12&-gjs$OBy79av`(WBzsKLC!{L%iJxfkq`K>?CZ!0k0(5v_bi z>0>bN5Bv15R>vL>4aKXVBf>C9%$#TIQutwO+#_28*eh}+l$;PTitFInyfdm+7^wgg zkZQ*2pd*(&O>%MfQs)Zj6?GAeL7MSUuZf?|x&_RILLd>}#UCcJJt;otsiUbKt{A*&M`{{V{f$iHOjysGdc z<<$myMRahNp5nXl1dC&v5j)il@Qpbe?qa;AG5{us$@C`%>ee=xBp zq1+MHyt~Z*0K^-5wLYyr=DU6&WB6t_7+Vc<@LO0>8~tm&?O%JS;1aM1V;M2fn)3L? zE55V~V=GOxz`qoTycp zn)hv6MpbccRfLRJN~*>!$9Sq~=R*@9eW_hT10Ww!HO%}dJ0yBESIxE05uM2S$zrFJ znW-CUWs-Z2m6GXRLQYsl_0Rxh1bLTmd5UIP*|XR86oFYubi;uJa&g|Nsc=^+e$`D; z0gR5-3${xC05adW`q7Q(b&F z#2(uzVjE7xfJpS3@8R#-xp}W#YnR;2E!(83&hRPF@S)1L!+0$h9t%Mv0yi_A<8EE+ z!sEUdd2T!Ks&3lMC?0hk$1&fXa@bWYir1Rm%*+WQ1|%5XyF%M2E+zRQH6gdECmmqY z@h*HnI~@c@qtCdH_Y@O{abR3%R3s=u7&A`LBO|=juUiOo+(4|jj-5xXDmtNJpiFWx zT?u$x`JgmM>r>RJd0CF7U5uYA5f#Mgr4>O1gh-0@1)u)_k}}-13KlJ->{m|oJ~MvB ztF3E zq;&`U)Nb&0L<681J2cxa1esy71|p$-NAA0ssj`va3qe{uHo?!u~HAL)2|+<@Xxgsme@HZLuWN$ z#NdFXv5`#4s1@;1M&w0I+0u0q8F?u?p5~}5>bdi>o@x-c?NPmHne-?mX@FTu@7kY= zTZZ#o4!0x>0%SlKitG48T}#CaEwPqma|E4+E7tRE;!obW&jyRcHRZY%<2J2YP>^CI z5;0u=02r{xhqaRDR-DH_r%La$<+iE_IzSjAxo>cwhSA}vV=U!KW6yc7Ka1gAEa*|J zYf@OOW7c}l*1K(Z8W;ZQ%B{nqTUfPqw2VPeKS`^z0*tztNQqum^ZaKI zbHpf;$|p%a+v2@z*R}9I1*?ug0O`}FdciTicdr>kuMnv2mVsp&j4)9|snX@779CC)5cw(iPRR2C+Adsl~N{-M6j zaJ`#le*jQG9;Ucd2UL*a#kugpg)ON0gh|IyOT*o9bZauI ze-GjYyVmG88rvxfM1}_jh0ARoQK)vP27)JklnuKMVo!ZAc;nv5My7xI=RDT(Fk(kOq)pusWLNYga9x7sH@QWrgN` z)dO){62z7#Ih^w}t#g|Bm$2Hf01>H9mM}UR7lG8o7}Rh&N#C|;@L+!sgs=lvODusI zn)(EUC7;4+0!(S!Zh!38&vDQ$v6t7V&aB5lGoSHNv;$sHSyU4mj>32R&3!H2FZg#B z#-HYS$$YHP*kh=c{b*MD5|yYA(5ihqG6!vo_MZ5 zj2nZ5EXt5>03Z<_v*>HXEg0?k*Qvm8?R$qeP=e~k572(~EB5Z&K)OhPcEG7!VoRxz z7|1q69;)Y%u^?qO9x^^L5=F(I6zFx4w$8GG0F0P8~a6cS-LK1TdWk3^EL#{ zc5wQ|)U@nBw^V(PI_+yr3>7rGNWc}9SWNThHAN)|BXN;kb}ozVR^U*_Ac0h68$dM^ zVOnHOHM>ZX00MhbaU3#Vw#Zpv5=-FxHx_+ zz0u;`P>m7<%F|inbr%NQ5RA*D?*e1(RN=2${wn5%U~YhBerTM1jaBd1vhY|2b^!XG zX1K2WOo=Fpz-%;}cRtmAAB#8DO~nZ!L>&8pwRJ&1_@cJheiQZ-%5K_VZFvmxY#(an z;W$^b#N54l<2o=QLcA~}?qosw*J!#2$#q}*TxoCfL-^1PFw)%^RCwMY?(mnb+apsU zQHuG8vvn_-~H)A+Yn`8SYU!bPE}&W z=6$(}!L~mAsFR2*@B7gjSzw6+pD|1nh4LQ~*~AmKq2*3gtU&V4{V6U(NFoUQU3(w* z6~x>+cx`IvC3Yuc>s3%ypve(FwNS+67z59r)}57ZjEu)4k^73}dn}}B^LkQ-j&ybR zrxy(KtJ&fic4+NgHu!~~GO6rEP&)n)!~Q0c<%>64&uLk@lmw4)QU3tbED;v&Ubj0# zNFP!9)SKZfK`VVvO-Mw2CZ%)X{6IU|x&HtcApO9uH{q^XmAr=7oD=))R^m9a$RLe? zgUF5QM#bCqg~Ug5{kb4@HLdVh1<-C|I*Uz20V!eNY-egPau^NMC(kr>{ zvx2TrsoWI=R2t2rx*#2t3i9tt-M9xM2C-&adV#`_xb7<4^vjL zYUp)ft%JN*a~Wd*{VPF@sabmp%DK#wTH@$JRRbP`z^@;V;yhP2?lPM^HHt!>YmD1E5&BteKNhEp_O(;HGfjK)(Ux(tB%LZE(f!C;& z9H3xy*!^q2hWM}Y@A%qvfHVL-2l=m7&D(eF>$b{MRAk3ErrZKF!RNfvl?1_2^rdTP zM#Je$fS@2TEB!1aQ%4Wsvk`LH1LhKA=}~WnSi22vQsi$K`+L`sdLHe@3{RZN=B9bn zq>n3oLEecNF-@*O4VdOP6d4JJR)!l9`4LPQKb7#qVko0Thw`*zp{F8Dj#>1hZM^1i ze>$$HB1SvreXE_UGVv>R%ez}5H;_MiyAZ%@-)XLMXxf!JjiZ_7rlR_g>kMFX8L_aqjP%oPM5YT|wb5CWYs&~H`Y z?4BGj2P|_na8GHX3=uOPmE<&$afu{r(=-&H4(v=y&(KNZLA@Yd7A_%~qVN2^xiZmi|4(>(+^H!~T=|*STkp z7p$$wQmiM;1}n3Mb$0ncBegyOlR&tnHwRNpt}#?%G#Mlc@Kc9J_=vG^k#PA-BTjjL zysPK}1yHIXK!7)|JB|2VzA4FPDT%@x0^r-OY(h}>1 zQOv1m`(xD7aQrQYK%wp2b&({nmFe{R)VQCAT!VJh=zui0&Hnn-TE)#8lH2L|epAbT z*15xiBo*}rfN9une@bbRew1uoduZAT8v1jCBE1iT{6UiXrRV-ip_%0wAMbjid{qAc z3upx<;^B`d&-nUN@V^yZN-lhfXamfXjMq;C$FC8FAy_V>=|ALB@c#f3SK=-!u9dc| z!K8v<^7g7-_?4wyP^`hqj_}Gi-(xlNsP2~<;8tgHOp}NvVE*;o7&z>I4H=F0>q^EB zMk_1BMC@v%R^mt?5wukGXoR`}2V+zWZT!+u?A+?OajOT@6uV*aIYlxUID)5ee|mO8 zR|GPizpr{o0O=+Vb4#Jsan- z&XmX)7^H<^m4ntMk*!ZpY*Ki8k1X`sm;yU=%@RomP{k`12XXn+BWDa?^sC#r%dwGH zw|#cFfsAiNaR{yDcR|Y&`%t>PHx$J202e!4Un_pc-lG2i3Az*>6_V|+2njk^Pf&OE zuRh>@8sdxU)}Bw5rDtI^OB_;`3i4&bh&;N|UA#|-$QTJ4M{zUyRo{rW;Uggmb%h}B zw38h-`c$o4X5y`66x6!=;7Iy|M}fO>_^uw_NOs7HkY>Gdh+EE}4_K-Mw6@;jg25ED z13L+?D*gWe6XC;%2o1q_Be}@hcJzTy#&Gs*aF4`VK%Oa576>9^89(l8aMrEfxR%%z zX0R@iCvtYHUEop7vl)oU26I21bDkc}tLtJ&Hy2PaH=cFDaG_;l#t7f1YS)yYjW`5? z4RaRNFf@*p*KLM`TVh%(txd~Hx|B~zuVaV!oz5GEcDv#0B2`HeV`=rOACBR=FbM+mMBf^6F3N zUrIQQWRQ^{{STxCJ|Ty z4RW^tBwV$1UfjatW;lu4n9Wc49wZ?0uCt9AyuioOrFDEeV7!Br0FQ6>D>@X?3Y9s* z#EISw22Ch`s1P&0Xn9L9QH=V3xTiwPaOorxvEMZ(aJO**GO{*~b%2tiR>nuyf3;fm ztJbWBwho{H$1&IGT=uw&fJqvYdBnwfh{EC{gSZBI(#K312XaJDF<4xJbDFGzA%+g% z(1MfUdDcEk$p(BZ5xSk~;e(Dq^r`;1>S&)boJi-S)b#^IxtvI% z9f03{rkpmeWL8qkU3@*K%3tNr-?QV2;6r{#E927YMq+${_{_JkCC}hC+e?Aa4V1bXM^VV(BhQ6($Zq z2VwU$_qQI}7dHV!2Q0z{Pd{^CImCQQ)&4Ju;q0Imjh86VrL_gs1RiD>N2FJnk3R&u zB_Vj7PzcopAY-I~UWXlW)$jUahm9iYEwWZ*;ze+LDTn^2!yAWK@W_HBfu!JlE9l6y zb`R}d7sT7Z__*F72vT_vLH%pbdth+Ma4|CMutX66N7ifUd?r;=_QH|@AVG{*AH=Mi z+#9X8Q0hYkF_`7nkZ{Rr(i>phWB?Mgo~Dozy58VuCICOJCFk)_t-U&khMr@WtK>pkL>|u(YCkl9ZHIGkO@AystXqGD+}qS4ndlS z6T$EngBE8v!hnBjnBn+FFQ>#Hb5>9`+7CR^T=*rIRyLS4v8n01$J;(tD5eF*eaATaVv_F!x&R0o zK|0k&p&t3JR^iCDrF>Nz66yex=`@dPLk$-a2!TC%U{@vh@f%LVn5P64^1xBCuT?lV zw|R;;h>QS@nMnNWmx=gj49#eiGm*--4Rx%6ZuUA8wRMpqK#|k-rC}m>=}1P}x9irh zAWY-E9bJf;eL#V%YUp}VxgZu#G-q$ln}_0-;L&hHjsF0}RHz`8f+l8&BTx_X0neDA z7{;4`o{Tddw7_YCrUX-$SAfGc&3}X{bz3A2;%aKpe87YGirglt)$+E-pZ-NyrX?^r zsK_3a%H@VkC*KDD$Xj21)n6dK#EPDfFifw%?bP-cHR zuC3(6W}RFd)2rtj*G--+$CZq)dg@E+a;6EW-{H7!C8q6=U;5B#Vek9bpL^lA?b{=U zb-w}tauk8-v95ObcNcoo_&hz`NIb{}VD#(lUf;(qmiPiVVX3?=S%bFG$jBJ&Uoc&T z!v6q>>}C#0`_L}KTaTAfCUaiPU$e!02->@K6NtBj5x$4H8YVH0{1BY>M^*+XSIfHAI|yQCno64M1j( zGJ_Gw175EM$6etpgfD6Apxb!FoQ;InrDx)H8Bwm0Y1?IEKXG1yu-tR$MnBO+R+G-P zW0B6J`nJJZh0g^{kg_?DDox=thHe{N4Z@66**JF}`-|I9AbluY@PL7S)C|CQHP7KI z9ZCsP_?4!o;M~Yd?;Q^>LrSBFuvwY-jXhMXd*Zdr>z?Pscw4uc;cLTL zk>$tQXb*>31dDf6sleBR?INz;W@tE>9I3ERo=z*C_Y5v15-xNj1Xnuhwg8<#Y7C7SjW05JXNUO|w;MB=2X5v3=!JW9%ftw_Vo zpDP}HX-OmI`tqzy2s`g0hM542`cf6i1C?xzzJiXfMQ5!rcrvEAuM*sP5{;)&1jTi= zqdrm_vB9V@Hrpdf=yNoOh*dj#im-A(Ao8lE(x)dCdpt;CnZe~1Xle(T`(!R-Wd`9OE9>Q>IZIV|?sKF8=+P-1<%a*v15y@5C zNyOvz=U+|6rys@ecP!iDcqU7N3Ab!P_Zxdx&tLd&A7fE&8spD9esF(YoU7Phh&K&m zhPTCwk9C(PLBIo?*H6H>g!qN4nxSvoBTxbX2Vy@e_14lX`BqOjqHU((R(i!w_YG?D z#mh>4R2uT1;LBkc@Z3$sJLz}>&_Km}%HLVyZLxby+lk2KxBIN><@7is4Y9qqR39gSWnzizXaxV z-TLVV?Os1_2i6lTX z%BObiyJz{FAPu~ue#aE)$HJ6*aG+olKc#cMypp`eAnqzt1_^?B&1cm72nQYOn~rB&2g1Y1AkO)&(!;_G*ue)Q%4$YL zhQmi)`_WSe2Q`@LkU;d>kbR=x7jjgH++4%ae)R8nL=p zBXQojUi9BCWHHRupNQ3EEzU+KsID|7#*YW)P)A?RzMy;;gyZ--PbF_$MLMlqC0NJd z-@oZz7yU!S+u=By326dNsck($i9PTu=zr;cF4f_9m8gI_zvT>c~C z&!hx5E|4Ti5@1N}HTS$Yal_jWYzEK+X9Wf@aPLP`66^$&)8 zEB^pZXYg;NT=|K#B=pUCVtKG9u%i=L&stVl5OO=$9~tn@6N$5N8pWVn@hX78^_uyg zi2Ngq;e=S5)`1NJ4Yo{s3h=KJ<2bk*SK|KwCcSTkI#}ZE@i3=Ckonz+5IS?OcmDub zv~3qQZonWNj(w?kkBcvcMX&>ggQ&;@_pYyod`MnMHv+oEs~;;K)!tNG#OOWpD-ISn za($>b2#j*gS$Hc;)cTrrvV#tQbj233r~ca80yB))FBfY-W-)Hsi8o9T4{={H#r!ji z;`p2G!ygzi3|kTb1FrSQcY))0XSA=wTv80+7$SEy%5PoqSQRZ33JwP<-W}2rLS7~# zNfHh_S8eN@Uk}6ZwwGAZZ%Q2i37L%l0Jy4O<8`@`LQFy|7C6UKy!|VJ=NWaKn4LyI zCAY6zgSc;7T{jeBJiug+sruJ-t9RQCYQwMfE3PY{lG>d=!(J^IAw0iPLfWmcMdwt5 zr~p|+_r`0Bi1>ZJCGN2nf0^?i;$XCPpVGVR+qfWH@W-iB6-$Xburag|1L<71tg3?E z8$iz5u~J}I2ItIIJ;jp~_2*JqSpm*=R>&TmDBQAGv)gSBrU9QR>}zCa$Ed0yR#L11PKS}LWEo{v)fn|0#aNA74>IhdnCdD( z^fMK;b?2I8xG^wAFjZrW9@L~1Ihg7;q=6idaYLz5G^RR5Y@St%*Ff0^g!z>EV&26I=c^=xU|g9 zI}w_gW+VtQYWEeI+=1riE7$lJ#Ld5kiwszsEJBcW4I~d@57wjq09A1|__1o`v?^pV zJ&4HqisATY6810IwkN}93h6tNKnJnfzKM}VO7cD>@Poy$YinlF+>lHI{{Rpoyj~~a{7=Jdgmw;&+C$p}hEWxNiIh;g=Y*T5Uwk+W|i+oBS7m z8ba{5LaE>QhR1$!M&|@p!&|qhLEBrCkNIz0O~A4kAwtI^Ip!(<0EHxrV50^Z4x|HA zejeqeBr@P^vx*JF++`(~P>IWLT;{_mIzd1LV1BiMI)XLU&-N*&@fk|!Wh0Y#dPme% z5X?kKVO65!@BHOShP z$ZQdcSm*?Et*QqwHqB+cRP~-z+O=#7o@7+YYRLJzuJquTgS{!p+I=WxME+)qxE)8W zVnc5ft!Oav{i~k%@Rf@xQ?MgrTqxyKjln;9>fp6K;6o52IGl8@?<~L}Pkq0|a&d?G zx}W9(44l$lwzgkdxnS!)VUT)FB0Cm<3C}9O4|G~DA5mXOoPDxaOQ@bl5!$*9-9Qz%t0$;6^1l)Iwd>a|0?aJ}LaZ1g z+ppTZ+Z=7%cNd5(*1$5FOixez*U|7l9=UN`@hFoTkbI=?zi$1FXZYpQaSgV%p+0+N z-mQ4pG|0_+mbi;oEhWToRlp$insDlJ4J+J4Mt5_eL1sMw6J7@${{T^N`fdLJ4QAt( zom$x2j+<|_SK*dE5Ah@ySA@1NFPYSnDtga3tKRZkqdLEuSj0l77b_Y??<8nl@P4Fr2&Hrco&t-!>{ z1fQv*p_DNMH!+O*5mZZnpDPq;^8hA&X1W{sgD`r7xA>`D;kRUvIydQDr5PFwjPs=_ zoD=OyqBjTWMSTyjs|Fwv!bg4U0<1C!`_?HxDW5}Ab<$w{!4)zyf_}Aw5mcGOK?b<4 z@H&ZZ9DskI{)V52CrJD^InKtQTh2kpYNKj>Adp8eH=}ydDHe%n<iJ?HePi|7<+v?nsfj&ahxjtk;XWBD*NX_W+I^8M?r{x5U@MWu7hOjkD> z@m?c{;ci%aK@rzUfK7OMUhT~w!6qQ!ZTnYu3NP?Px?T#*zk# z(zgI23p&7Gp*4F}h(#(9w5eta{%m!~{{Uj6a_%)dxbn#A1d=5CL9a)Lyq2vtqSl1T@cgpg#wxjtDys~-SOUD%jjNjX2tk`hK;=KJb8KMD_vJy7XAx2=M_8<9q}1ba1}Rv^NFTK* zVI+f(tzf_$6IENonUhsZ$k8$D(yTH(huH5|OT<8*wJ}w3<`6qnTEq|P9fZ_wTI@(T zn5nWAFeK0nMg$SuQNhe-psWW^r8s9(8T6z4w>ScNY-vr2XjTC74Y$^!=xw=I1zVx# zORK(lcBQyO5L{>a)c&07j$zo$@(tRJo3V6B13c>D_+kc-J?5724o(1J&#%35nT^Oi z#dmN)nHn7%@+5Z5N5kLE;a7i4m_q~dg@*%=0G)d;?^=J(yF)ZxROe@T89(E+u}h_802If zkEl4WKJUV~R7W=&^0eWttd6jA71{95fjYO3YU2nePmP1?itWF^J+%h&Svs%;!6)fU zd*OoGO9ce~07*ZcdB@?ZE}M&V>*PouopU$B?;&Gm?V?Ep>I4uw6EwCy8+8j8WoG;& zTps(?PX%ynN%T;EBS_pm#hF$2k@?aKE`RpP{{W01$kx#W>I8b@^QtDHgm0m$3%7uo zpHW$vAO80bZTp||NG)(vKf?=Ws1)~Y@b`ol6B=ZU#X{ExY^w#os4??mBVq4}q2aZp zD1fk>N%h~aD)Uc^XcG3WD3}ts0(Y8I#DGI91|w|MZSfa>h;ppUb%v5jf(JhJ?f(GN zCy2MiUSe77X^xmAh>F(w-f+1qkeEe}}mf0Q=V!%(OW3AH6};=0GuuYj+H3fJakTSAmJs>sD7J z!5JGG%!W6SzWEiAfH{F1ie}U{-<1`AFqoa@sWKoyGt#Af0}@6}a+D)%9+j}$;i*av za|hg1p4kNAMMeP1rPRc;&&pML{7=uw|gOww=rO+CftnNMOl;92NS~rOuXK&h%i=jba zKo~MV->p6Cqxfdd0KncoE6VY98izwBtZguogb~bFE%a6MN7AAshyrKcsIj;PdPNls z51bX}6y-yx8n812RXA&&72QDqCk3;$cbK-qV4)cux_eif#oHDxTm@tk^BU#x+!LgZ z!gi*!b}!D|YIj>r)`N+};=K=tI~x+rB5*6LH=(E1L^0||y;UzV^9Rg-kDNC|$%qOTcMbbGb^q|Jk`Ry#m`amPz zlv&}RjU>(_+&HeEgfiJrtO)L*e{u@t3?e4v-^L2=jE?rFJ!&fZ>A=?I6_r%68IwE^BIk z{{Uh7R}eoiKy;Ew(oF9@-jrF12g@U%q!4E(-hviZ0K7wKqd&} zdQ}R7BoIe0{L-0R0VAa;(odAh=y_9HXx>5PR$H2vAQE>2dPYrPkqzs;LG68cij=Z$ zCWz8k%eO;PduB{y-ns4!$rSb1?Ygv89j?OJiOMPf{h7@!H6GwdmqnR2Cp z>J42ui-Vl@tBd-#iSJFX%K<^|xvUrZVEPgHR$@Vh$Ec*9wLrok#ZKzAysiY~cdkp! z0{MKi$ZcF>OcF`YD9l%4Isz1ePz)1;OT<|57jf%cYPT%0ESjK5U?f!#l@OcrJ!zH5 zBpCB=P`hv|lDHdS)>kn*Pu{!uTd#FWvDBatDLy8S*OApj9Rq$?-ngFAkQ-XD=4T*Q z;m?J{D=-B?k9zfuyG~D(N$Mu4Z-zmTgHQIIs)EP_L7vn{4{cHuvWeu=`O)wcgAD5< zd_^g+N+0I3n29SM^v2udtFLL=xW9wq2O2nl%6!wm+PmiNp(vVTCsY}#DYyW{cA2Kv zbg2>BHJbIn$W56Vp^WT0ing{eG`@Rcm?IN_N1=*VXIvi;=a@Ac#kG0Y9r@Rr##+65 zh}C7n+tU!k@`cev$*S0 zV6uGGHbOTCJ$9sECN!S-pa9$bHH?8c9=lK`3;=y-jYD|#rVMA4dQ+-&~5t0n8EIOpozO*1!YiGt3?9CDU)paym6jis41%X5sML>FMcSG1Qj;AghDU zoG}dQLQPsx4^D+d9u+W`~D(gZz#l z$3-=s#}HBv4RKHRZ}*Bzb{$FbuAzfB6CTq{!O)#dr#y%;^rZ^NI)eL?SR{lON zNUmhyIF(I0?6kzo6Z%(I4`AA~RKX<46N-Hd76O>)1}neA@ey@{u#r%>pce9+^)(B~ z#6xNUyW+XaFcQ#rgSIKr$7zgns;$AXQ&gE#&-0=oM`2gBdF|nHr|`Bx_NpVJ5*TUd zHx*U4tl|vxscDXk2elPpA_)h+_0ekaXvjG9tl=4;h;CriZYfY?Yf1MN6KP-NZ1k8k zjE`@5-!&#xG8+{qv{z{B0;I!nGaR|p+(dlS+K_>{9<UClz4a0njtIXu!>I>0;5DCL_IjTn)Q6 ztSGx_-uWcyOWHsKZG@T6I_yx95`jU&-O{V3{UK@8r+(y(K@xIJW=gQehb%H#W# zeziAtK@VvZ^&5WGjpbyIDL?&Uu)IdX1jN=iLdK&Kan6j-U#)FK3{cipY zI^1RIBb9V5^mRJ?v&>L?VgUpd>M2zLG62 z@JuQ7+OaYuoF4Q80nDFF(<>fF6u<_Vn#ziLQ>i1;te2s;BCZ*30jme7q`7Z$MDiqH z(vVp~GK<#wlLUHWdWGH&-Q1%=>O>J-FT*{ClMDpGsHhhV6_zKhYpNB`hT4GWX05}a zHrd#e2LsE$r53hr{Ks9QW8b|_&5NEcoiM6k_Bj6l71iL}2taZ$Mm=khcxKi?Do3q% zTe+eYn4iRRit4j0po@V(_pXDXxqwXoO0<*1VJ4}Yhor%DQ@+!Px-CSr@dl% zo}17Pn!tOCif7D52h%iV3I1D)i^zaLKUy)>e${}^MkwQ%6bqo;*Xk&Emc~o0deWQd zKNrwao9d?pzLckk2W4aH6sGzA0QHY{sh1NX#lLe$_?8PE5EII349-B%Rb!A87_%^D zGQdD5dVOaZBeYP}B>9bL19m?3jyIfd6}b+yWF4ZnUB0!9gD`%yxq}XKXMqMM~n0I*AOw)HnC2Foozk{`JpsHH;7srVs60jx;ndfy_+^ z-%5{OwL}pcmh5WDaJD4RNUJTvL=m-A1$=X*Wh!HC^b+A#B^-afMO|(qA2*cs6=*A% z3^@@+4x{A-Na<82NR~6-GgBi`(n9+hMzjExARdNkRIoBk0nR2VUrYSJIG%X>(s*`( z01J!nKTJ|OTWL{nFdamGRVU%@DZ{Idg{oI9l|FTfi&_&pfHbUya7jG=Tu>7o2YPTp z9&8g#Hr_z#Sd1AP@~t{xXE~xEWNkfWx-Gspj70BM(lr9uoGSx^@5|89U`X=-(c3hQ zZC!k)>t1#41cohXp!7Ax#9O_pZclzy((VPkteB7{L`6kk#Vj1@Ryi>uy$f6qiDBN& z$O{C?>5A^PVMh)_olF`D6~)J>vJ?Ea?AJRUQ1Ra!7^orWE8~aTuVhm|Lvq)Wse$z8qSdo>AG1dq)Ax|ZyDnIj@ z#Uq_ei0SNT2r>c4p(1iY6}4stJ?ZD6ph+15Xe*PCsH3ZV;(#ENk-4Uk*jD9TMSQD) zAPRK4)}{c1xa4ZKoSB2ZGIyz59%4e3k?JZi{{Vs%o?MUn(v_4tkUXuiU_UzGxGy0R z%RLF-{8kq(DDy6(&|p(|j=D+mw&3J}>MNq%#v{zZhbS^C$VWMzwa^^Em?OO#h^zrn zJ@=?GX+JiJ{;pn>%Xa0d5_HcjgIBeAE>5NZ^#lEixKbC&Ln-EQj+L~GjYJ)YI5Z6U zj+0Yj3lO78o_xQ0O(YBuI?}+zs*p2}wkZe%4aw{)0B0P!{{V`>vY#tN^!@0c43Xm7 zSCg1Ik6KoRqbM1MpYmbc7 z&AwK4A?3Q#b>niaF4=orqUx^E6 zJ4UrO+4RDT#L(6cpaan#O$5$m{H$R^<=6E-KWm~;m^dJm+PyY~FI4MM+G6fXCk;=c9*bjt@GVb z{joULmMvkliywl^eZ0*{~jgK1LT*w%f@gLy;y^g>8VZXuYZnHdA)~qWqUei z{0PHIX02_MJmzo=1o#CgVeXCp(&5OeKwn)xyQO91{>@%!;+~m^BiK0lntfnZOdsEN zYP866$gY`!RkxYuP~s%g8>fK~*JN!tySqfc|i(c5IOUM`LUnR;#wk%@`Bc-)PG9 zzDx3wqWq}?9W5MLfHd-do+@XnQzde|Yuo;DFjM`#`g*%B3wl>qH_mba(7Z{W~IKEgkQxrs?QE}=qc5q%xKzdnK{RiN-=u9sgTD9Q#m6CHVJ5>}5Vp7YU3#@A; zk=@B*^_TN5l{nHBx#4b4Of??YFVQ_~_8x2vTCaq;kCeYWi}m|e#Q2C75?u%imcJai zwS|eh=43oJ69^1IfW}`u(^UB2+%ecwBAf3lk3TC)X8zHJ#=HSItWxo$@<-*Lufok~ z$kjbU4|dR%j@8*{o?qMn-AX_@L*b9xP6G#hNCqd8UjD>1H%q_oCLtF{$FbDyC@rBz z3jf9PZdS`Cb)C(*g4Xbtr2J(N@PNDMiiU9)GGSN5#yBU=C$OTaFetInU5BM$=IJ=; z6@`^6jN|Gev)CUxkPxM%%UtwXGtk|TlWEgwXpSmaapc;XD3Wf#u3H21hg&1xXxPgt&VGL$ z4a^24v*0VT(w3L;J5KSCVFhtxb-0rpU>lR0(^}yiab@0e*1m&s)=>ec<$NOD`X{ya zBrgA*C(qMuHvRz7d*{H^s!A1-f{e@;(_UVkJa=?s5X&FS7=ajk48JffDsx4)=c1ZB z#j9vjG}WWaEd0^PHUeB=)1~IMeZW`Yn#*>PD3-hS^0s4;lqLUNnv>XF-P)Y@yB}6-3Io!qf?jo$kC*OyejK=ks7^`vH3NSn2lTl})?ZRJtZ?|O+ zCq}p#4>K)29f_ah&;8OaCy`UNxUw^z5#f|k!DCvz_+574nr)Dr9J2dJSz9J4QTKUZ z<}dS!RzKsFB%q6%sHK;sv2>Mw%@w_5vsDoTr*zEPjN}zMly+xTB1Zem*7Juqpc8fB z^D9Y73NgEGNg+WrRRc~WO6HL9pnwq+vlfC~Lf@5~&f-&StA0&KP#pI&qEQ%*VEvO@ zPzqJA%BUQc`dIA-F})zmn+5Sg7nqiSplyUQ2blC9;Jr)C13pAH9l5aI9ZvJh?`T&F zpu!HZAGDD(T<)KJrbaF*us194!iS`Qb1_>4RS5;azQ!k{; zB7Z1WFoJ4J{>-c=_F%)Yz1OWnpua05vr#ULzJk&IM(Vm?ecu>LmTxkW)LFOz|Ss>CyRD6ilBUY{7= zRL&7)4q7iLEx()4dDDg2Bqnae6nLckSxg~p(_$yrA&L(mQs)&p6I$6oK!`uN>XbY0 zgVfs6y`IPR)<{i}kGQ7jgg;b?)lu?Bhd#DuIxMdGIrZ~FdE3!c7T>|!Z?|A2E?-{L z5xu(|s|&=)4e9F0#B-D@jx|`?)hF*8`;KYI-PlPPz|(pwGNEo$C%ag@;;XhCyHn1l5j3n*Or_nO~UJY!-=&yZ1G}@{w zU@lO)EvxzJQBeX1^)F>YKeGmAbuVyy+_vZqK5F}Pebsrz>N_pnlflQ$tAB3GUhv?) zw%D_5-A=tTI~p1HWb6i9-ptiR97!|bxBNcBsg1cJ>QI^VRqUf{1WR1Hj>`rRMDJ7S zf{IgG#@mlh<>*-2k3`b+!w1S`KAPD2WZpQioeaG8t7hWcB1LCJ4LqRzE(_DB=6k3V zc)O+5z|F#^z+?B$d-m8Hu26lvUsU8PRs4$4cG-31@-#-L!sy4W44}SeOOr*Y%@y zye{iH;p`V2-coMwsFtNGb2ah{RZ`k3QxA0k`zT$1c|QnA%fegZO9y86dr3GKkD2)r zzp+>QCVWKz+kYBvrS(Q=lRh1IuhM&t5>l*}7RFKG(Is*7`l$^ky}4mPm~+{L z%R;?jL^%?^tvyqY7<3jaRkFeNvzzo((nrqj3^!a);H_V{Z3?j%(tP9a@yUYDnI+2g zz7qQ{+_1~iDvyB0q-g$@g$bu6XrP}_D}8uhr1@d`2h3m>jcD9;DDqY z;51-*UX76)x~DnbkG+{VpBbOy=_Hg}Eo%@v28~i{v-h`#}&-L#!q4hG^L*39@tP8h5;X{x% zq3BPrxoS6YL@rI!cdIUyMo#&Hi0ozHHqA%(j@-TqT|KWo?B##_7^8;aglyu!nWA{z zoLxBIJEL6Nek2x>rCp{n+z{shA(VXLm87G6TzOnBA^5DS_IJrP>_UhJO~r_XGu%40 zp1sD`a7caaZCAQ7BBt^@n}fhv)U5uwz0{<0zV!D(_vx(8v+GZ9FI`J~H1B_txrXp< z>*p^Qf7jQtSXaizDap+kI53x_scUr#LEXC3tUnIrhUKuoz$k@WqG@4tII6Y?NJSIeqC^*E40(PC^ z=s&b%F^rVq5i}Ex<$-%rG=v(K`?^F0)e@25ZniXUJk0p9jX_|dmNyzq1Qg6DIK{L4 z0#16A4drYP+Cezq?izyL5szyitMFO!i{z+1m0T|ez~7sozorB|+S&^l%P7nL09{w7 zLy#%g+1O?Uj`)y+;Gf>o*TDM(N~T{9)cBs^B@iG(b2APiCys^^3Hi&8))Mwf8v8Xe zD{%PoVKF!kG4a^`TTy?p7%9)Sj5Xk6nz(<>S5VMtin~A^tQEQ$5V2G-2j!sA-1Jn{ zr262bi679l1js=JyS&D^R8pRju^BQN&Y+I`BDd76_ek$5_f8wPv#vo|t%OBs+ooAE z>20-eJMKrzG9!_$-oIpOMsrzRr12azKh3A&zRI3Py!XR@{QU>l4#5Oz_%?^$FPYKz z9~DLe4|#eXlLzv3bxiBcvl9|QDf>2r5^+XYt+QuS@gU0pL7ctrR&N$Px5bb*;YxL1<;SIW@j5_6!!8_mX$fBIn zmgB{ZqU{21i;T3AcEF~!iFu4dPi2?HzLZ=V02D@76myBOGRb#PfcyV!@&Zi|%V=&A zh7@D{dyrS-K&MOU^#J&L8xGzQRpqg50!LquV|^`|y2E)1b?3c{#sz5GENs+>#Zpl> zr-y64r4EAB+V)HHzu$mAecvZ#2Ls@z&CC{TT>x}yr2$dgS41SY$#0|tu>WQj7yv$n zX3)|8C+fqZmG;6jy<6R|9fKFSjHlBkqts^UVkIkyc)3;f#|i%LL7$vm@_99(!~|83 zj&%-iBiNG}_wvn$@#)pr2n~!~jBe^$HRLo3w|*bu$-C`J66&AD=XFoi`#n)z0f*D0 z4nb-1U%5>Q>sT$zL-hFaQ3ahMwp7TfS&ld`vyvawA!KH9P+#2lChi99$4w(1UZd6{ z4~A{MkBqk*3o;D;10>;ljgZ4YJ&`&QF0ME&DKwT=s~YnpWXJFdJr8yblJD^7f+r<{XojZ$mE;KV#i-QkH7QH@(iVzB%6!Ab^b8 zU?`Ms&a!*N!^q)fmIMAb^AIn-fe&x;l~X*f>)n39Hn!%Qr2OSWTkAaoHCUWa-Az#6 zLyK|`>#sahAHQ6cO%xMwrBs|E_Iapt#SNPFAFIph{*aGt8a1Stuok^2kE`V4zpj*9HCUl4@q0OEsgqkswti zx^x1OmQ(Gl)u{@bf+WXMzXqcMS+LLZdU2Gd9^Ws-6<4#|?;-O$t`($$9n z;WaV!&REPP=-L zDC^`(n_2GE_KwDGt zd61dufMLga<@!UM*#ZkZIKpPRukZEi?3ui*dg9&PI# z!CI5&!%ev@Tg+%Uq`9@IOwApjlz=h0l(vxvH!`t035T_b41N%91G**=1D6oj$MI;u zF3_3^Oc$3$*ecLPuU7<$uF~uneBhf{$|A4oqUbvLG5*%(Qo_?Gw_e0teCHKItas`5 z1cTAo5Q!bgi%~+qGg78!&fXbQc*~f);tV2ug+Py(>uaWLy@xG5; zB#B>6kAWzTYieOCu$4Eq1G=d;yZR4f zcs|s0HHsQy_6@DFDzdqGyN`Jt(su6WbuWb8-x43S5Z#v$g(&!dN-OYggv^DP1_Fp2 zY`(poMm#hxh=k7R?o_Fa1r59FoR7Nu!DwY8*eY!0RnRwEMtPplAO8W4xo){>oTFU2 z<0$p=Zo~Wk0b->bMi|q=+9$|a-<~jUgvwc55_7fOh^~5=hnem|ZaC^hx3%$wyYas$ zr5ev$eQU-Db$aRL#V@Ao^UXsQc=e9Ci|oqHT)-GTse9{;4^>1vi zMAX6AmiSojM;Uh1m<&yY(gw+av*%uN^eh8oe<*uZ$wTW3Q$RrqqD2HU&QLDbATQdL z?4TeRZ}tl`h<~d%s(~Byr{`+n9< z_2GN45%{)7!(jRHuf|Rwk6k<~>^uI|bpZParNiaS^^drHQs3oUeUr$j#FeB7)-s|W zxFEhIqeZL*d-9aFOOglgAdro@&oJL^P7{$p$iOs~E`@4UfAknNRq8AA{O)Jq3rdtH zOKGaIm;zFFbjscP+{K=>^)aG=SA_rBS%wr9M2!i@nFui!m8Am$0$A$B#(qTsJE5yOzp8I( zG%Q)&X=()#fIa%!_nHkiX9OJC0E-r0$Wg=kdiOAUNBny;FApjeSSvgE{DwR1phk9L z1!Oc;O9Y?yDY>;=6R{2O4BV1(+1rT-mp?O5+_eZulQR??zGybLD$`Yopqz@Ocq|ds@_=lHgphTY&}c# z_DC%}$hB_{=FB;<}JB_~Ok{)E0noyKS`I z>UG5!XZ$jUr|kyBNpkVo3k3_$t#@(c?VHtm|i=S$KQ+E z3tvpIRlZg@#VbslW8leGKky=*%Fs3ks*jpXx+v8BA3*8DN}-JDkyw&>kY*7brRWHI zlIZa`LSahOFnT>Ho9}eW)*dmi;c$1o8VHCUKj3cgEtKq;S#X#dR1ny4yv4?NFXNc> zh&cLSOkQSPLbwe#OeaUYr;$^^fRO%K#Jca;`mo}kpdPQ%i@FOLm*jygT`p_3_s^Zi z2Y4(DOchuj6u;r~6`7YF>WuTr%k64xXJyzwtaWV%dQK=(a=dR}jwLk+0SL#KqIb!nlpY}PXn3o+=ttj-bw;~qu_y}&7Qf3OisOU6< z+YpJ@y536zvrf?L!kL)SVABnHv?LQ7qwv=(sg^i1%~zU+(KEy*y$0x@Kh8CXy{=2|Mp2 zR25M^+H*Roh*i*%{C;+k$p}3Z&ZI1iy^~I4x17;~FeuGu67a6Ox>X^3_-9Hf0r}J` z-1BEG0GW$_wzQp_A336#i`sY%P>0)2Pi}yh_i=S^(c>pVM)sPiax8C}5RP(jdbW9Q zV{8=Kfn1|49^^DGm?Jo6rkC-c@(+`=s?@k}z!#eIU-H$A6V8NLG9FSn0LiT% z^7;~=x)=e1c8t8}6UuC-4Nw@~qHYnWIKk6I*J=MX(crH!xq?!E^ZYHTDej9&_9p=k zzxB47aqrnF3DMh+?SpC_>IF(#H2cM&uFre7X|9Ni8?Pz*<4naot-28DZDFZfC6Dw? zEEr0K2HYk69QkiZ0`>%~{J=aC=GitQ#FF~X`IzaefV7ZpL(MZ1llH!4oj@M7Ps%&@ zP0n%apE2cvGX0X=N=(LV8!};rTOS#_+)2b(!Gvl-0wA7*LlV2)dv(~(t`pLn8ENHw zSXWD^tC4!Gh*R(gGYOmzoUwA~wS5Q<*!Zf%zrb3wA?J>JBb5jT?84>OPPr^!MDLEj z3f%8J-y~M0Tjs2@Nd-kYQu%>D+UMK8Hvra3yX4227sB&pr_2$eri*qftL8kh&1{cL z^I6q_J4+#ECV9)b3dXPl0Y~CsXslmS1bTB?*(l1cc1tQNFbPvwZp~FIuI`=0KO^)e zUQ!MM%jHyEw4-AP)sY5|(1XNC;dgAR0Z+=q?lcsC*5A}_9z1eJ^oW%W`*g|BjYQi) zvf2HBTnf5HoM9k1ZxHp6#D5)h#K8Yu>4~};ZBt+bh-Soq^SxsUonqSLRikkt)fSQSsXvCJ-Ipl zhE+G`egMu!lIrNbPO%Fpl$w0%`s#N>9?p) zF~DFYCX4*@16F>b&vc4P*d)ghKcgpAW+pQ6jk>PVUg4{!cC=foL%FZr7(>HTezYWV zGKu`*)ovQ*Gfqo??^L?N7@xBRmo!NkD4VjK=49Gpn=Oe1;1jd5S{)<=Y-?U8Ni$F# zTFr9ZG_l{$re$c}a%oY|U|;^238IkwbDu7id$@i79g3s9voGint!j%l+zT>bd7RGhS zp~I3&G2qnf7P~a~UqnR?oeg1h3Mhp4zmy)S#K_4qxI2tqVmkmRATykned=dh8LG>Y zf5l2}fzK+`F_v0L3USdL54qp0L|pZ!8^`v$d*s>7?(P^0M(!HhvpF<}b0J!>YBO3g z0=Qm(2aod+CHfqGU7Pc^3IY(7D=cA3y*d7^>lkRSezWBJ0>BpE;6oR(%~x`RYyS$> z!2Cw-e3SBbyvTz>RTCVs+B3_NP*VKk%x`%5i_8d5x)U2n4nzyZ*fY>ZT^aDq=^=d> zaDdYOchxmJ%|x6WXE7lMSREu%^J=WV;cH%8dMcCm8dLrjJwkrlsc%K3wLmh6f6Zc* zrr&!a%^C#bd{E8D3C?x2$pOu$13=AEcfu>_cn|6%UQvi#DUG^G6KiJna3Asu-8{!> zx-Kc+A=*S1Z~vIw`9|DlwaV4LIwY24cmVW@XgP6FGF+4mXQ8B0(YoH)sL6e~kkve1u zV7GKn9=$JE%O@ZQ3=}g`C9?D!l(^8FPP~(9rXreMf!&10J}IKaULT8as6R+6)}L*E z*BKv%gYM)Ri)QF%Kw4cylRaZ){C0%1p2M{d44Ck-;(2@n8-|J7hf3|Rr3TodqXep0 ztuNra_CZ9%80!Cx`0T{JC8ADLCF`cpHtqW)k7D|kxX9pG6rZzkPRNXRgF8z~heKdU zt_gqZ9RE-AZwc_2S=NstQurCx9!Tt|-BqC8Jppvn#$0v++NAN8W{*Q@}tHKGxjkmx1In$xE zDzGTn5Xms@iR?vlv+(K@>#v(Gh6rAgZKR?LyP7y!NP;ZGvX}dTDGixymnhR2o~z!m zlDt<;I?zGb%oa2!QB8hv8s3uduU#l~4kV9xGzx>`#oLe@v=1V6)|0f~&44|)J@-#@E{%*40S*q(73pCO?Mi1*{>0Lzg*MLuA9noTV!x)%G6VJJzljP1Bsji61&Y& z33vNb$CTet=1a`Hz*oL~tjBy?lF7szfd@sShKP-P4zir3D%6@DIj$wu2wP~r*FwdT zIIQm1xzjIp*u=%4iN%%MPOq#P>mZYkd<#nQKYTHC0yr)@sCR-7hzB>M)ZLxxH@B19 zYjRf0UZ+Id8JF>cXx0Wuxnl5+%xPu7@;e$;kGzXjaZwz0$ylVLhm(ZU+Ne z)d6zD_-|u7C$whYO-=764t2*_?x9fC!HTOUn%f$XLWTG!UW}6r&8qr8l>^5=9QEZ^ z2B-Y5+aV(dN-2EFk!7J|b3pGw84LaF^C18SguA@z%HdIQEuWHj)FgCQ-vU)+fg)K& zgVR(?tNH?jor?`Ac~eKl*ka`h_-q&4?;0_na)zHvE!?)V$eUpQLJq*7BcFk|MLNJ0 z@h358Jr$x?g|h7%4ki`4fQN^K|Jhcz@IJ2#xpkW8aa=8YW98;vxnoo+aklPi2bw6O zB#6n25U`c0m4uCPcrH^!s5tojCVuEHc&KjFC&Ng`aR;ny?bib^3FzKMgbh@xry8C;* z6uMxk$wz}}(`=Y&6f?uG4)|Q!SXW+(qO0(wjjWLwB!9O(5JP>qxvXxw)>g9Cy( z((iu>7{wOr7Z^}TG>+D~udxEz6A60EeAiBzQ>&AfGhZ1X_vggeIkaWIHB-vd;Xkk!FqkX zAZElt9fvy37wRc=T#D{YJ+70I3xmvK*V&~NlWk8;HVo5&v_?ozoVz=xTre$%cz6%A^NU4j1`6?F%TYk9dN zqQ7TJ#z#%bY+4Na8Lp;LzI!n27hQdFy?@=)W;i7|+B)ry5)UXb=X5%qizZ3Ffy;?g zk65hPepnF){}13>%%K<9-%o!q-7aUWE0*LS4)#Hjs#rorvH~R(fdHrI+4`lrc{MEi z8|b}N)+IN>bXyU^hb6E*l7Q*B)JINv+Ll;WIror}7GJEI204?z%!ZeAgSz_0VHHW- zbk%QJkto8^m~tlbMk<%T_&B|& z-PJL<$3^^;&;D5;6TD26^a56LL=CUAg;-T3|I(P$<{Q`B?viYwyW*758C#tst%y@k zf`o0F>E2^2uB$9HC`jEf(`#Op*mtwsE@u0<{oW7EF6bl zARo~CGirEJ%QlH-)t65IHg^9O9-c2f$Bkg(d|G@531E|<%0CM5XuXop;D|+=7-Y_^TSD%vhPyO9#bukk zz##WGv{#U5N|IgK6(AcNW-ABW@W3joG^5MsXqrN6E{6zAM9cZqsRg+n;rI;?s8uhp zfuswY)%P?kEayvUUqM4fCU`K6v`I<*T2%v4b;*JSiMZBiu-|+lajw_eb90xw!zYg) zZp&t&&}OrXNTCBpRL;sbITAqQO$7X-6sA#%gm#lMDf9pb>qT123hW#f)hTvqzq&zX zr{_1a)KFiw53@exu|&@rjWUOXm=0DXDCnJ9la1w!ZbY${vpz~7zu+RkH67G%*TW;# z#;$co{KefXVH{O9bsPGtOzssW3|O6nHx9@X*X6j?@7 z3bHKVa@HI$TyIKlkF27kKRqVJHztq#dMID|e1(-(ckq(&S(WOucv4z4uXff8OTtyK z@Sb+&2ggTqkOFzbG=vF5&Y~l~)i6BvM`KU7Rv-9_?p>1&OYSY?Be^bJoqT%l#h!!m zKP;=XKKb-3&NMXk6JYUJUq44WCoaCeNJ8oI_*!PnY2SIFf?Z=$;p@DF+3BSOqk&I;WVB{<8z8Ua!x06G>(QUK!T0r zIIcEv85#yapYNIr2Y`8K26%PF%-9=rU) zjAv+)e$^xPt^K8eNVK$uAb;$GtL}^vg6Hkl(55UnT8(VaYRTZV7q$ZCycL zarm?vX{^$FaN-no<}?kwLF!Rds#@)lhj8+{*WBdWFUir!; zjdu*X)lH{mW~}{XuW!4t$ZEvyfD zR^snRCyskfpjzP=B~azS3;|r#L%s>>KiE0o!cRmR&=jn^>@DyiOZENLXb^FUcz0Rk zh6YaJW61N!iDuR-mYO^wF2X{2`XDV{@?Xd3Md20nw;h#Ff-%fvAB#cXMJ@{0(}I_u z>{63C?Ogwc8a{0KPT(?IRHm97N#9SajoE{d3)+(?UKCUgsX;~va>Q7uMR6UKIE{gV zF_y11_&wVRFuFQ>U99}J;b7Wg6;p*XY~3BLcy1VD`y6*oze8?`ttf1wTvL7F`aSgh zoJU`ORB_C=usl#e@>jisveOSjJy!{zDB0Yw#MwB{lh`+iw^5a*y|WRoyDvS+QDC)k zG2RDD_4Z55C%Jm2UyD^#0!u+gjco)@t*^?1AZ;WeaK5zFv7;@MTF<8Q-Rv(+JkQxY z1)o@OMI_J^4LH-k>_f}Nf+;h+)Fwg!;Jy+U%dly_mdwe1-G+rRewe*n$aRx!GBB2F z7hDoGwNZpU()@7+u3#tB$I4W->{HH#Qo-&zeYVMw-*oL67?*h%sEIwr(NCvqzbowW zK(%0YhciFR4;HL9zMf9II)f8t8Unw1n41B;mn7on1r(9u+IFTyp>*;^iJEV%wHjSQ zG>njon;1h2K6%adF($3YnU1T-f=P?Zc-hHRV;W zq%_8dnjR`+JCIGM#ao;XE-}VS85R}d%GE!~I$xt5;Wjes zLP=-U%9g)nnE3eq*)gR2)}D2>WQo_T6u9repx^yrhaz)1?)&416_ZYW%*Zf|lN8v~ zR?($p9`T0tL!O;LkEr7u+vAzjoT4;Syw73LoSaEP#FcA%l_O1*y*M$`4MR{e|Fwj@ z;#ZY-nj#9yrfyjB`U#0;AR6TWnjH>2fx!4@w+`uY#fCvLA=L5qY+cerqL(!i3sT^b~j}O0P!r9ji2+Rd%1w4NM;9Zp@^@mSbyzA2Pk3w^4$(yZ9 z+#CF^G`wdo?=Wi4!EhnHb)?^*3Pi_qe7IOC1;^>-{O~50f)sR49G}_pB2)fNax%>n zF|O&JWDJSo&5i$^*qFcgG#vh#g}N3?We&qD+_Wbznh@r~XgvJFH5NEvuQ9q z5G;;1%p+(w^*LafPjkW7$0fPFK=jeJdBQP165MdTr0w14O-pd367VP!($uzt1)t>8(vH3d!H-oSa1uN3Z1HPR10J^5}9>ksej2 zrN$O|e4&m2QmFGPw-+^CAcR{HcV|kO*8q;arA(r^4=AzqH?vb!WAvr&3e$lG8;@9BdDFW zV%|8D)A&aChXtVowsl;w&h^FxIv%~iUK5tMPL}Rkek^&~Va-Z@NBopI{pj6y|015LSMHC=|7YCDzC`tqy1zPNM`!t;Kc8 zCOdkG*-z!z?}Q^05@sdl%LaQ#(tv>)lM05kCObyf*Zoc%lopdOwNwK02hX-$uhgZ} zP|n7DI!+%<1%&Yyn!VJ1(}W(!(!sI{{aE@{Pi@h;NR9OZrC!;a#9zzQ45!GZ6a2Y_ zGq*{GI5A5IWzJ@!d^M5^Z`=}~HMd;OwZj`XbNEgmaM$o>KepxR*_%W!5rW(PS#-Bx$>&wi zpB3Yqn^!IyOD*L*EVsAT(@a7tsN0iT*&-eu|Jnr`Pb>SsXhwh25S`5yeWLv&-`BxH zf2UmG$&0Ftl#|Ed-<`i0+`PZ^2J|0b8>zk}l0}DR<bary7r1d_)KGY-38-Gd5XZ}q9&6^iD;!*Sf(3H| zWfe~PxltDtl{YIHuRT+Leg%~c`(kgZT?#&IiiA|vYP zqx(ReY3zQ1@5>%2oq7&&sOOeE)x#9lsiAiC;R}+UDXA^AFw7&J&Pcb4V}1Ts^*Aap)Sb;Q@GSpqZQH6c$Lf8U8`CrU>5YrEp-qFsw<#Q<=YRswl&zt(Oc299f>clJH&azDrN*#wLPf zA_s@rVJIPnjdaakGT>hRZhw!WD>{DH5J`RF(Qz8E$yF$gaMM&}2rf8GzR(Qfh-7zxSRYn! zZSFdys$AS{FEL?Vr!lsBPp)BhCyT5k(mI70T`ryCGl($oNB3|-xTXGY+ z*n540W*(r2n{p%QxGU+gbO;KwSX3^M>99n%^bKP{=HS3s2JU(<7m(tC*5zAby|>rL zO;=Rzi$)(*NLF};mU#)jSLaWfDO(OSdhA?GN9EF{^x9|!CF%qyvu;HJD8{Ey`yBi$ zlM=RKR)XN>4QeUYh!7{VJ+B$P@|~vk9{SenzMT=z?ji2))GV3)s+6SpOfEWQ!dyOd z_@G8bJByh1yrAiyG=~Ya2h(NYG^A)*Kab6`QxtL~zag$2@PwiGoU#PCY*%E(>dQG| z{Rv52La3QM2kt>Ga>a6Zl{R~`P^RFh&E34{X*T< Date: Sun, 8 Nov 2020 17:45:29 +0100 Subject: [PATCH 3/3] Use Interlocked.Increment(ref Unsafe.Add(ref histogramBase, luminance)); --- .../GlobalHistogramEqualizationProcessor{TPixel}.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 0c5a109a62..488426f939 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -115,6 +115,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization #endif public void Invoke(int y) { + ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); int levels = this.luminanceLevels; @@ -123,8 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization // TODO: We should bulk convert here. var vector = Unsafe.Add(ref pixelBase, x).ToVector4(); int luminance = ImageMaths.GetBT709Luminance(ref vector, levels); - ref int histogramAtLuminance = ref MemoryMarshal.GetReference(this.histogramBuffer.Slice(luminance)); - Interlocked.Increment(ref histogramAtLuminance); + Interlocked.Increment(ref Unsafe.Add(ref histogramBase, luminance)); } } }