Reputation: 145
I did a search on the previous asked question, but without finding what i need to optimize my code.
For info, I am running on Python 2.7 but could change to 3 if needed
I am converting every pixel of an image and because of some condition i have to do it pixel by pixel. So i have nested for loop with an if-elif-else statement inside, and it takes an awful long time to run. For an image of 1536 x 2640, the whole code takes ~20 seconds and 90% of time is inside this double for loop
I believe there should be a better way to write the below code
for pixel in range(width):
for row in range(height):
ADC = img_original[row, pixel]
if ADC < 84:
gain = gain1
offset = offset1
elif ADC > 153:
gain = gain3
offset = offset3
else:
gain = gain2
offset = offset2
Conv_ADC = int(min(max(ADC * gain + offset, 0),255))
img_conv[row, pixel] = Conv_ADC
Thanks for the help
edit for additional detail:
@Jean-FrançoisFabre is right and I am applying three different gain/offset depending on which section I am between 0 to 255. But the section are not always evenly space and can be modified. Maybe to give some additional context, i am simply applying a custom S-curve to an image to shift the pixel value up/down. And each column in the image have their own S-curve
my gain1,2,3/offset1,2,3 values are floating point. the gain will always be positive and the offset can be negative or positive.I also have an individual value for each pixel in the width direction, but they are common in the row direction.
Example, all pixel from column 1 with can use gain/offset 1,2,3 from the 1st row in table below. All pixel from column 2 in the image will use gain/offset form the row 2 in the table below
Pixel Gain1 Offset1 Gain2 Offset2 Gain3 Offset3
1 0.417722 24.911392 0.623188 7.652176 1.175676 -76.878357
2 0.43038 25.848103 0.623188 9.652176 1.148649 -70.743225
3 0.443038 23.784809 0.637681 7.434776 1.175676 -74.878357
4 0.443038 22.784809 0.652174 5.217384 1.175676 -74.878357
5 0.455696 23.721519 0.637681 8.434776 1.202703 -78.013519
6 0.455696 21.721519 0.637681 6.434776 1.243243 -86.216217
7 0.455696 22.721519 0.623188 8.652176 1.216216 -82.081085
8 0.443038 22.784809 0.623188 7.652176 1.22973 -85.148651
... until pixel 2640 in width direction
I will look at @Jean-FrançoisFabre solution, but in the meantime i was also looking at using some numpy approaches.
Once i get something that compute faster, i'll post my finding here
Upvotes: 3
Views: 682
Reputation: 145
This is the final implementation i am using to remove the 2 for loops. Going down to around 1~2 seconds per images
I am creating 3 array where i will replace the content by 0 when it is not in the range i want. Then do the gain multiplication and offset on each before adding the whole
height = img_original.shape[0]
width = img_original.shape[1]
print 'height = ', height, 'width = ', width
# create temp 3 2D-arrays
img1 = np.array(img_original,dtype=np.int)
img2 = np.array(img_original,dtype=np.int)
img3 = np.array(img_original,dtype=np.int)
#create the 2D array for gain/offset based on 1D array
# csv array acquire from .csv file, INDEX_xx for column to read
array_gain1 = np.tile(csv[1:, INDEX_G1],(height,1))
array_offset1 = np.tile(csv[1:, INDEX_O1],(height,1))
array_gain2 = np.tile(csv[1:, INDEX_G2],(height,1))
array_offset2 = np.tile(csv[1:, INDEX_O2],(height,1))
array_gain3 = np.tile(csv[1:, INDEX_G3],(height,1))
array_offset3 = np.tile(csv[1:, INDEX_O3],(height,1))
# replace the content by 0 when not in the desired zone
np.place(img1,img_original >= G2_TARGET, 0)
np.place(img2,img_original < G2_TARGET,0)
np.place(img2,img_original > G1_TARGET,0)
np.place(img3,img_original <= G1_TARGET, 0)
np.place(array_offset1,img_original >= G2_TARGET, 0)
np.place(array_offset2,(img_original < G2_TARGET), 0)
np.place(array_offset2,(img_original > G1_TARGET), 0)
np.place(array_offset3,img_original <= G1_TARGET, 0)
# apply the gain/offset for each zone
img1 = np.array(img1 * array_gain1 + array_offset1, dtype=np.uint8)
img2 = np.array(img2 * array_gain2 + array_offset2, dtype=np.uint8)
img3 = np.array(img3 * array_gain3 + array_offset3, dtype=np.uint8)
# recrete the whole image
img_conv = np.clip(img1 + img2 + img3, 0, 255)
Upvotes: 0
Reputation:
Have a try with a lookup-table: you precompute all transformed ADC values in the range [0,255]
, and the loop body will simplify
for pixel in range(width):
for row in range(height):
img_conv[row, pixel]= LUT[img_original[row, pixel]]
Upvotes: 1
Reputation: 140196
Since your values are between 0 and 255 and your bounds are evenly spaced, you could use the trick below:
you seem to want to apply 3 different gains depending if you are on the first third, the second third or the third third of the 0-255 range.
Why not computing the index by dividing by 85 (255/3) ?
simple proof of concept:
gainsoffsets = [(10,1),(20,2),(30,3),(30,3)] # [(gain1,offset1),(gain2,offset2),(gain3,offset3)] + extra corner case for value 255
for value in 84,140,250:
index = value // 85
gain,offset = gainsoffsets[index]
print(gain,offset)
result:
10 1
20 2
30 3
in this loop there's only one division and no if
. Should be much faster (beside the numpy
approaches)
You could use finer level with more accurate lookup tables (also avoid the division by generating 256 tuples):
gainsoffsets = [(10,1)]*85+[(20,2)]*85+[(30,3)*86] # add more intervals for more thresholds
Upvotes: 2