casdoor 是我用过最垃圾的开源项目!
没见过代码结构如此混乱、代码逻辑如此混乱的项目,基础功能的 bug 一大堆不修复,整天在开发那些收费项目,github 上用中文提了一个 issue ,直接被删了,真牛逼。
没见过代码结构如此混乱、代码逻辑如此混乱的项目,基础功能的 bug 一大堆不修复,整天在开发那些收费项目,github 上用中文提了一个 issue ,直接被删了,真牛逼。
文|西坡
故事从那天打开虎嗅网站开始。这年头,可以看的网站不多了,我一直觉得虎嗅是可以参考的一个。
没想到那天虎嗅的头条赫然是:《漂亮国的金融阴谋论,让人不寒而栗》。下面是一个我早就讨厌的名字。
现在的网络上,我讨厌的东西就像福寿螺的粉红色卵一样,无处不在。我尽量躲开它们。可是一天不留心,昨天还干干净净的芦苇杆上,又被下了一坨。
庄子说,吾生也有涯,而知也无涯。我说,吾生也有涯,福寿螺的繁殖力而无涯。以有涯随无涯,殆已!注意,我又要强行讲课了。庄子在这里并非要鼓吹知识无用论,而是说以谁为主。如果以外部知识为主,那就殆了。所以庄子的解决方案是:“为善无近名,为恶无近刑,缘督以为经,可以保身,可以全生,可以养亲,可以尽年。”
要以我为主,不要贪多求全,不要追求外部虚名,把知识真正学到自己身上,只跟真正与自己有关的知识相处。这对身逢知识大爆炸,深陷知识焦虑的现代人,其实有相当的启发。
关键句“缘督以为经”,意思是顺应生命自身的节奏,不要被外部的他人的节奏扰乱。缘:循,顺应。督:督脉。人身前的中脉为任脉,人身后的中脉为督脉,任、督二脉为人体奇经八脉的主脉,主呼吸之息。
好了,回到我和福寿螺的爱恨情仇。
按照庄子的启发,我也不能以我的有涯“随”福寿螺的无涯。记住了吧,以我为主。福寿螺泛滥成灾,我只能从我无力做主的空间里撤出来,专心营造我认可的语言环境。东坡当年开垦东坡没少费劲,西坡今天也要开垦西坡,或许当年我在选择这个笔名的时候,命运就已悄然注定。
然后是前两天,一个微信群里,一个对语言素有洁癖的朋友脱口而出一句“东大……”。他是讽刺的用法,但我也愣住了。我一直以为这样的朋友会把这些词拒之口外的。难道很多东西不都是从不以为然发展到习以为常、无能为力的?
既然所有人都在用,那么这种词是否已经成了人畜无害的中性词?我的敏感是否是有必要的?
昨天晚上睡觉的时候,我思考了好大一会儿。如果因为语言洁癖,而失去所有朋友,那当然是我无法承受的代价。语言是交流用的,只能自言自语多么可怜。但是我想,情况也未必会发展到这种地步。
重要的是,我得吼出来。
我发现很多恶劣的现象,在蔓延的过程中,因为没人站出来怒吼一声,所以每个人都默默地忍受着。你以为别人觉得是合理的,所以不好意思讲,别人也以为你觉得是合理的,所以也不讲。每个人都觉得不合理,但都不讲,然后这种不合理终于变成了合理,无人能挡。
那么今天我就明说了。我拒绝使用“东大”“美丽国”“小日子”,不管你对相关国家、相关事件持有何种态度,你都可以使用体面的汉语去表达。
有人说,便于审核。这个因素我当然知道,我也很无奈,但我同样认为这个理由不充足。
我所厌恶和拒斥的,是人们在使用“东大”“美丽国”“小日子”这些词汇时,相互传递的那种漫不经心,以及对这种漫不经心的自鸣得意乃至自命不凡。仿佛使用了这种切口,就成了某种“自己人”。我就是要断然自外于这种慵劣的精神共同体。
我不愿意为了一时的便捷,一时的不得已,而永久性地伤害我珍爱的语言。
顾炎武曾经区分“保国”与“保天下”的不同:“保国者,其君其臣肉食者谋之;保天下者,匹夫之贱与有责焉耳矣。”保卫语言就是“保天下”层面的事,匹夫有责。
有的读者看到我写过的一些“责之切”的文章会问,你想做叶文洁吗?统一作答:我不是叶文洁那个类型的,我对中国文化有深深的认同与眷恋。我绝对无法容忍这里的“中国”二字被置换为“东大”。
补充一则旧事,刚上大学的时候,我还是个中二少年。当我了解到这所大学里,大多数人毕业后都会出国,大多数出国的都不会回来,我曾经感到很大的不解与失望。如果是这样,将来谁为我们所来自的人群负责呢?后来我当然释怀了,大多数人只是想搭乘既有的列车,过自己的日子。但我也不为自己的中二后悔。
当时我也曾仔仔细细地思考过,我发现我不愿意也无法离开中国文化。于是我做了一个决定,不管将来会发生什么,我得在这儿。后来每当读到“吾国吾民”这四个字,胸中都会有莫名其所以然的起伏。共同经历,这对我很重要。
时代给我们提供的选项,不仅有现成的道路,而且有现成的困难,我们对困难的克服会成就独属于我们自己的道路,也会为后来者减少困难。
我始终相信,“正确的语言是正确的生活的核心。”我的嘴里住着我的家,不是什么垃圾话都可以随便混进来。
历史告诉我,这种垃圾话都是有寿命的,我等得起。在等的过程中,我就在我的坡上待着。如果远方有还未谋面的朋友,和我一样对自己使用的语言有要求,那么他至少可以来我这里。
最近一直都非常忙,所以连续 20 来天都没有碰过 Midjourney 了。前两天在社交媒体上看到,新推出的 V5.2 中有一个向外扩写的功能,因为此前已经在 PS+SD 的组合中见过这类拓展画面的应用思路,所以很想看看 MJ 的 Zoomout 能做出什么样的东西来。趁着端午假期这个空档,我集中跑了几波测试,有一些小小的心得,在此记录一下。
总体结论有三个:
1、Zoomout 可以无限次数地向外扩展,但随着镜头的拉远,Midjourney 自身的联想能力并不足以做出任何有意思的画面,不刻意控制地放大出来的画面,到了第 3~5 步之后,就会明显变得乏味和缺乏美感。
2、通过刻意地控制画幅比例、扩张倍数,以及针对性地调整 prompt 的描述,可以利用这个功能讲出有意思的故事。关键在于,使用者对于「镜头语言」的理解,以及对运镜和故事之间联系的掌控程度。
3、对工业设计的辅助甚微,做点「花活儿」可以,一旦涉及到逻辑,依旧不行。
测试内容目录:
1、通过默认的 Zoomout X2 按钮连续放大 3 次
2、通过默认的 Zoomout X2 按钮连续放大 15 次
3、通过自定义 Zoomout 微调构图
4、通过自定义 Zoomout 构建人物画像
5、通过自定义 Zoomout 构建人物性格
6、通过自定义 Zoomout 完善场景氛围
7、在 niji 中应用自定义 Zoomout 构建人物和场景
8、自定义 Zoomout 构建情绪与故事
9、通过焦点变化构建故事的场景
10、通过镜头变化,构建故事的起承转合
以下为部分测试过程记录:
test case no.1:通过默认的 Zoomout X2 按钮连续放大 3 次
操作方式:连续 3 次放大图像两倍,不对 prompt 进行修改,也不对画幅做设置。
输出成果:在奔跑的场景中增加了后方的人,有一点点故事性,但继续放大后会明显失焦,花面焦点始终在最开始的小女孩身上,继续放大生成的场景和人物都是模糊的。
test case no.2:通过默认的 Zoomout X2 按钮连续放大 15 次
点击以全屏查看图片 Click to view the image in full screen
操作方式:连续 15 次放大图像两倍,不对 prompt 进行修改,也不对画幅做设置。
输出成果:外围拓展的场景越宏大,有效信息和故事性就越低,除了在阴影中无意间冒出的人影,没有任何惊喜和意料之外,拓展的画面也很单调乏味。
test case no.3:通过自定义 Zoomout 微调构图
点击以全屏查看图片 Click to view the image in full screen
操作方式:不对 prompt 进行修改,按 1.1 和 1.2 的拓展比例小幅度调整画幅。
输出成果: 初始图像是近景特写,根据图像本身的特点,对画幅进行小幅度地微调来获得完整的全景镜头,以及合适的构图比例。
test case no.4:通过自定义 Zoomout 构建人物画像
点击以全屏查看图片 Click to view the image in full screen
操作方式:先生成一个黄色漩涡图案,然后拓展时改写 prompt 为一只眼睛,进而生成一个带特征的面部局部画面,再次拓展时修改描述词为一个洞穴中的原始部落男性。
输出成果: 成功构建了一个有目标特征「黄色漩涡瞳孔」的男性角色,通过控制拓展比例以达到最终效果—-人物整体和局部特征均得以完整呈现的画面。
test case no.5:通过自定义 Zoomout 构建人物性格
点击以全屏查看图片 Click to view the image in full screen
操作方式:先生成一个红色皮夹克的女性胸像,再改写 prompt 获得其坐在摩托车上的局部画面,再改写画幅比例获得完整的人物与车辆的全景照。
输出成果: 成功构建了一个有目标特征「红色皮衣+摩托车」的女性角色,通过控制拓展比例以达到最终效果—-人物细节和整体氛均衡的画面。
test case no.6:通过自定义 Zoomout 完善场景氛围
点击以全屏查看图片 Click to view the image in full screen
操作方式:在初次生成的几批图像中,选择合适的画风和画面主体,再根据已有画面特征修改画幅比例。
输出成果: 在选定风格和主体后,将竖幅主体拓展为气势更足的全景影像。关键是拓展比例并非默认的 2 倍或 1.5 倍,而是根据实际需求来控制比例,同时也需要关注怎样的画幅比例可以传达对应的氛围。最终图像画幅比例是 3:1,适合展现有足够细节的宽幅场景。
test case no.7:在 niji 中应用自定义 Zoomout 构建人物和场景
点击以全屏查看图片 Click to view the image in full screen
操作方式:
step 1、使用 niji 5 的 style original 生成一个细节丰富的初始人物;
step 2、以 1.2 的 Zoomout 比例纵向拓展出人物的半身画像,画幅比例是 1:2;
step 3、以 1.1 的 Zoomout 比例和 2:1 的画幅比例重构画面,得到外围场景;
step 4、以 1.2 的 Zoomout 比例和 3:4 的画幅比例重构画面,生成人物全身像;
step 5、改写 prompt 添加「宫殿」关键词,以 1.65 的 Zoomout 比例和 3:2 的画幅比例重构画面,生成人物在场景中的全景画面。
输出成果: 虽然人物细节和场景氛围的融合程度还不错,但因为漫画角色的细节较多,在多次 Zoomout 的过程中,场景的丰富会逐渐抢掉中心人物的视觉焦点。因此在每一次修改画幅比例与关键词的时候,需要多加注意对视觉元素的控制。
test case no.8:自定义 Zoomout 构建情绪与故事
点击以全屏查看图片 Click to view the image in full screen
操作方式:
step 1、生成一个情绪和神情符合目标的初始人物;
step 2、改写 prompt 同时添加「马」关键词,以 2 的 Zoomout 比例和 3:4 的画幅比例重构画面,生成后续画面的基础,此时需要注意人物与马的位置关系,否则后续生成的画面会非常扭曲怪异;
step 3、以 1.05 的 Zoomout 比例和 2:1 的画幅比例重构画面,生成完整的马匹造型与部份环境信息;
step 4、对比改写 prompt 产生的变化,黑发组不改描述词,以 1.1 的 Zoomout 比例和 3:4 的画幅比例重构画面;白发组添加「巨大镜子」关键词,以 1.6 的 Zoomout 比例和 3:4 的画幅比例重构画面。
输出成果:通过控制 Zoomout 的幅度、画幅比例和 prompt 的调整,可以生成指定场景的画面,且人物的神态到位、情绪饱满,整体画面焦点清晰。但美中不足是,构图不够自由。
test case no.9:通过焦点变化构建故事的场景
点击以全屏查看图片 Click to view the image in full screen
操作方式:
step 1、生成一个在河岸上的粽子;
step 2、修改 prompt 为「熊宝宝正准备吃粽子」,以 2 的 Zoomout 比例和 3:4 的画幅比例重构画面;
step 3、修改 prompt 为「小熊一家在野餐」,以 1.2 的 Zoomout 比例和 4:3 的画幅比例重构画面。
输出成果:通过对 prompt 的修改,控制 Zoomout 的幅度、画幅比例,可以改变画面中的焦点和表达主题,适合不同文化元素之间的混搭。
test case no.10:通过镜头变化,构建故事的起承转合
点击以全屏查看图片 Click to view the image in full screen
操作方式:
step 1、生成一幅鲜花山谷的画面,人物要明显;
step 2、修改 prompt 为「一面巨大的镜子在草地上」,以 2 的 Zoomout 比例和 3:4 的画幅比例重构画面,此处竖构图是为了生成较高的全身落地镜;
step 3、修改 prompt 为「少女站在镜子前」,以 1.5 的 Zoomout 比例和 3:2 的画幅比例重构画面,改为横构图是为了囊括少女全身以及环境信息。
输出成果:通过改变画面中的焦点和增加元素,在镜头逐渐拉远的过程中,故事缓缓托出。
我的整体感受是:
通过 Midjourney V5.2 的 Zoomout 无限拓展,一次次修改画幅比例、提示词内容,可以用镜头语言的变化来讲故事了,也可以基于一些初始的「点子」延展成有意思的融合作品。但越是这样,越发显得对话式、指令式的交互界面( SD 那种也不算图形交互 )的局限太大了,我很希望今年之内能发展出图形交互界面。
没错,今年 AI 的爆发指向了一个新的趋势:对话式交互界面。但人类之所以发明绘画,开始通过设计图来制作各式各样的新工具,恰恰就是因为语言本身的效率太低。这个逻辑其实也可以从媒体形态上找到端倪:文字–> 图像–> 视频。仅仅依靠对话,我们无法构建出一个一把剪刀;仅仅通过语言表达的播客,也无法传达任何需要视觉才可以精准理解的信息。对话指令的交互界面与图形交互界面之间的关系,并非只是 dos 和 windows 之间的差异,更重要的点在于,后者可以更直观地完成交互,以及精准地进行创作行为。AIGC 的重点不仅仅只是 AI,而是我们如何使用 AI 进行「Generative Content」。
我说一句话,AI 给我一个东西,这不是创作。
创作是一个生命在主观意志的驱使下,刻意的、有目的地表达其心中所想。
因为 GPT 的爆发而说对话式交互是未来,这样的断言是过于冲动的。只要是一个严肃的创作者,就会立刻意识到,真正的创作一定需要多纬度的交互界面。这其中不仅仅包含对话指令,同样更需要图形界面以及在数字虚拟空间中的三维交互。AIGC 工具与 PS、表格、PPT、思维导图等已有工具的结合,就是这类多维交互的雏形。
那一刻,我们不会等太久。
Midjourney V6 的质感和细节,真的是飞跃式的成长!
和今年三月相比,已经完全脱胎换骨了。对自然语言的理解和再表达,也已经在渐渐脱离「咒语」的局限,结合 ChatGPT 的语言转译,一个人能够用母语把尚不明确的观念表达清晰,愈发显得重要。
点击图片,可查看原始尺寸高清大图:
当 AI 越来越擅长理解人类的自然语言,我们就愈发迫切地要掌握「用语言表达思想」这件事情。
因为语言的精度和颗粒度,将会在人类与 AI 的相处、合作中,展现出人类智力的上限所在,以及外延的纵深能够得着多远。
在图像处理领域,我们会遇到如下需求:把图像中的目标物体和背景分开。比如背景用白色表示,目标物体用黑色表示。此时我们知道目标物体的灰度值相互接近,背景灰度值相互接近,那么用大津算法能很好把目标从背景当中区分开来。
比如对于下面这张灰度图片
目标物体是中间黑色的几何物体,我们想让这些物体和背景区分更明显一些,比如让物体为纯黑,背景全白。那么我们就需要找到一个合适的阈值,使图片上灰度值大于这个阈值的像素点为255(白色),灰度值小于阈值的像素点为0(黑色)。也就是变成下面这幅图:
那么大津算法需要处理的就是找到最佳的阈值,让目标和物体尽可能分离开。
为了找到合适的阈值,我们首先观察原图的灰度直方图📊:
这是用 Matlab 对原图形成的灰度直方图,灰度直方图的含义是统计图像中不同灰度值的分布情况。由图我们可以看出两个尖峰,在灰度值为0~20的地方存在一个尖峰,代表原图中有大量像素点灰度值为0~20,经观察我们可以认为这部分对应于目标物体。同理我们可以看出背景的灰度值大多集中于100~140之间,为了让目标和背景区分更加明显,我们想让目标物体的灰度值全为0,背景的灰度值全为255,这种处理手法也称为二值化法。
那么阈值取多少合适呢?从图上看似乎取50~100中的任意一点都可以,但是实际情况并不想参考图那样明显,有些图片背景和目标物体较为接近,我们需要一个算法来找到最优阈值才行。
首先我们思考什么样的东西才能成为一类,而我们又是怎么分类的。对于参考图来说,我们可以认为灰度值接近的为一类,灰度值不接近的不是同一类。那我们又是如何衡量灰度值接近的程度呢?这里面就需要用到方差的概念。
方差相比大家都了解,同一类的物体方差小,不同类的物体方差大。所以对于此图我们希望分类的结果是对于灰度值相近的同一类物体,它的方差最小,称为类内方差最小。灰度值不接近的不同类物体,它的方差大,称为类间方差最大。
所以步骤总结如下:
首先我们要形成参考图的灰度直方图,这样方便我们找到最佳阈值。
接下来我们通过穷举每一个灰度值,计算以此为阈值的类内和类间方差。
找到能形成类内方差最小的或类间方差最大的阈值,这个就是我们要找的最佳阈值。
下面以两类分割讲解下具体的算法,实际上大津算法可以分割多类出来。
因为 Medium 不支持显示 MathJax 语法的公式,所以对这部分感兴趣的直接移步到《大津算法(OTSU)》查看吧。
/* OTSU 算法
* *src 存储灰度图像,width 图像宽,height 图像长
* 返回最佳阈值
*/
int otsu(const int *src, int width, int height)
{
int histogram[256]; //存储灰度直方图,这里为256色灰度
int t,thred;
float wf,wb,ub,uf,curVal,maxVal;
int sumb=0,sumf=0,sumW=0,sumPixel=width*height;
wb=wf=maxVal=0.0f;
//求灰度直方图
memset(histogram,0,sizeof(histogram));
for(i=0;i<width*height;i++)
{
histogram[src[i]]++;
}
for (i=0;i<256;i++)
sumW+=i*histogram[i];
//枚举每个灰度
for(t=0;t<256;t++)
{
//求两类类概率密度
wb+=histogram[t];
wf=sumPixel-wb;
if (wb==0||wf==0)
continue;
//求类均值
sumb+=i*histogram[t];
sumf=sumW-sumb;
ub=sumb/wb;
uf=sumf/wf;
//求当前类间方差
curVal=wb*wf*(ub-uf)*(ub-uf);
if(curVal>maxVal)
{
thred=t;
maxVal=curVal;
}
}
return thred;
}