Image similarity measure
up vote
2
down vote
favorite
I'm attempting to speed up my genetic algorithm that modifies images. After searching, I've found that my fitness function is the bottleneck, taking sometimes up to 11 seconds to complete.
The fitness function accepts two images and returns a float representing the total difference between the images' corresponding pixels, measured as distance in Cartesian RGB space.
To test, I used some large images of the same size (1254 x 834). What can I do to speed this process up?
def fitness(original, new):
fitness = 0
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = original.getpixel((x, y))
r2, g2, b2 = new.getpixel((x, y))
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
python performance image edit-distance genetic-algorithm
add a comment |
up vote
2
down vote
favorite
I'm attempting to speed up my genetic algorithm that modifies images. After searching, I've found that my fitness function is the bottleneck, taking sometimes up to 11 seconds to complete.
The fitness function accepts two images and returns a float representing the total difference between the images' corresponding pixels, measured as distance in Cartesian RGB space.
To test, I used some large images of the same size (1254 x 834). What can I do to speed this process up?
def fitness(original, new):
fitness = 0
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = original.getpixel((x, y))
r2, g2, b2 = new.getpixel((x, y))
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
python performance image edit-distance genetic-algorithm
1
getpixel((x, y)) is very likely to be the trouble, as it is very slow operation for every language(and API) I know. I dont know much of python but finding alternative should be easy task for you.
– wondra
Jan 29 '15 at 22:12
The three calls toabs()are pretty pointless, given that the only thing we do with the results is to square them.
– Toby Speight
yesterday
I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have.
– Toby Speight
yesterday
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
I'm attempting to speed up my genetic algorithm that modifies images. After searching, I've found that my fitness function is the bottleneck, taking sometimes up to 11 seconds to complete.
The fitness function accepts two images and returns a float representing the total difference between the images' corresponding pixels, measured as distance in Cartesian RGB space.
To test, I used some large images of the same size (1254 x 834). What can I do to speed this process up?
def fitness(original, new):
fitness = 0
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = original.getpixel((x, y))
r2, g2, b2 = new.getpixel((x, y))
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
python performance image edit-distance genetic-algorithm
I'm attempting to speed up my genetic algorithm that modifies images. After searching, I've found that my fitness function is the bottleneck, taking sometimes up to 11 seconds to complete.
The fitness function accepts two images and returns a float representing the total difference between the images' corresponding pixels, measured as distance in Cartesian RGB space.
To test, I used some large images of the same size (1254 x 834). What can I do to speed this process up?
def fitness(original, new):
fitness = 0
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = original.getpixel((x, y))
r2, g2, b2 = new.getpixel((x, y))
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
python performance image edit-distance genetic-algorithm
python performance image edit-distance genetic-algorithm
edited yesterday
200_success
127k15149412
127k15149412
asked Jan 29 '15 at 22:03
TimCPogue
344
344
1
getpixel((x, y)) is very likely to be the trouble, as it is very slow operation for every language(and API) I know. I dont know much of python but finding alternative should be easy task for you.
– wondra
Jan 29 '15 at 22:12
The three calls toabs()are pretty pointless, given that the only thing we do with the results is to square them.
– Toby Speight
yesterday
I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have.
– Toby Speight
yesterday
add a comment |
1
getpixel((x, y)) is very likely to be the trouble, as it is very slow operation for every language(and API) I know. I dont know much of python but finding alternative should be easy task for you.
– wondra
Jan 29 '15 at 22:12
The three calls toabs()are pretty pointless, given that the only thing we do with the results is to square them.
– Toby Speight
yesterday
I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have.
– Toby Speight
yesterday
1
1
getpixel((x, y)) is very likely to be the trouble, as it is very slow operation for every language(and API) I know. I dont know much of python but finding alternative should be easy task for you.
– wondra
Jan 29 '15 at 22:12
getpixel((x, y)) is very likely to be the trouble, as it is very slow operation for every language(and API) I know. I dont know much of python but finding alternative should be easy task for you.
– wondra
Jan 29 '15 at 22:12
The three calls to
abs() are pretty pointless, given that the only thing we do with the results is to square them.– Toby Speight
yesterday
The three calls to
abs() are pretty pointless, given that the only thing we do with the results is to square them.– Toby Speight
yesterday
I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have.
– Toby Speight
yesterday
I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have.
– Toby Speight
yesterday
add a comment |
2 Answers
2
active
oldest
votes
up vote
2
down vote
accepted
Thanks to @wondra for the help. I rewrote the function without the getpixel and just loaded the pixels using the image.load() method. This caused the execution time to reduce down to 3.09 seconds.
Rewritten function:
def fitness_new(new):
fitness = 0
new_data = new.load()
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = optimal_data[x, y]
r2, g2, b2 = new_data[x, y]
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
optimal_data is preloaded as a global variable instead of being passed as an argument, because it is accessed elsewhere. I know this is probably not the most ideal method.
add a comment |
up vote
1
down vote
To speed this up further, you should use the numpy interface of PIL (if you are not yet using PIL, you should, for this reason):
from PIL import Image
import numpy as np
# `int` important because otherwise it might wrap around when subtracting
optimal_data = np.asarray(Image.open("optimal.png"), dtype=int)
new = np.random.randint(0, 256, optimal_data.shape)
def fitness(optimal_data, new):
return np.sqrt(((optimal_data - new)**2).sum(axis=-1)).sum()
This takes only 258 ms ± 2.21 ms for a 2424 x 2424 pixel image on my machine, while the function by @TimCPogue takes 9.93 s ± 465 ms with the same images.
Note that the array has the shape (width, height, channels), where channels is usually 4 (red, green, blue, alpha), not 3 like your code assumes. If you want to disregard differences in alpha, either set the alpha channel of the new image to the one of the optimal data (new[:,:,-1] = optimal_data[:,:,-1]), or slice in the fitness (optimal_data[...,:-1] - new[...,:-1]).
For some more readability and the possibility to use a different norm in the future (albeit at the cost of about 30% speed), you could make the norm to use a parameter and use np.linalg.norm, as suggested in the comments by @GarethReese:
def fitness(optimal_data, new, norm=np.linalg.norm):
return norm(optimal_data - new, axis=-1).sum()
Consider usingnumpy.linalg.norminstead of writing your own 2-norm.
– Gareth Rees
yesterday
@GarethRees: While it does make it more readable, it is about 30% slower. Even after makingnorm = np.linalg.norma local variable.
– Graipher
yesterday
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
Thanks to @wondra for the help. I rewrote the function without the getpixel and just loaded the pixels using the image.load() method. This caused the execution time to reduce down to 3.09 seconds.
Rewritten function:
def fitness_new(new):
fitness = 0
new_data = new.load()
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = optimal_data[x, y]
r2, g2, b2 = new_data[x, y]
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
optimal_data is preloaded as a global variable instead of being passed as an argument, because it is accessed elsewhere. I know this is probably not the most ideal method.
add a comment |
up vote
2
down vote
accepted
Thanks to @wondra for the help. I rewrote the function without the getpixel and just loaded the pixels using the image.load() method. This caused the execution time to reduce down to 3.09 seconds.
Rewritten function:
def fitness_new(new):
fitness = 0
new_data = new.load()
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = optimal_data[x, y]
r2, g2, b2 = new_data[x, y]
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
optimal_data is preloaded as a global variable instead of being passed as an argument, because it is accessed elsewhere. I know this is probably not the most ideal method.
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
Thanks to @wondra for the help. I rewrote the function without the getpixel and just loaded the pixels using the image.load() method. This caused the execution time to reduce down to 3.09 seconds.
Rewritten function:
def fitness_new(new):
fitness = 0
new_data = new.load()
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = optimal_data[x, y]
r2, g2, b2 = new_data[x, y]
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
optimal_data is preloaded as a global variable instead of being passed as an argument, because it is accessed elsewhere. I know this is probably not the most ideal method.
Thanks to @wondra for the help. I rewrote the function without the getpixel and just loaded the pixels using the image.load() method. This caused the execution time to reduce down to 3.09 seconds.
Rewritten function:
def fitness_new(new):
fitness = 0
new_data = new.load()
for x in range(0, width):
for y in range(0, height):
r1, g1, b1 = optimal_data[x, y]
r2, g2, b2 = new_data[x, y]
deltaRed = abs(r1 - r2)
deltaGreen = abs(g1 - g2)
deltaBlue = abs(b1 - b2)
pixelFitness = pixelFitness = math.sqrt(deltaRed ** 2 + deltaGreen ** 2 + deltaBlue ** 2)
fitness += pixelFitness
return fitness
optimal_data is preloaded as a global variable instead of being passed as an argument, because it is accessed elsewhere. I know this is probably not the most ideal method.
answered Jan 29 '15 at 22:30
TimCPogue
344
344
add a comment |
add a comment |
up vote
1
down vote
To speed this up further, you should use the numpy interface of PIL (if you are not yet using PIL, you should, for this reason):
from PIL import Image
import numpy as np
# `int` important because otherwise it might wrap around when subtracting
optimal_data = np.asarray(Image.open("optimal.png"), dtype=int)
new = np.random.randint(0, 256, optimal_data.shape)
def fitness(optimal_data, new):
return np.sqrt(((optimal_data - new)**2).sum(axis=-1)).sum()
This takes only 258 ms ± 2.21 ms for a 2424 x 2424 pixel image on my machine, while the function by @TimCPogue takes 9.93 s ± 465 ms with the same images.
Note that the array has the shape (width, height, channels), where channels is usually 4 (red, green, blue, alpha), not 3 like your code assumes. If you want to disregard differences in alpha, either set the alpha channel of the new image to the one of the optimal data (new[:,:,-1] = optimal_data[:,:,-1]), or slice in the fitness (optimal_data[...,:-1] - new[...,:-1]).
For some more readability and the possibility to use a different norm in the future (albeit at the cost of about 30% speed), you could make the norm to use a parameter and use np.linalg.norm, as suggested in the comments by @GarethReese:
def fitness(optimal_data, new, norm=np.linalg.norm):
return norm(optimal_data - new, axis=-1).sum()
Consider usingnumpy.linalg.norminstead of writing your own 2-norm.
– Gareth Rees
yesterday
@GarethRees: While it does make it more readable, it is about 30% slower. Even after makingnorm = np.linalg.norma local variable.
– Graipher
yesterday
add a comment |
up vote
1
down vote
To speed this up further, you should use the numpy interface of PIL (if you are not yet using PIL, you should, for this reason):
from PIL import Image
import numpy as np
# `int` important because otherwise it might wrap around when subtracting
optimal_data = np.asarray(Image.open("optimal.png"), dtype=int)
new = np.random.randint(0, 256, optimal_data.shape)
def fitness(optimal_data, new):
return np.sqrt(((optimal_data - new)**2).sum(axis=-1)).sum()
This takes only 258 ms ± 2.21 ms for a 2424 x 2424 pixel image on my machine, while the function by @TimCPogue takes 9.93 s ± 465 ms with the same images.
Note that the array has the shape (width, height, channels), where channels is usually 4 (red, green, blue, alpha), not 3 like your code assumes. If you want to disregard differences in alpha, either set the alpha channel of the new image to the one of the optimal data (new[:,:,-1] = optimal_data[:,:,-1]), or slice in the fitness (optimal_data[...,:-1] - new[...,:-1]).
For some more readability and the possibility to use a different norm in the future (albeit at the cost of about 30% speed), you could make the norm to use a parameter and use np.linalg.norm, as suggested in the comments by @GarethReese:
def fitness(optimal_data, new, norm=np.linalg.norm):
return norm(optimal_data - new, axis=-1).sum()
Consider usingnumpy.linalg.norminstead of writing your own 2-norm.
– Gareth Rees
yesterday
@GarethRees: While it does make it more readable, it is about 30% slower. Even after makingnorm = np.linalg.norma local variable.
– Graipher
yesterday
add a comment |
up vote
1
down vote
up vote
1
down vote
To speed this up further, you should use the numpy interface of PIL (if you are not yet using PIL, you should, for this reason):
from PIL import Image
import numpy as np
# `int` important because otherwise it might wrap around when subtracting
optimal_data = np.asarray(Image.open("optimal.png"), dtype=int)
new = np.random.randint(0, 256, optimal_data.shape)
def fitness(optimal_data, new):
return np.sqrt(((optimal_data - new)**2).sum(axis=-1)).sum()
This takes only 258 ms ± 2.21 ms for a 2424 x 2424 pixel image on my machine, while the function by @TimCPogue takes 9.93 s ± 465 ms with the same images.
Note that the array has the shape (width, height, channels), where channels is usually 4 (red, green, blue, alpha), not 3 like your code assumes. If you want to disregard differences in alpha, either set the alpha channel of the new image to the one of the optimal data (new[:,:,-1] = optimal_data[:,:,-1]), or slice in the fitness (optimal_data[...,:-1] - new[...,:-1]).
For some more readability and the possibility to use a different norm in the future (albeit at the cost of about 30% speed), you could make the norm to use a parameter and use np.linalg.norm, as suggested in the comments by @GarethReese:
def fitness(optimal_data, new, norm=np.linalg.norm):
return norm(optimal_data - new, axis=-1).sum()
To speed this up further, you should use the numpy interface of PIL (if you are not yet using PIL, you should, for this reason):
from PIL import Image
import numpy as np
# `int` important because otherwise it might wrap around when subtracting
optimal_data = np.asarray(Image.open("optimal.png"), dtype=int)
new = np.random.randint(0, 256, optimal_data.shape)
def fitness(optimal_data, new):
return np.sqrt(((optimal_data - new)**2).sum(axis=-1)).sum()
This takes only 258 ms ± 2.21 ms for a 2424 x 2424 pixel image on my machine, while the function by @TimCPogue takes 9.93 s ± 465 ms with the same images.
Note that the array has the shape (width, height, channels), where channels is usually 4 (red, green, blue, alpha), not 3 like your code assumes. If you want to disregard differences in alpha, either set the alpha channel of the new image to the one of the optimal data (new[:,:,-1] = optimal_data[:,:,-1]), or slice in the fitness (optimal_data[...,:-1] - new[...,:-1]).
For some more readability and the possibility to use a different norm in the future (albeit at the cost of about 30% speed), you could make the norm to use a parameter and use np.linalg.norm, as suggested in the comments by @GarethReese:
def fitness(optimal_data, new, norm=np.linalg.norm):
return norm(optimal_data - new, axis=-1).sum()
edited yesterday
answered yesterday
Graipher
22.8k53384
22.8k53384
Consider usingnumpy.linalg.norminstead of writing your own 2-norm.
– Gareth Rees
yesterday
@GarethRees: While it does make it more readable, it is about 30% slower. Even after makingnorm = np.linalg.norma local variable.
– Graipher
yesterday
add a comment |
Consider usingnumpy.linalg.norminstead of writing your own 2-norm.
– Gareth Rees
yesterday
@GarethRees: While it does make it more readable, it is about 30% slower. Even after makingnorm = np.linalg.norma local variable.
– Graipher
yesterday
Consider using
numpy.linalg.norm instead of writing your own 2-norm.– Gareth Rees
yesterday
Consider using
numpy.linalg.norm instead of writing your own 2-norm.– Gareth Rees
yesterday
@GarethRees: While it does make it more readable, it is about 30% slower. Even after making
norm = np.linalg.norm a local variable.– Graipher
yesterday
@GarethRees: While it does make it more readable, it is about 30% slower. Even after making
norm = np.linalg.norm a local variable.– Graipher
yesterday
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f79023%2fimage-similarity-measure%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
getpixel((x, y)) is very likely to be the trouble, as it is very slow operation for every language(and API) I know. I dont know much of python but finding alternative should be easy task for you.
– wondra
Jan 29 '15 at 22:12
The three calls to
abs()are pretty pointless, given that the only thing we do with the results is to square them.– Toby Speight
yesterday
I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have.
– Toby Speight
yesterday