The meaning of HSV channel
The HSV channel refers to a color model in image processing, which consists of three channels: hue (Hue), saturation (Saturation) and brightness (Value). Hue represents the type of color, saturation represents the purity or vividness of the color, and value represents the brightness of the color. The HSV channel is often used for tasks such as color analysis, color filtering, and color adjustment in image processing. Compared with other color models, it is more intuitive and easier to adjust, so it is widely used in the fields of computer vision and image processing.
Halcon operator routine
read_image (Image, 'D:/lena.jpg') decompose3 (Image, ImageR, ImageG, ImageB) trans_from_rgb (ImageR, ImageG, ImageB, ImageH, ImageS, ImageV, 'hsv') trans_to_rgb (ImageH, ImageS, ImageV, ImageR1, ImageG1, ImageB1, 'hsv') compose3 (ImageR1, ImageG1, ImageB1, MultiChannelImage)
Here, the three-channel RGB channel is first separated into separate channels, then the RGB and HSV channels are converted to each other, and finally the three-channel image is merged into an RGB image.
The image effect of Halcon is:
Source code implementation
Convert RGB to HSV
It should be noted here that halcon explains the value range of the three HSV channels. The value range of the H channel is 0 to 2*pi, the value range of the S channel is 0 to 1, and the value range of the V channel is 0 to 1. The commonly used images are BYTE byte type, and the numerical range is 0 to 255. The formula is modified here so that the image data range obtained by Matlab is 0 to 255, which can be displayed directly. You can see it from the workspace of matlab. calculation process.
The following is the effect of using Matlab to implement trans_from_rgb
trans_from_rgb (ImageR, ImageG, ImageB, ImageH, ImageS, ImageV, 'hsv')
Matlab source code
%%%%%%%%%%%%%%%%%%%%%%%%%%%% % code--RGB channel to HSV channel %Time: 2023.9 %%%%%%%%%%%%%%%%%%%%%%%%%%%%% clc; image=imread('D:\lena.jpg'); [height,width,channels]=size(image); figure; imshow(image); title('rgb-image'); image_R=image(:,:,1); image_R=double(image_R); image_G=image(:,:,2); image_G=double(image_G); image_B=image(:,:,3); image_B=double(image_B); %%%Convert to HSV channel H_image = zeros(height,width); S_image = zeros(height,width); V_image = zeros(height,width); %%RGB to HSV for i=1:1:height for j=1:1:width %%%Calculate the maximum and minimum values of the three channels image_matrix = [image_R(i,j), image_G(i,j), image_B(i,j)]; maxValue = max(image_matrix); minValue = min(image_matrix); V_image(i,j) = maxValue; if(maxValue == minValue) S_image(i,j) = 0; H_image(i,j) = 0; else %%%Calculate saturation S_image(i,j) = (maxValue - minValue)*255/minValue; %%%Calculate H channel if(maxValue == image_R(i,j)) H_image(i,j) = 42.5.*(image_G(i,j) - image_B(i,j))./(maxValue - minValue); elseif(maxValue == image_G(i,j)) H_image(i,j) = 42.5 * (2 + (image_B(i,j) - image_R(i,j)) / (maxValue - minValue)); elseif(maxValue == image_B(i,j)) H_image(i,j) = 42.5 * (4 + (image_R(i,j) - image_G(i,j)) / (maxValue - minValue)); end end end end %%%RGB needs to be rounded H_image = uint8(H_image); S_image = uint8(S_image); V_image = uint8(V_image); figure; imshow(H_image); title('H_image'); figure; imshow(S_image); title('S_image'); figure; imshow(V_image); title('V_image');
The final effect is:
The final verified effect is consistent with the halcon effect;
At the same time, if the above code is implemented in C++, it is as follows. In order to ensure accuracy, the output result is of double type, but the range is also between 0 and 255. To display, it needs to be converted to unsigned char type:
C++ source code
//Convert RGB image to HSV image /* Input: rData: r channel image gData: g channel image bData: b channel image Output: hDoubleData: h channel image, h channel adopts double type, retaining accuracy sDoubleData: s channel image, s channel adopts double type, retaining accuracy vDoubleData: v channel image, v channel adopts double type, retaining accuracy */ void trans_from_rgb(unsigned char *rData, unsigned char *gData, unsigned char *bData, double *hDoubleData, double *sDoubleData, double *vDoubleData, int height, int width) {<!-- --> if ((height <= 0) || (width <= 0)) return; //Allocate memory space outside the function if (rData == NULL || gData == NULL || bData == NULL || hDoubleData == NULL || sDoubleData == NULL || vDoubleData == NULL) return; int i; unsigned char minValue,maxValue; for (i = 0; i < width * height; i + + ){<!-- --> //V channel data, the maximum value of the three channels maxValue = std::max(std::max(rData[i], gData[i]), bData[i]); minValue = std::min(std::min(rData[i], gData[i]), bData[i]); vDoubleData[i] = maxValue; if (maxValue == minValue){<!-- --> sDoubleData[i] = 0; hDoubleData[i] = 0; } else{<!-- --> //S channel sDoubleData[i] = (maxValue - minValue)*255.0 / maxValue; //H channel if (maxValue == rData[i]) hDoubleData[i] = 42.5 * (gData[i] - bData[i]) / (maxValue - minValue); else if (maxValue == gData[i]) hDoubleData[i] = 42.5 * (2 + (bData[i] - rData[i]) / (maxValue - minValue)); else if (maxValue == bData[i]) hDoubleData[i] = 42.5 * (4 + (rData[i] - gData[i]) / (maxValue - minValue)); } } }
Convert HSV to RGB
The formula description given by halcon is:
Matlab source code
Similarly, using Matlab to implement:
%%%%%%%%%%%%%%%%%%%%%%%% % code--HSV channel to RGB channel %Time: 2023.9 %%%%%%%%%%%%%%%%%%%%%%%%%% clc; image1=imread('D:\lena.jpg'); [height,width,channels]=size(image1); image_H=double(image1); image2=imread('E:\S_image.bmp'); image_S=double(image2); image3=imread('E:\V_image.bmp'); image_V=double(image3); %%%Convert to RGB channel R_image = zeros(height,width); G_image = zeros(height,width); B_image = zeros(height,width); %%%Convert HSV to RGB for i=1:1:height for j=1:1:width if(image_S(i,j) == 0) R_image(i,j) = image_V(i,j); G_image(i,j) = image_V(i,j); B_image(i,j) = image_V(i,j); else %%Hi = floor(image_H(i,j)*2*pi/255/deg2rad(60)); %%normalized to 0 to 2*pi Hi = floor(image_H(i,j)*0.025); Hf = image_H(i,j)*0.025 - Hi; %%%%According to the value of H, map C, X, and m to the three components of RGB respectively. if(Hi == 0) R_image(i,j) = image_V(i,j); G_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255*(1-Hf)); B_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255); elseif(Hi == 1) R_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255*Hf); G_image(i,j) = image_V(i,j); B_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255); elseif(Hi == 2) R_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255); G_image(i,j) = image_V(i,j); B_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255*(1-Hf)); elseif(Hi == 3) R_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255); G_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255*Hf); B_image(i,j) = image_V(i,j); elseif(Hi == 4) R_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255*(1-Hf)); G_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255); B_image(i,j) = image_V(i,j); elseif(Hi == 5) R_image(i,j) = image_V(i,j); G_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255); B_image(i,j) = image_V(i,j) * (1 - image_S(i,j)/255*Hf); end end end end %%%RGB needs to be rounded R_image = uint8(R_image); G_image = uint8(G_image); B_image = uint8(B_image); figure; imshow(R_image); title('R_image'); figure; imshow(G_image); title('G_image'); figure; imshow(B_image); title('B_image');
The final effect is:
It can be seen that the effect is consistent with Halcon;
C++ source code
Similarly, implemented in C++:
//Convert HSV image to RGB image /* Input: hDoubleData: h channel image, h channel adopts double type, retaining accuracy sDoubleData: s channel image, s channel adopts double type, retaining accuracy vDoubleData: v channel image, v channel adopts double type, retaining accuracy Output: rDoubleData: r channel image, r channel adopts double type, retaining accuracy gDoubleData: g channel image, g channel adopts double type, retaining accuracy bDoubleData: b channel image, b channel adopts double type, retaining accuracy */ void trans_to_rgb(double *hDoubleData, double *sDoubleData, double *vDoubleData, double *rDoubleData, double *gDoubleData, double *bDoubleData, int height, int width) {<!-- --> if ((height <= 0) || (width <= 0)) return; if (rDoubleData == NULL || gDoubleData == NULL || bDoubleData == NULL || hDoubleData == NULL || sDoubleData == NULL || vDoubleData == NULL) return; int i; double Hi, Hf; for (i = 0; i < width * height; i + + ){<!-- --> if (sDoubleData[i] > 0){<!-- --> Hi = floor(hDoubleData[i] * 0.025); Hf = hDoubleData[i] * 0.025 - Hi; if (Hi == 0){<!-- --> rDoubleData[i] = vDoubleData[i]; gDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0*(1 - Hf)); bDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0); } else if (Hi == 1){<!-- --> rDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0 * Hf); gDoubleData[i] = vDoubleData[i]; bDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0); } else if (Hi == 2){<!-- --> rDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0); gDoubleData[i] = vDoubleData[i]; bDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0*(1 - Hf)); } else if (Hi == 3){<!-- --> rDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0); gDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0 * Hf); bDoubleData[i] = vDoubleData[i]; } else if (Hi == 4){<!-- --> rDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0*(1 - Hf)); gDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0); bDoubleData[i] = vDoubleData[i]; } else if (Hi == 5){<!-- --> rDoubleData[i] = vDoubleData[i]; gDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0); bDoubleData[i] = vDoubleData[i] * (1 - sDoubleData[i] / 255.0 * Hf); } } else{<!-- --> rDoubleData[i] = gDoubleData[i] = bDoubleData[i] = vDoubleData[i]; } } }
Channel splitting and merging C++ source code implementation
Three-channel split
In Halcon, the operator for splitting three channels is:
decompose3 (Image, ImageR, ImageG, ImageB)
The corresponding C++ function for splitting a three-channel image is:
//Split three-channel image /* Input: srcData: three-channel image, the memory arrangement is BGRBGRBGR... Output: rData: r channel image gData: g channel image bData: b channel image */ void decompose3(unsigned char *srcData, unsigned char *rData, unsigned char *gData, unsigned char *bData, int height, int width) {<!-- --> if ((height <= 0) || (width <= 0)) return; if (srcData == NULL || rData == NULL || gData == NULL || bData == NULL) return; int i; #pragma omp parallel for num_threads(3) for (i = 0; i < width * height; i + + ) {<!-- --> bData[i] = srcData[3 * i]; gData[i] = srcData[3 * i + 1]; rData[i] = srcData[3 * i + 2]; } }
Three-channel merger
In Halcon, the operator for splitting three channels is:
compose3 (ImageR1, ImageG1, ImageB1, MultiChannelImage)
The corresponding C++ function for merging three-channel images is:
//Merge three-channel images /* Input: rData: r channel image gData: g channel image bData: b channel image Output: bgrData: color images, merged into BGRBGR....arranged */ void compose3(unsigned char *rData, unsigned char *gData, unsigned char *bData, unsigned char *bgrData, int height, int width) {<!-- --> if ((height <= 0) || (width <= 0)) return; if (bgrData == NULL || rData == NULL || gData == NULL || bData == NULL) return; int i; #pragma omp parallel for num_threads(3) for (i = 0; i < width * height; i + + ) {<!-- --> bgrData[3 * i] = bData[i]; bgrData[3 * i + 1] = gData[i]; bgrData[3 * i + 2] = rData[i]; } }