Мне кажется, надо порядок условных операторов поменять (h * 1.2 > h > h *0.8). В текущем варианте h * 1.2 не будет (поскольку первое условие выполнится).
Можно не сортировать массив cnts а просто найти максимальный элемент, зависит от характера масок, как много там контуров обычно будет находится, может быть совсем неощутимо.
Из очевидного, что касается именно opencv - нету смысла всю картинку переводить в lab, если потом вырезается roi. Сначала вырежьте roi, а потом переводите в lab. Аналогично можно сделать с mask_hard (хотя если использовать адаптивные методы бинаризации, то результат может немного отличаться по краям roi)
Не так хорошо знаком с opencv для python, но в версии для c++ по умолчанию используется распараллеливание, где возможно. Имеет смысл собирать библиотеку с openmp / tbb и смотреть, что какой дает прирос.
Если обрабатывается пачка картинок, то явно запускать в несколько потоков, аггрегируя результаты, если это необходимо.