Repository: opencv
Issue: Convert Pixel Function (including a prototype)
Labels: feature

### Describe the feature and motivation

Every now and then i want to convert single pixel values. that includes channels, depth, colorspace, scaling and container type (e.g.: VecN and Scalar). at the moment that is not possible with opencv using a single function. i would like to have such a function.

Following is a probably not so perfect and still incomplete prototype of such a function. What do you think is missing besides grayscale support?

### Examples:
```C++
//HLS (0-255) to RGB
cv::Vec3b rgb = convert_pix<cv::COLOR_HLS2RGB_FULL>(cv::Vec3b(hue, 128, 255));

//channel manipulation and scaling
cv::Vec4b rgba = convert_pix<cv::COLOR_BGR2RGBA>(cv::Vec3b(1.0, 0, 0.5), 255.0);

//binarize and convert from Scalar to Vec4b 
cv::Vec4b binarized = convert_pix<-1, Scalar, cv::Vec4b, true>(scalar, 1.0/255.0);
```

### Code:
```c++
template<typename T>
constexpr int matrix_depth() {
	if constexpr(std::is_same_v<T, uchar>) {
		return CV_8U;
	} else if constexpr(std::is_same_v<T, short>){
		return CV_16S;
	} else if constexpr(std::is_same_v<T, ushort>){
		return CV_16U;
	} else if constexpr(std::is_same_v<T, int>){
		return CV_32S;
	} else if constexpr(std::is_same_v<T, float>){
		return CV_32F;
	} else if constexpr(std::is_same_v<T, double>){
		return CV_64F;
	} else if constexpr(true) {
		static_assert(false, "Type not supported for operation.");
		return 0;
	}
}

template<bool Tround> double doRound(double t) {
	if constexpr(Tround) {
		return std::round(t);
	} else {
		return t;
	}
}

/*!
 * Convenience function to color convert from all Vec_ and Scalar_ variants
 */
template<int Tcode = -1, typename Tsrc, typename Tdst = Vec<typename Tsrc::value_type, Tsrc::channels>, bool Tround = std::is_floating_point_v<typename Tsrc::value_type> && std::is_integral_v<typename Tdst::value_type>>
Tdst convert_pix(const Tsrc &src, double alpha = 1.0, double beta = 0.0) {
	constexpr int srcCn = Tsrc::channels;
	constexpr int dstCn = Tdst::channels;

	using srcv_t = typename Tsrc::value_type;
	using dstv_t = typename Tdst::value_type;
	using src_internal_t = Vec<srcv_t, srcCn>;
	using intermediate_t = Vec<srcv_t, dstCn>;
	using dst_internal_t = Vec<dstv_t, dstCn>;
	static_assert((srcCn == 3 || srcCn == 4) && (dstCn == 3 || dstCn == 4), "Only 3 or 4 (src/dst) channels supported");
	constexpr int srcType = CV_MAKETYPE(
			matrix_depth<typename src_internal_t::value_type>(),
			src_internal_t::channels);
	constexpr int intermediateType = CV_MAKETYPE(
			matrix_depth<typename src_internal_t::value_type>(), dstCn);
	constexpr int dstType = CV_MAKETYPE(
			matrix_depth<typename dst_internal_t::value_type>(), dstCn);

	std::array<src_internal_t, 1> srcArr;
	if constexpr (srcCn == 3) {
		srcArr[0] = src_internal_t(src[0], src[1], src[2]);
	} else {
		srcArr[0] = src_internal_t(src[0], src[1], src[2], src[3]);
	}

	cv::Mat intermediateMat(cv::Size(1, 1), intermediateType);

	if constexpr (dstCn == srcCn) {
		intermediateMat = srcArr[0];
	} else if constexpr (srcCn == 3) {
		intermediateMat = intermediate_t(srcArr[0][0], srcArr[0][1],
				srcArr[0][2]);
	} else if constexpr (srcCn == 4) {
		intermediateMat = intermediate_t(srcArr[0][0], srcArr[0][1],
				srcArr[0][2], srcArr[0][3]);
	}

	if constexpr (Tcode >= 0) {
		cvtColor(srcArr, intermediateMat, Tcode);
	}

	std::array<dst_internal_t, 1> dstArr;
	if constexpr (!std::is_same<srcv_t, dstv_t>::value) {
		//will just copy if types match
		if constexpr (dstCn == srcCn) {
			intermediateMat.convertTo(dstArr, dstType);
		} else if constexpr (dstCn == 3) {
			cvtColor(intermediateMat, intermediateMat, cv::COLOR_BGRA2BGR);
			intermediateMat.convertTo(dstArr, dstType);
		} else if constexpr (dstCn == 4) {
			cvtColor(intermediateMat, intermediateMat, cv::COLOR_BGR2BGRA);
			intermediateMat.convertTo(dstArr, dstType);
		}
	} else {
		if constexpr (dstCn == srcCn) {
			dstArr[0] = intermediateMat.at<src_internal_t>(0.0);
		} else if constexpr (dstCn == 3) {
			auto im = intermediateMat.at<src_internal_t>(0.0);
			dstArr[0] = dst_internal_t(im[0], im[1], im[2]);
		} else if constexpr (dstCn == 4) {
			auto im = intermediateMat.at<src_internal_t>(0.0);
			if (intermediateMat.depth() == CV_32F
					|| intermediateMat.depth() == CV_64F) {
				dstArr[0] = dst_internal_t(im[0], im[1], im[2], 255.0);
			} else {
				dstv_t a = std::numeric_limits<dstv_t>::max();
				dstArr[0] = dst_internal_t(im[0], im[1], im[2], a);
			}
		}
	}

	Tdst dst;

	if constexpr (dstCn == 3) {
		dst = Tdst(dstArr[0][0], dstArr[0][1], dstArr[0][2]);
	} else if constexpr (dstCn == 4) {
		dst = Tdst(dstArr[0][0], dstArr[0][1], dstArr[0][2], dstArr[0][3]);
	}

	if (alpha != 1.0) {
		if constexpr (dstCn == 3) {
			dst[0] = doRound<Tround>(dst[0] * alpha);
			dst[1] = doRound<Tround>(dst[1] * alpha);
			dst[2] = doRound<Tround>(dst[2] * alpha);
		} else if constexpr (dstCn == 4) {
			dst[0] = doRound<Tround>(dst[0] * alpha);
			dst[1] = doRound<Tround>(dst[1] * alpha);
			dst[2] = doRound<Tround>(dst[2] * alpha);
			dst[3] = doRound<Tround>(dst[3] * alpha);
		}
	}

	if (beta != 0.0) {
		if constexpr (dstCn == 3) {
			dst[0] = doRound<Tround>(dst[0] + beta);
			dst[1] = doRound<Tround>(dst[1] + beta);
			dst[2] = doRound<Tround>(dst[2] + beta);
		} else if constexpr (dstCn == 4) {
			dst[0] = doRound<Tround>(dst[0] + beta);
			dst[1] = doRound<Tround>(dst[1] + beta);
			dst[2] = doRound<Tround>(dst[2] + beta);
			dst[3] = doRound<Tround>(dst[3] + beta);
		}
	}
	return dst;
}

```


### Additional context

_No response_