There are several options, depending on what you mean. All lose information in one way or another.
- dst.converTo(src, CV_8U) will keep just the first 256 values, and everything higher becomes 255. Good for when someone just stuck an 8-bit image into 16-bits of memory.
- dst.convertTo(src, CV_8U, 1/256.0) divides everything by 256. Good for when someone just scaled all the values into 16-bits, or if someone has done something to the image, like below.
- normalize(src, dst, NORM_MINMAX, 0, 255) Takes the lowest value, sets it to 0, the highest to 255, and scales everything else linearly in between. Good if the image is fairly well distributed between min and max.
- equalizeHist(src, dst) Takes the image, and spreads it out across the range of values. Then do 2 to make it 8 bits. Only sort of counts as not losing information, as it distorts the image.
- CLAHE takes the image, spreads it across the range of values by regions. Good for when one part is brighter than the rest. Only sort of counts as not losing information, as it distorts the image, even more than equalizeHist.
- That's pretty much all OpenCV has. You are now in the world of Local Area Contrast Enhancement, which has hundreds if not thousands of methods. Get your google on and find one that works for you.