使用cmath优化Python分形渲染后出现黑色边框问题求助
曼德博集合渲染:解决优化后的黑边问题
我编写了一个曼德博集合渲染程序,优化后渲染时长从40秒缩短到12秒,但迭代次数增加的区域出现了黑色边框。问题出在使用cmaths.isinf()判断发散的逻辑上——部分已经发散的点还未达到inf状态,被误判为属于集合(渲染为黑色)。以下是问题详情和解决方案:
完整原代码
from PIL import Image from PIL import ImageShow from PIL import ImageColor as ImageColour import cmath as cmaths colours = [ "navy", "darkblue", "blue", "cornflowerblue", "lightsteelblue", "lightskyblue", "turquoise", "palegreen", "lawngreen", "greenyellow", "yellowgreen", "goldenrod", "gold", "yellow", "darkorange", "orange", "brown", "maroon", "red", "deeppink", "darkmagenta", "mediumorchid", "magenta", "darkviolet", "slateblue", ] def testpoint(c, zoom): zofn = 0.0 count = 0 while not cmaths.isinf(zofn) and count < (5 * zoom): zofn = (zofn * zofn) + c count = count + 1 if str(zofn)[1] != "n": return -1 else: return count def mainprogram(): zoom = zoom = int(input("Set Zoom Level. (Default: 10) ") or "10") * 10 centre_x = int(input("Input centre x value. (Default: -50) ") or "-50") centre_y = int(input("Input centre y value. (Default: 0) ") or "0") halfw = result.width // 2 halfh = result.height // 2 for i in range((centre_y - halfh), (centre_y + halfh)): for x in range((centre_x - halfw), (centre_x + halfw)): coordinate = complex((x / zoom), (i / zoom)) value = testpoint(coordinate, zoom) if value == -1: colour = (0, 0, 0) else: colour = ImageColour.getrgb(colours[value % len(colours)]) result.putpixel( ((x + halfw) % result.width, (i + halfh) % result.height), colour ) print("line", (i + 250), "done") ImageShow.show(result) mainprogram() result = Image.new("RGB", (1000, 500), "Black") mainprogram()
代码差异对比
- 原判断逻辑(无黑边但速度慢):
while str(zofn)[1] != "n" and count < (5*zoom): - 优化后逻辑(速度快但有黑边):
while not cmaths.isinf(zofn) and count < (5*zoom):
解决方案
方案1:遵循数学定义判断发散(最优)
根据曼德博集合的数学规则:若复数z的模大于2,则该点必然发散。直接判断模的平方(避免开根号,提升速度)是否超过4,比isinf()更准确且高效。
修改后的testpoint函数:
def testpoint(c, zoom): z_real = 0.0 z_imag = 0.0 count = 0 max_iter = 5 * zoom while (z_real ** 2 + z_imag ** 2) <= 4 and count < max_iter: # 拆分复数运算为实部虚部计算,减少开销 new_real = z_real ** 2 - z_imag ** 2 + c.real new_imag = 2 * z_real * z_imag + c.imag z_real, z_imag = new_real, new_imag count += 1 # 达到最大迭代次数则视为在集合内(黑色),否则返回迭代次数 return -1 if count == max_iter else count
额外优化点:
- 修正
mainprogram中重复的zoom = zoom = ...赋值,改为zoom = int(input(...) or "10") * 10 - 移除
putpixel中的取模操作(x和y的范围不会超出图像尺寸):result.putpixel((x + halfw, i + halfh), colour)
方案2:修复isinf()的漏判问题
如果坚持使用isinf(),可以在循环结束后补充判断模是否大于2,避免漏判发散点:
def testpoint(c, zoom): zofn = 0.0 count = 0 max_iter = 5 * zoom while not cmaths.isinf(zofn) and count < max_iter: zofn = zofn ** 2 + c count += 1 # 补充判断:模大于2也视为发散 if cmaths.isinf(zofn) or abs(zofn) > 2: return count else: return -1
此方案性能略逊于方案1,因为复数运算和模计算的开销更高。
内容的提问来源于stack exchange,提问作者Jessica
相关产品推荐
相关产品推荐

