编辑:在等待查看使用Image.point()时可以考虑哪些可能的解决方案时,我想补充一点,我现在明白为什么我的表达式在ImageMath.eval中不起作用。
ImageMath.eval对整个图像执行单独的操作,而不是逐个像素地执行整个eval语句。所以当我问(a-b)>我真正问的是,如果(a-b)产生的图像是> 255.这是一个荒谬的问题。并且Pillow自动剪辑值,没有选项允许包装在源中。如果我对C ++和编译python库有了更好的理解,我很乐意分叉并改变xD。
但是在获得这些知识之前,我只需要在图书馆之外手动完成。
原始问题: 我有这个减法例程来相互减去图像中的两个通道。 (我使用中间人图像,因为如果我过早加入偏见,偶尔会得到奇怪的结果)
def subChannels(c1,c2,divisor=1,bias=0):
ecpression = "(a/%s)-(b/%s)" % (divisor,divisor)
middleman = ImageMath.eval(expression, a=c1, b=c2).convert("L")
return Image.Math.eval("a + b", a = middleman, b = bias).convert("L")
非常简单。它有效。除非我有偏置/偏移量将值推到255或低于0.然后它只是剪切结果。
现在我尝试添加一个荒谬的if else评估语句,试图为我做包装。
def subChannels(c1,c2,divisor=1,bias=0):
overflow = "((%s) - 256) if (%s) > 255 else (256 + (%s)) if (%s) < 0 else (%s)"
sub = "(a/%s)-(b/%s)" % (divisor,divisor)
expression = overflow % (sub, sub, sub, sub, sub)
middleman = ImageMath.eval(expression, a=c1, b=c2).convert("L")
add = "a + b"
expression = overlfow % (add, add, add, add, add)
return ImageMath.eval(expression, a=middleman, b = bias).convert("L")
那失败了。生成的图像只能得到一些正确的像素。其余的都被吹灭了,纯白色。真的很奇怪。
然后我尝试手动执行方程式,因为我知道在Python中的图像中循环每个像素会比较慢。
def subChannels(c1,c2,divisor=1, bias=0, clip=True):
#images have already been resized to match before sending here
pixels1 = c1.load()
pixels2 = c2.load()
for x in range(c1.size[0]):
for y in range(c1.size[1]):
r = pixels1[x,y]
g = pixels2[x,y]
value = wrapOrClip((r/divisor)-(g/divisor), clip)
value = wrapOrClip((value + bias), clip)
pixels2[x,y] = value
return c2
def wrapOrClip(value, clip = True):
if clip:
return max(min(value,255),0)
else:
return ((value)) - 256 if ((value)) > 255 else ((value)) + 256 if ((value)) < 0 else ((value))
奇怪的是,结果正是我的预期。所以,出于某种原因,我不明白我的方程式逐像素工作。但是如果用作ImageMath.eval中的表达式,它会产生截然不同的结果。
我更愿意使用eval,因为时间测试eval需要1秒才能完成工作。我的手动方法耗时9秒。
我知道推理中明显的缺陷是什么?
编辑: 包括我正在使用的代码的完整工作版本。
from PIL import ImageGrab, Image, ImageMath, ImageTk
from io import StringIO
import sys
#One stop shop for resizing, splitting, and combining the channels
#for all methods on the dialog
def evalImages(image1,image2, channel1 = -1, channel2 = -1, divisor=1, bias=0, clip=True, mode="add"):
modes = {"add": addChannels,
"sub": subChannels,
"mul": mulChannels,
"and": andChannels,
"or": orChannels,
"xor": xorChannels,
"average": averageChannels,
"dark": darkChannels,
"light": lightChannels,
"diff": diffChannels}
#resize image 2 to fit image 1
if image1.size != image2.size:
image2 = image2.resize(image1.size, Image.BICUBIC)
if channel1 == -1:
#if we are combining all channels
r1,g1,b1 = image1.split()
r2,g2,b2 = image2.split()
r3 = modes[mode](r1,r2,d = divisor, b = bias, clip=clip)
g3 = modes[mode](g1,g2,d = divisor, b = bias, clip=clip)
b3 = modes[mode](b1,b2,d = divisor, b = bias, clip=clip)
return Image.merge("RGB", (r3,g3,b3))
else:
#0 = Red, 1 = Green, Blue = 2
c1 = image1.split()[channel1]
c2 = image2.split()[channel2]
return modes[mode](c1, c2, d = divisor, b = bias, clip=clip)
#Function to either wrap the value aroun or clip it at 0/255
def wrapOrClip(value, clip = True):
if clip:
return max(min(value,255),0)
else:
return ((value)) - 256 if ((value)) > 255 else ((value)) + 256 if ((value)) < 0 else ((value))
#Function subtracts two single band images from each other
#divisor divides the values before they are subtracted
#bias is an offset applie to each pixel after subtraction and clipping or wrapping
def subChannels(c1,c2,d=1, b=0, clip=True):
#images have already been resized to match before sending here
pixels1 = c1.load()
pixels2 = c2.load()
for x in range(c1.size[0]):
for y in range(c1.size[1]):
r = pixels1[x,y]
g = pixels2[x,y]
value = wrapOrClip((r/d)-(g/d), clip)
value = wrapOrClip((value + b), clip)
pixels2[x,y] = value
return c2
#Multiply channels
def mulChannels(c1,c2,d=1, b=0, clip=True):
pass
#Bitwise AND channels
def andChannels(c1,c2, d=1, b=0, clip=True):
pass
#Bitwise OR channels
def orChannels(c1,c2, d=1, b=0, clip=True):
pass
#Bitwise XOR channels
def xorChannels(c1,c2, d=1, b=0, clip=True):
pass
#Average the two pixels in each channel together
def averageChannels(c1,c2, d=1, b=0, clip=True):
pass
# Choose the darkest pixels from both images (min(c1,c2))
def darkChannels(c1,c2, d=1, b=0, clip=True):
pass
# Choose the lightest pixels from both channels (max(c1, c2))
def lightChannels(c1,c2, d=1, b=0, clip=True):
pass
# Absolute value after Subtract
def diffChannels(c1,c2, d=1, b=0, clip=True):
pass
#add both channels together to get a ligher image
def addChannels(c1, c2, d=1, b=0, clip=True):
#bias is added to end result
#divisor is applied to each channel individually
pass
if __name__ == '__main__':
#I normally have code here to put two images on the clipboad
#then grab the down with ImageGrab.grapclipboard
#But that's specific to the program that runs this code.
#so instead here are two simple open functions
if len(sys.argv) >= 3:
print "args are good"
image1 = Image.open(sys.argv[1])
image2 = Image.open(sys.argv[2])
print "files opened"
#In this example, I'm combining the Red an Green channels of the two images together
image3 = evalImages(image1, image2,channel1 = 0, channel2=1, divisor = 1, bias=0, mode="sub")
print "channels merged"
image3.save("3.jpg")
print "file saved"