duangsuse::Echo
709 subscribers
4.23K photos
127 videos
583 files
6.44K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
其实 extract-subtitles 完全可以移植到 Java,关键依赖只不过…… 是 OpenCV, Tesseract, 和一个从信号取得所有峰值索引的 peak estimation 算法
不知道 Python 用 ctypes 绑定一些简单的 C function 还是用 JNI 简单(
不过也必须有 Iterator 并且能实现 chuncked 方法,要不然处理点高清视频 JVM 上照样卡死。( java.util.stream.Stream 里面已经有了 reduce 方法,不过那个是新的 Java API,Android 里也木有…… )
This media is not supported in your browser
VIEW IN TELEGRAM
Forwarded from dnaugsuz
#Kotlin #code 简单经典的状态机:

fun String.stripBetween(pair: Pair<Char, Char>): String {
var state = "initial"
val sb = StringBuilder()
next@for (c in this) {
when (state) {
"initial" -> if (c == pair.first) { state = "inner"; continue@next }
"inner" -> if (c == pair.second) { state = "initial"; continue@next } else continue@next
}
sb.append(c)
}
return sb.toString()
}


"hello[ds]world".stripBetween('[' to ']') == "helloworld"
🤔 来说说吃饭路上想的两个设计啊。 #Learn

1. ParserKt 依然是 PWOC(Pattern/PatternWrapper, OptionalPattern, ConstantPattern)
SDRIES(Seq, Decide, Repeat, item, elementIn, satisfy)
CCDPAC(Convert, Contextual, Deferred, Piped, AlsoDo, Clam)
SJIT(SourroundBy, JoinBy, InfixPattern, TriePattern)
NLL(NumUnitPattern, LayoutPattern, LexicalScopedPattern)
此外还有 GreedyPattern, TextPattern, LexerFeed
TriePattern 也有特别的 PairedTriePattern 和 DictPattern
将来 ParserKt,我会争取让人能“完全理解”这个框架。(在我的眼睛里,这个问题本应该非常简单)

2. 当然是 MontagePY 了,我整理出了一个参数表,以及提供了一个基于 Fold 以同时实现 count diff pixel & color average 功能的方法(Fold 真是一个“宝藏架构”……),下面我会叙述自己的设计思路。
先说说 Fold 吧(它不过就是把 create, loop-modify state 的模板代码给解耦成 constructor, accept, finish 了而已,同时这也可能增大 GC 压力但 Java/JS 不就是这么用的么)
我觉得 Fold 对 ParserKt 真是超赞啊,之前没有在工程系 parser tool 里见过能直接把输入变成 Int 而无需弄什么 parseInt 啊 toInt 啦的流程(其实这个东西在比较学术的 parser comb. 实现里也很常见,比如 Haskell 有 lazy list 的情况下,只不过那些语言复用性都太弱)
其实许多东西都是原来早就存在了的(解析组合子不就是那几个东西嘛),只不过 ParserKt 把它们融入自己的灵魂、强调强调再强调、带到了工程实践中来而已。其他方法也很好,只不过总结方法没有 ParserKt 这么直白而已。
Fold 的两个操作 accept 和 finish 的字符数目竟然都是一样的,我非常喜欢(不像 Observer 那 onNext onFinish,虽然我也没更好方法(onDone 太 trivial) ),最近有点强迫症……

整理的参数包含三类:
字体| font, font-size, font-color
图像| mode, scale, spacing
微调| draw-color-code
当然还有
输入| image, seq

🤔其实蒙太奇图可 hack 性是比较大的,比如 draw 的也不一定是 text 什么的(做了接口抽象出来了)
比如可以弄很多 icon,给它们加能调颜色的 filter 而拼成一幅图画,不过现在暂时没法给安排成(整体就一 Callable[[List[String]], Iterator[TintedDrawable]] 的问题,关于 cycle-ing chars 呢…… 也可以给放在绘制函数的外面不必强求无限循环,更加灵活)

整个弄完后我觉得可以搞成 GIMP 插件的形式,不过不知道如果提交到预置里会不会不能弄自定义代码了,emmm。


……啊,漏掉了关于 Fold 的设计。
原来 draw-color-code 的要求很简单,就是要么画(求一个 color)要么不画
(题外话,原来我给它叫成 draw-unless 了,脑子里当时想的是 draw-unless = hide-if = 最直白(而且能强调它是否定)的肯定形式…… 很糊涂别试理解,后来我给改成 draw-color-unless 的时候我想的是要么返回原有 bool 要么是 Color ,结果就没发现实际返回只能是 (Color|False) 应该换成 (Color|None) ……这都是动态类型的锅 ||@_@| )

可是这个过程…… 如果按照原项目参考,我一下子合并了三个参数:
+ keyColor: 生成图的背景色,同时也用作原图“背景”的参照色
+ keyThres: 如果所有通道的 distance 求和大于这个值,视为不同色 (颜色模糊匹配)
+ keyRatio: 某块的同色达到这个比率即为背景块,不作绘制

如你们所见,我把整个参照合并为了一个函数: Callable[[Image], Nullable[Color]]
但是我仍然要移植原算法,就需要解决一个数据依赖问题:
—如果我 lambda it: colorAverage(it) if ... else None
……等等,我们哪里求了要靠 keyColor/keyThres 算出的 n_difference ? 条件是 n_difference / it.width*it.height > keyRatio 或者说 n_difference > it.width*it.height*keyRatio 才可以啊?
如果专门为此定义一个函数(这还不谈到底把一个色块遍历了几遍),则灵活性又下降了不少……

就是这样,一个子程序要遍历一遍,求出两个数据以供判断。
(关于 lambda 里不能定义局部变量怎么解构的看下面)
lambda it: let(lambda *ac_p: ac_p[0] if ac_p[1] > it.width*it.height*keyRatio, AllFold(imagePixels(it), MapFold(Averager, 3), colorDistanceCount(rgbColorFrom("#FFFFFF"), 10) ))

所以 class Fold 这种模式不仅有利于循环逻辑的解偶,还可以提升代码的灵活性( 1个fold 对应 N组数据(MapFold) 还是 N个fold 对应 1组数据(AllFold) 都OK )
突然觉得这个 lambda 写得好…… 烂。好了就说这么多。
迫真重构小讲堂(草
duangsuse::Echo
我不是很喜欢最后那个算 padLeft 和 padUp 的 大致就是 lineW = (cfg.font_size + sp_h) * scale lineH = (cfg.font_size + sp_v) * scale cols = int(image.width / lineW) rows = int(image.height / lineH) padLeft = (image.width - (cols*lineW) + sp_h) /2 padTop = (image.height - (rows*lineH)…
(h_sp, v_sp) = spacing
(w_item, h_item) = (d+sp for d, sp in zip((font_size, font_size), (h_sp, v_sp)) )
(m, n) = (int(full/d) for full, d in zip((width, height), (w_item, h_item)) )
padLeft = (width - (m*w_item) + sp) /2
padTop = (height - (n*h_item) + sp) /2

🤔 两点。 (0) 即便 width - (m*w_item) aka. width - (int(width/w_item) *w_item) 可以用 width % w_item 替换(并且甚至可以上 Python 的 divmod)我也没用(因为我没发现不止可以用 divmod,哈哈)
(1) (a/2+b/2) 不是永远都可以合并去 (a+b)/2 的,比如浮点高精度,但这里可以。
(2) 即便可以把 padLeft, padTop 写成 value destruct 的形式,我也没那么写,这都是为了可读性。
duangsuse::Echo
(h_sp, v_sp) = spacing (w_item, h_item) = (d+sp for d, sp in zip((font_size, font_size), (h_sp, v_sp)) ) (m, n) = (int(full/d) for full, d in zip((width, height), (w_item, h_item)) ) padLeft = (width - (m*w_item) + sp) /2 padTop = (height - (n*h_item) + sp)…
下文直接上骚操作:
(width, height) = imageBounds(image)
global font_size

(h_sp, v_sp) = spacing
(w_item, h_item) = (font_size+sp for sp in (h_sp, v_sp) )
(m, n) = (int(full / item) for (full, item) in zip((width, height), (w_item, h_item)) )
(padLeft, padTop) = (int( (full % item + sp) /2) for (full, item, sp) in zip((width, height), (w_item, h_item), (h_sp, v_sp)) )

*最后一条 zip(a, b, c) 真心不推荐,这条是真闹着玩的。
This media is not supported in your browser
VIEW IN TELEGRAM
这段代码可不止是我意淫出来的,而是真的可以用 :P (虽然这个例子相对简单)
duangsuse::Echo
这段代码可不止是我意淫出来的,而是真的可以用 :P (虽然这个例子相对简单)
for i in range(0, n):
x_text = padLeft
for j in range(0, m):
area = (x_text, y_text, x_text+w_text, y_text+h_text)
shadow = image.crop(*area)
x_text += w_item
avgc, is_draw = averageColor(shadow)
if not is_draw: continue
draw.text(area[0:2], next(chars), font=font, color = avgc if cfg.font_color == None else cfg.font_color )
y_text += h_item

这段代码也不是我瞎写的,也是真的可以用(
最喜欢能够直接替换上去,试运行的代码了

cfg.font_color = rgbColorBack(cfg.font_color) if cfg.font_color != None else None
y_text = padTop
for i in range(0, n):
x_text = padLeft
for j in range(0, m):
area = (x_text, y_text, x_text+w_item, y_text+h_item)
shadow = image.crop(area)
x_text += w_item
avgc, is_draw = getAvgColor(shadow, keyc)
if is_draw:
draw.text(area[0:2], next(chars), font=font, fill = cfg.font_color or rgbColorBack(avgc))
y_text += h_item
这个调试过才是可以直接替换的(看来还是不能脱机颅内执行
不过,我对 y=padTop; for i { x=padLeft for j { x+=w } y+=h } 这种风格好像也没本质帮助,要好好重构只能上 LayoutPlanner 了(
This media is not supported in your browser
VIEW IN TELEGRAM
duangsuse::Echo
不过,我对 y=padTop; for i { x=padLeft for j { x+=w } y+=h } 这种风格好像也没本质帮助,要好好重构只能上 LayoutPlanner 了(
不过虽然不能杜绝这种 y=padTop; for i { x=padLeft; for j { x+=w } ; y+=h } 的风格,要说方法倒是也有
很简单,不一定要给 i, j 留位置因为它们根本就没被引用!实际上它们只是相对于 x, y 的“计数变量” (i, y = enumerate(range(y_min, y_max))) 而已。
亲数学系的程序员也可以用 i*h_item 直接替代 y,如果你不嫌每次算很麻烦的话(不过这倒是一种很吸引人的做法……因为这样就可以并行计算,虽然众所周知绘制都一般是单线程的)
不过反正 Python 和 Kotlin 一样提供了 range(不知道一些静态语言为什么没有,emmm),所以只需要 for y in range(0, height, h_item) 即可…… 连 i, j 和 n, m 都省了 😂 反正 n = height/h_item
duangsuse::Echo
不过虽然不能杜绝这种 y=padTop; for i { x=padLeft; for j { x+=w } ; y+=h } 的风格,要说方法倒是也有 很简单,不一定要给 i, j 留位置因为它们根本就没被引用!实际上它们只是相对于 x, y 的“计数变量” (i, y = enumerate(range(y_min, y_max))) 而已。 亲数学系的程序员也可以用 i*h_item 直接替代 y,如果你不嫌每次算很麻烦的话(不过这倒是一种很吸引人的做法……因为这样就可以并行计算,虽然众所周知绘制都一般是单线程的)…
#Learn #dev #tip 有些人可能看不习惯 n_item, x_item, y, h, w, i, j, n, m, 甚至 h(orizontal), v(ertical) 这类缩写中 n x y 这样的字符总被放前面的情况(
其实在物理式子的命名里,这种做法很流行(事实上,物理系命名法相当接近日常编程会使用的命名方式了,它一点也不“数学”),它把“类型”缩写放在命名的头部。

众所周知,本 duangsuse 是推崇零文档的(代码就是最好的文档
而不是把文档写成逻辑的重复,文档只应描述一些特殊的值/状态要求。

一般我们说“中文编程”排除某些“机器翻译”实例之外,另一些实例不能写出优雅好看的大项目的原因也在于此——相较英文,中文把最能体现一个句子意图的词放在了最后面,这个问题使得它在程序设计上阅读起来有点不舒服。
然后就是这样,不对我少写了个 / 2 ……
嗯…… 我知道为什么重影了。 边角处重影是因为 y 和 x 的区间过大,过大是因为 range 并非整除区间, start <= i < stop 就可以,y,x 的最后一次迭代 item 的 h,w 将不足。 要切换模式,可以预先把 stop 变成 it - (it%d_item) aka. it-(it/d_item)*d_item
这个版本大概就是这样
🌝 #Python #code 这个宇宙太疯狂,大海掀翻小池塘~