CUDA тут не нужна, просто не следует перебирать массив numpy питоновскими циклами.
Если есть возможность - выражай желаемое через операции над массивом в целом. Тогда цикл организует numpy, и он это делает через нативный код, ЕМНИП - работает в разы быстрее.
Подход ниже сожрёт больше памяти, но зато короткий, выразительный и использует только средства numpy.
image = cv2.imread("image.png")
image = image.astype(numpy.uint32) # смена типа нужна, иначе при сдвиге потеряются разряды
image_clone = (image[..., 0] << 16) | (image[..., 1] << 8) | image[..., 2]
Если хочется заморочиться по памяти:
image = cv2.imread("image.png")
image_clone = numpy.zeros(image.shape[:2], numpy.uint32)
# краткие формы операций не создают новый массив, а изменяют существующий по месту
image_clone |= image[..., 0]
image_clone <<= 8
image_clone |= image[..., 1]
image_clone <<= 8
image_clone |= image[..., 2]
P.S.: как там, у imread() починили уже неумение работать с юникодными путями?