From f4c1775f72e8e254f0069350b537d3990f175047 Mon Sep 17 00:00:00 2001 From: Chris Mounce Date: Thu, 8 Aug 2024 01:52:31 -0700 Subject: [PATCH] Partially fix k-means and commit an example image --- blocked.png | Bin 0 -> 3285 bytes two-tone.py | 21 ++++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 blocked.png diff --git a/blocked.png b/blocked.png new file mode 100644 index 0000000000000000000000000000000000000000..596e43fc439e6c8d51fa335ba788d6109f19b124 GIT binary patch literal 3285 zcmV;`3@Y=9P)gf-Gk-jYOmbA(582f?0AELGsF* zuwj#p_mTH(EIi9v&X!Sx(a1!a`p)~Fs{X;woEnHR?o&^_=hUemCh-Rc{=hCrc4Y0$ z+S5v`C~Q%V3@|a!P<&qBgRTgOWaOZI6 z{rjY8^4fc07&&}Y0A{B^u%+}na-)&6p?gKxN+1sWqZ0>9JZ}L|l@_)nK}i>RDQ4}? z&f(!Q=;D zLLU6KX(aIe>FKHZi}Xdw4x^RL`0*8n9Rfgv9zpe~0xEjTrF+o8RUVq%O7P^L(En5Q z#Nmgm?;+SSyeg^=z!nYu`Xu_5)`slQi=5#=9_E}9cB=*o@T340J1ROVr~rIniwa$W zf}ee>nB;3p5Wvz>cd1p{s!aRIvcb zzX$^)gsz3AVrT3TA*tH&a~kOUZ=+yU1AH$?@V@;pHQiZs}NoV^?|9z=_>AiCBzn|xgIGijth+wf!9VRS5 zBUG>%n`o|cpoIAQFy=;yy6`)qKnH~laYz^+(Lf5QXgDiXLG~z<00{Rv8T9@C+25^g z2v8wsjDjd&{rB(RJ*Q=kMBMG09pr zG)*iVfMEGk>$6k`%;5ZvotydY{0<2B4S`z-K~4fILx2V|{?0=L5{SZi9{bjRO`h{Z z(7*%@9t0poDvJi#O!#Jrut+CrAbs!NK?x(z@9%FA3=QYPv5gw22w0kObZht@+dfp( zDNYAKNQYK6KpA|QV2$^23QH@b9Xv=JU%z~6tN(|qD$~WtoqQHJ(6~Ea)QcL*M{@A z9@OUpnZjYukRcOu+1Ge$L<4^02JPnd=JxiezBsBsSD(d!Jb$hZw9lVE2Z9rMZ5je-&_>~r4n8PC zB;fNWXi$D|f|P&)0Ree{a**W*V#XTL;5IPaRJRR5)S!J8G?1KM0>B(#gG&uG`K=v~ zr_;&QkLAVq?96-gJE$E-{OXl_$xnm97)A|9kRlJbJ>fiGuPLlutxy-A0H9s(nZ0~Z z{M7^brC&8sc$734DgpZyfXQ?`_I`Zk&#DM+(EBSA)Fkk(nG3^IG{E#@8TJ$3wT^>e zGNrurD<26&15{;}AdYP^U2_y(ApDuPi8oFC5f89nRGO*5-u^1jAwC*B&W2ov!^qJ@e1c z-<~xJuQUqc;I;QZ@;74MucwoANDbC(Vpsl;D{WWu`WxQ*K@WLH192esMqt?PSD?dt0J8yT1d1fT{4$}t2Gq?!j4 z1i}BB;1yGm1h3^s1OX19feCpK0n`Wp4Zg)N?Ct4vQkglW)ffIQGB_>c&D)POvAK15+CD^q=0Ur(;DuPc$ z#a6f5NmGXFFcqevPBSZ_1I`8c$Wz4`GmQ>prz!-K>xq1I2$>n8TpzvT~AQ|BEWea z2sq9lAqcK1ff&Y&fn)g5vXBM4QlKRcT*mM*PXHyTfdCHe7bOlgW<8m#*OLIigM$z} z1%i+VJU4Pz@DA45C}D)$I79$Jz#fUeCC2IL=@%Z~u|JUZRkjA$DX7ss_Kw{JG7azD z@V?_qJic!wC~(A5f=tuEfFQLk0UuWa*jJeRTit|<6!eClWA}4Cr}4M#l@D$cwh&Yw zc&EU0OWw1X;*LoX}G4 z=gxWvN!#TM0>0`8q4SXjVY>rdU2*$}%}5prtQCEvok=_W5)igJD7yrU1&^+FJ3+-$ zo5LQM=J+O#;-iHWBkdfykTof)mgjPQ;+cv2-_}|nw}>HFEKKH*6;Wl;*D`}RuI@})xg3P@juzJs_C)efwhJW-C zMi>Az8n~APs0KxvMgrWT5TQe`2$kS&4hdX4O?w1sB0Kmne%|F&A3s)=g9reK1a5=C z@@^>*v6HO~MQa8w9wm0Rg*) zeva2k0ssv}(^w6WhQH7N2#FnQ8;@BFFfHFLT;{U&`UGj<#*JY=HRY5w|F9@(Znx1*RYbiPeZMKtu7f5$=Tp}P5 zWH-ISS7-Hn!kx?u4H|;X^axCsfGZ0&`L?IQs;!PrHzs#;ZO90R&R?EtkWNWdpi3YQ zx&&~RH3E!%*qKs;F2QZHLO9)+s0hddA@Ex)5U2uu0<&EMcHbB@Xri!^C}4H``c>9R z4FZ$!P614|ajFa36wL#6-s%hqI(YyQc5B(;)PL)P&;Gf_o&xu(Nk~F59NqY}3cEu{ za2FADE7=%-Y&F;*h{dP@*d5Tp-_mf95eJvomza`COv0Pnkn0_SkO|BQvq5lq`Cl4j zeGRZzh6XCa^K;Okh?&R>oYg7-tPq4I6Xtrccs|%5sFh&6g&;G1f?e*`>%reJFXu^r zDFBmpF5FbNtp--l=b;)H&4sz{6aSU=Oaj^3Ug~Vzrh(}Ygp}lYfdq0I`}XZx^PuSF zfhMG+WK)pm8Ukj*IS2&c-+!Uuv+OG;I)o!!AaXVoT1=!Q?;5lF0zgYMitmz;_@jy} z%R3yxMW@a>tk&~bkTnhUI~GWIcf`ac>N1EZ21kkk1kx{RD6Urp02*BMG_Wl}0Eqbrgx;q{ zqaBUHQFufD$hKU%WsktL8e~i8C=s*crGtzwG{E0h_`eZ!0OqDCN@2I!(F*o|6c^T4 TOaUAT00000NkvXXu0mjfc%$A0 literal 0 HcmV?d00001 diff --git a/two-tone.py b/two-tone.py index c5ead60..e9f4603 100644 --- a/two-tone.py +++ b/two-tone.py @@ -44,15 +44,21 @@ def choose_color_pair_kmeans(counts: dict[RGB,int]) -> tuple[RGB, RGB]: def weighted_avg(colors: list[RGB]) -> RGB: result = [0, 0, 0] + total_weight = 0 for color in colors: weight = counts[color] + total_weight += weight for i in range(3): result[i] += weight*color[i] - return tuple(round(x/total_count) for x in result) + if total_weight == 0: + total_weight += 1 # Hack to handle division-by-zero + return tuple(round(x/total_weight) for x in result) # k-means to choose the two colors old_result = (None, None) - result = (darkest_color, lightest_color) + result = (min(colors, key=brightness), max(colors, key=brightness)) + print(f"k-means using {counts=}") + print(f"starting value: {result}") while True: lists = ([], []) for color in colors: @@ -63,7 +69,9 @@ def choose_color_pair_kmeans(counts: dict[RGB,int]) -> tuple[RGB, RGB]: else: lists[1].append(color) old_result = result + print(f"{lists=}") result = tuple(weighted_avg(cs) for cs in lists) + print(f"updated: {result}") if old_result == result: break return tuple(nearest_color(x, palette) for x in result) @@ -119,7 +127,10 @@ def two_tone(img: Image, xy1: tuple[int,int] = None, xy2: tuple[int,int] = None) if color not in counts: counts[color] = 0 counts[color] += 1 - dark, light = choose_color_pair_luminance(counts) + #dark, light = choose_color_pair_luminance(counts) + print(f"choosing color pair for {xy1=}") + dark, light = choose_color_pair_kmeans(counts) + print(f"final: {dark=}, {light=}\n") if light == None: # Only one color, *ought* to be a no-op light = dark @@ -138,3 +149,7 @@ blocked = img.copy() for x in range(0, img.width, BLOCK_SIZE): for y in range(0, img.height, BLOCK_SIZE): two_tone(blocked, (x, y), (x + BLOCK_SIZE, y + BLOCK_SIZE)) +blocked.save("blocked.png") +for x in range(5): + for y in range(5): + blocked.putpixel((x, y), 0)