0
$\begingroup$

Are there any other techniques I could use to improve the performance of the below timed operation that converts a "Byte" type Grayscale image to "Real32" using the given lookup table.

I've tried using ImageApply with a compiled function to perform the lookup, but that is actually a bit slower.

testImage = Image[RandomImage[1, {1600, 1600*4}, ColorSpace -> "Grayscale"], "Byte"];

decompandImage[img_?ImageQ] :=
  With[{lut = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 
   16, 17, 18, 19, 20, 21, 22, 23, 25, 27, 29, 31, 33, 35, 37, 39,
    41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 67, 71, 75, 
   79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123, 127, 131,
    135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 
   183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 
   231, 235, 239, 243, 247, 255, 263, 271, 279, 287, 295, 303, 
   311, 319, 327, 335, 343, 351, 359, 367, 375, 383, 391, 399, 
   407, 415, 423, 431, 439, 447, 455, 463, 471, 479, 487, 495, 
   503, 511, 519, 527, 535, 543, 551, 559, 567, 575, 583, 591, 
   599, 607, 615, 623, 631, 639, 647, 655, 663, 671, 679, 687, 
   695, 703, 711, 719, 727, 735, 743, 751, 759, 767, 775, 783, 
   791, 799, 807, 815, 823, 831, 839, 847, 855, 863, 871, 879, 
   887, 895, 903, 911, 919, 927, 935, 943, 951, 959, 967, 975, 
   983, 991, 999, 1007, 1023, 1039, 1055, 1071, 1087, 1103, 1119, 
   1135, 1151, 1167, 1183, 1199, 1215, 1231, 1247, 1263, 1279, 
   1295, 1311, 1327, 1343, 1359, 1375, 1391, 1407, 1439, 1471, 
   1503, 1535, 1567, 1599, 1631, 1663, 1695, 1727, 1759, 1791, 
   1823, 1855, 1887, 1919, 1951, 1983, 2015, 2047, 2079, 2111, 
   2143, 2175, 2207, 2239, 2271, 2303, 2335, 2367, 2399, 2431, 
   2463, 2495, 2527, 2559, 2591, 2623, 2655, 2687, 2719, 2751, 
   2783, 2815, 2847, 2879}/2879. (* // N // Developer`ToPackedArray *)
  },
  Image[Map[lut[[# + 1]] &, ImageData[img, "Byte"], {2}], "Real32"]
  ];

AbsoluteTiming[test4 = decompandImage[testImage]] [[1]]
$\endgroup$
4
  • 1
    $\begingroup$ Remove {2} will make it bit faster i think, with same result. Also you should move + 1 so you have ImageData[img, "Byte"] + 1 $\endgroup$
    – Coolwater
    Jul 17, 2017 at 19:40
  • $\begingroup$ Thanks @Coolwater. Removing the '{2}' gave a significant improvement. Moving the +1 was produced either no change or a slight slow-down. $\endgroup$ Jul 17, 2017 at 22:44
  • $\begingroup$ Just changing the code after LUT definition to Image[Partition[lut[[Join @@ ImageData[img, "Byte"] + 1]], First@ImageDimensions@img], "Real32"]] should speed things nicely... $\endgroup$
    – ciao
    Jul 18, 2017 at 2:25
  • $\begingroup$ Thank for your input @ciao. Your suggestion runs .28s, slightly faster than my initial implementation, but significantly slower than Coolwater and Henrick's. $\endgroup$ Jul 21, 2017 at 4:17

1 Answer 1

1
$\begingroup$

Buildung on Coolwater's comment, one can obtain a slight improvement by compiling this into a Listable and parallelized CompiledFunction:

clookup = Compile[{{lut, _Real, 1}, {x, _Integer, 1}},
   lut[[x + 1]],
   CompilationTarget -> "C",
   RuntimeAttributes -> {Listable},
   Parallelization -> True
   ];

lut = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 
    18, 19, 20, 21, 22, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 
    45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 67, 71, 75, 79, 83, 87, 
    91, 95, 99, 103, 107, 111, 115, 119, 123, 127, 131, 135, 139, 143,
     147, 151, 155, 159, 163, 167, 171, 175, 179, 183, 187, 191, 195, 
    199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 
    255, 263, 271, 279, 287, 295, 303, 311, 319, 327, 335, 343, 351, 
    359, 367, 375, 383, 391, 399, 407, 415, 423, 431, 439, 447, 455, 
    463, 471, 479, 487, 495, 503, 511, 519, 527, 535, 543, 551, 559, 
    567, 575, 583, 591, 599, 607, 615, 623, 631, 639, 647, 655, 663, 
    671, 679, 687, 695, 703, 711, 719, 727, 735, 743, 751, 759, 767, 
    775, 783, 791, 799, 807, 815, 823, 831, 839, 847, 855, 863, 871, 
    879, 887, 895, 903, 911, 919, 927, 935, 943, 951, 959, 967, 975, 
    983, 991, 999, 1007, 1023, 1039, 1055, 1071, 1087, 1103, 1119, 
    1135, 1151, 1167, 1183, 1199, 1215, 1231, 1247, 1263, 1279, 1295, 
    1311, 1327, 1343, 1359, 1375, 1391, 1407, 1439, 1471, 1503, 1535, 
    1567, 1599, 1631, 1663, 1695, 1727, 1759, 1791, 1823, 1855, 1887, 
    1919, 1951, 1983, 2015, 2047, 2079, 2111, 2143, 2175, 2207, 2239, 
    2271, 2303, 2335, 2367, 2399, 2431, 2463, 2495, 2527, 2559, 2591, 
    2623, 2655, 2687, 2719, 2751, 2783, 2815, 2847, 2879}/2879.;

cdecompandImage[img_?ImageQ] := Image[clookup[lut, ImageData[img, "Byte"]], "Real32"];
$\endgroup$
2
  • $\begingroup$ Thanks @Henrick, that compilation style also produced a significant improvement. My function -> .33s , Removing {2} -> .16s , compiled/listable -> .10s $\endgroup$ Jul 17, 2017 at 23:24
  • $\begingroup$ Image[ImageApply[lut[[#*255 + 1]] &, Image[testImage, "Bit16"]], "Real32"] - though serial - is very fast as well, however does not give exactly the same result as above, as 16bit requantisation is being done, but no grayscale level is being lost for the given lut. However Image[ImageApply[lut[[#*255 + 1]] &, testImage], "Real32"] would do 8bit requantisation, so many grayscale levels would get lost for this lut. Is there any particular reason why Real32 requested? Bit16 is sufficient in many practical cases, so the back casting could be skipped. $\endgroup$
    – UDB
    Jul 28, 2017 at 17:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.