当前位置: 首页 > news >正文

Matlab/C++源码实现RGB通道与HSV通道的转换(效果对比Halcon)

HSV通道的含义

HSV通道是指图像处理中的一种颜色模型,它由色调(Hue)、饱和度(Saturation)和明度(Value)三个通道组成。色调表示颜色的种类,饱和度表示颜色的纯度或鲜艳程度,明度表示颜色的亮度。HSV通道常用于图像处理中的颜色分析、颜色过滤、颜色调整等任务,它相对于其他颜色模型具有更直观和易于调节的特点,因此被广泛应用于计算机视觉和图像处理的领域。

Halcon算子例程

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)

这里先将三通道RGB三通道拆开成单独的通道,再将RGB与HSV通道互相转换,最后将三通道图像合并成RGB图像。
Halcon的图像效果是:
在这里插入图片描述

源代码实现

RGB转成HSV

在这里插入图片描述

这里需要注意的是,halcon这里将HSV三通道的取值范围作了说明,H通道的数值范围是0到2*pi,S通道的数值范围是0到1,V通道的数值范围是0到1。而常用的图像为BYTE字节型,数值范围是0到255,这里对公式做了修改,使Matlab得出的图像数据范围是0到255,可以直接显示,这里可以从matlab的workspace中看到计算过程。
以下便是使用Matlab实现trans_from_rgb的效果

trans_from_rgb (ImageR, ImageG, ImageB, ImageH, ImageS, ImageV, 'hsv')
Matlab源码
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%代码--RGB通道转HSV通道
%时间: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);%%%转化成HSV通道
H_image = zeros(height,width);
S_image = zeros(height,width);
V_image = zeros(height,width);%%RGB转成HSV
for i=1:1:heightfor j=1:1:width%%%计算三通道的最大最小值计算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%%%计算饱和度S_image(i,j) = (maxValue - minValue)*255/minValue;%%%计算H通道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));endendend
end%%%RGB要取整
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');

最终实现的效果是:
在这里插入图片描述
最终验证的效果与halcon效果一致;

同时,以上代码采用C++实现的话如下所示,这里为了保证精度,输出结果采用的是double类型,但是范围也是0到255之间,要显示的话,需要转化为unsigned char类型:

C++源码
//将RGB图像转化成HSV图像
/*
输入:  rData : r通道图像gData : g通道图像bData : b通道图像
输出:  hDoubleData : h通道图像, h通道采用double类型,保留精度sDoubleData : s通道图像,s通道采用double类型,保留精度vDoubleData : v通道图像,v通道采用double类型,保留精度
*/
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;//在函数外部分配好内存空间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通道数据,三通道的最大值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通道sDoubleData[i] = (maxValue - minValue)*255.0 / maxValue;//H通道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));}}
}
HSV转成RGB

halcon给出的公式说明为:
在这里插入图片描述

Matlab源码

同样的,采用Matlab实现:

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%代码--HSV通道转RGB通道
%时间: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);%%%转化成RGB通道
R_image = zeros(height,width);
G_image = zeros(height,width);
B_image = zeros(height,width);%%%HSV转成RGB
for i=1:1:heightfor j=1:1:widthif(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));    %%归一化到02*piHi = floor(image_H(i,j)*0.025);Hf = image_H(i,j)*0.025 - Hi;%%%%根据H的值,将C,X,m分别对应到RGB三个分量上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);endendend
end%%%RGB要取整
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');

最终实现的效果是:
在这里插入图片描述
可以看出,与Halcon效果一致;

C++源码

同样的,采用C++实现:

//将HSV图像转化成RGB图像
/*
输入:  hDoubleData : h通道图像 ,h通道采用double类型,保留精度sDoubleData : s通道图像 ,s通道采用double类型,保留精度vDoubleData : v通道图像 ,v通道采用double类型,保留精度
输出:  rDoubleData : r通道图像 ,r通道采用double类型,保留精度gDoubleData : g通道图像 ,g通道采用double类型,保留精度bDoubleData : b通道图像 ,b通道采用double类型,保留精度
*/
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];}}
}

通道拆分与合并C++源代码实现

三通道拆分

Halcon中,拆分三通道的算子为:

decompose3 (Image, ImageR, ImageG, ImageB)

对应的拆分三通道图像的C++函数为:

//拆分三通道图像
/*
输入:  srcData : 三通道图像,内存排列方式是BGRBGRBGR......
输出:  rData : r通道图像gData : g通道图像bData : b通道图像
*/
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];}
}

三通道合并

Halcon中,拆分三通道的算子为:

compose3 (ImageR1, ImageG1, ImageB1, MultiChannelImage)

对应的合并三通道图像的C++函数为:

//合并三通道图像
/*
输入:  rData : r通道图像gData : g通道图像bData : b通道图像
输出:  bgrData:彩色图像,合并成BGRBGR.....排列
*/
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];}
}

相关文章:

Matlab/C++源码实现RGB通道与HSV通道的转换(效果对比Halcon)

HSV通道的含义 HSV通道是指图像处理中的一种颜色模型&#xff0c;它由色调&#xff08;Hue&#xff09;、饱和度&#xff08;Saturation&#xff09;和明度&#xff08;Value&#xff09;三个通道组成。色调表示颜色的种类&#xff0c;饱和度表示颜色的纯度或鲜艳程度&#xf…...

【C进阶】动态内存管理

一、为什么存在动态内存分配 我们之前学的都是开辟固定大小的空间&#xff0c;但有时候需要空间的大小只有在程序运行时才能知道&#xff0c;那么就引入了动态内存开辟 内存分布所在&#xff1a; 二、动态内存函数的介绍 2.1malloc和free 动态内存开辟的函数 void * malloc…...

神经网络的梯度优化方法

神经网络的梯度优化是深度学习中至关重要的一部分&#xff0c;它有助于训练神经网络以拟合数据。下面将介绍几种常见的梯度优化方法&#xff0c;包括它们的特点、优缺点以及原理。 梯度下降法 (Gradient Descent): 特点: 梯度下降是最基本的优化算法&#xff0c;它试图通过迭代…...

linux 装机教程(自用备忘)

文章目录 安装 pyenv 管理多版本 python 环境安装使用使用 pyenv 和 virtualenv 管理虚拟 python 环境 vscode 连接远程服务器tmux 美化zsh 安装 pyenv 管理多版本 python 环境 安装 &#xff08;教程参考&#xff1a;https://www.modb.pro/db/155036&#xff09; sudo apt-…...

Tensorboard安装及简单使用

Tensorboard 1. tensorboard 简单介绍2. 安装必备环境3. Tensorboard安装4. 可视化命令 1. tensorboard 简单介绍 TensorBoard是一个可视化的模块&#xff0c;该模块功能强大&#xff0c;可用于深度学习网络模型训练查看模型结构和训练效果&#xff08;预测结果、网络模型结构…...

SpringCloud 微服务全栈体系(二)

第三章 Eureka 注册中心 假如我们的服务提供者 user-service 部署了多个实例&#xff0c;如图&#xff1a; 思考几个问题&#xff1a; order-service 在发起远程调用的时候&#xff0c;该如何得知 user-service 实例的 ip 地址和端口&#xff1f;有多个 user-service 实例地址…...

flutter 常用组件:列表ListView

文章目录 总结#1、通过构造方法直接构建 ListView 提供了一个默认构造函数 ListView,我们可以通过设置它的 children 参数,很方便地将所有的子 Widget 包含到 ListView 中。 不过,这种创建方式要求提前将所有子 Widget 一次性创建好,而不是等到它们真正在屏幕上需要显示时才…...

十四天学会C++之第七天:STL(标准模板库)

1. STL容器 什么是STL容器&#xff0c;为什么使用它们。向量&#xff08;vector&#xff09;&#xff1a;使用向量存储数据。列表&#xff08;list&#xff09;&#xff1a;使用列表实现双向链表。映射&#xff08;map&#xff09;&#xff1a;使用映射实现键值对存储。 什么…...

Linux 下安装 miniconda,管理 Python 多环境

安装 miniconda 1、下载安装包 Miniconda3-py37_22.11.1-1-Linux-x86_64.sh&#xff0c;或者自行选择版本 2、把安装包上传到服务器上&#xff0c;这里放在 /home/software 3、安装 bash Miniconda3-py37_22.11.1-1-Linux-x86_64.sh 4、按回车 Welcome to Miniconda3 py37…...

Django和jQuery,实现Ajax表格数据分页展示

1.需求描述 当存在重新请求接口才能返回数据的功能时&#xff0c;若页面的内容很长&#xff0c;每次点击一个功能&#xff0c;页面又回到了顶部&#xff0c;对于用户的体验感不太友好&#xff0c;我们希望当用户点击这类的功能时&#xff0c;能直接加载到数据&#xff0c;请求…...

k8s认证

1. 证书介绍 服务端保留公钥和私钥&#xff0c;客户端使用root CA认证服务端的公钥 一共有多少证书&#xff1a; *Etcd&#xff1a; Etcd对外提供服务&#xff0c;要有一套etcd server证书Etcd各节点之间进行通信&#xff0c;要有一套etcd peer证书Kube-APIserver访问Etcd&a…...

基于python开发的IP修改工具

工作中调试设备需要经常修改电脑IP&#xff0c;非常麻烦&#xff0c;这里使用Pythontkinter做了一个IP修改工具 说明: 1.启动程序读取config.json文件2.如果没有该文件则创建&#xff0c;写入当前网卡信息3.通过配置信息进行网卡状态修改4.更新文件状态&#xff0c;删除或修…...

Mybatis源码分析

1. Mybatis整体三层设计 SSM中&#xff0c;Spring、SpringMVC已经在前面文章源码分析总结过了&#xff0c;Mybatis源码相对Spring和SpringMVC而言是的简单的&#xff0c;只有一个项目&#xff0c;项目下分了很多包。从宏观上了解Mybatis的整体框架分为三层&#xff0c;分别是基…...

python树结构包treelib入门及其计算应用

树是计算机科学中重要的数据结构。例如决策树等机器学习算法设计、文件系统索引等。创建treelib包是为了在Python中提供树数据结构的有效实现。 Treelib的主要特点包括&#xff1a; 节点搜索的高效操作。支持常见的树操作&#xff0c;如遍历、插入、删除、节点移动、浅/深复制…...

Rust之自动化测试(三): 测试组合

开发环境 Windows 10Rust 1.73.0 VS Code 1.83.1 项目工程 这里继续沿用上次工程rust-demo 测试组合 正如本章开始时提到的&#xff0c;测试是一个复杂的学科&#xff0c;不同的人使用不同的术语和组织。Rust社区根据两个主要类别来考虑测试:单元测试和集成测试。单元测试很…...

专业管理菜单的增删改、查重

1&#xff0c;点击专业管理菜单------查询所有专业信息列表 ①点击菜单&#xff0c;切换专业组件 ②切换到列表组件后&#xff0c;向后端发送请求到Servlet ③调用DAO层&#xff0c;查询数据库&#xff08;sql&#xff09;&#xff0c;封装查询到的内容 ④从后端向前端做出…...

vue3插件开发,上传npm

创建插件 在vue3工程下&#xff0c;创建组件vue页: toolset.vue。并设置组件名称。注册全局组件。新建index.js文件。内容如下&#xff0c;可在main.js中引入index.js&#xff0c;注册该组件进行测试。![在这里插入图片描述](https://img-blog.csdnimg.cn/a3409d2cbeec41c797d5…...

python【多线程、单线程、异步编程】三个版本--在爬虫中的应用

并发编程在爬虫中的应用 之前的课程&#xff0c;我们已经为大家介绍了 Python 中的多线程、多进程和异步编程&#xff0c;通过这三种手段&#xff0c;我们可以实现并发或并行编程&#xff0c;这一方面可以加速代码的执行&#xff0c;另一方面也可以带来更好的用户体验。爬虫程…...

大模型LLM相关面试题整理-位置编码-tokenizer-激活函数-layernorm

10 LLMs 位置编码篇 10.1.1 什么是位置编码&#xff1f; 位置编码是一种用于在序列数据中为每个位置添加位置信息的技术。在自然语言处理中&#xff0c;位置编码通常用于处理文本序列。由于传统的神经网络无法直接捕捉输入序列中的位置信息&#xff0c;位置编码的引入可以帮助…...

python在nacos注册微服务

安装 首先需要安装python的nacos sdk pip install nacos-sdk-python 注册 注册过程非常简单&#xff0c;需要注意的是&#xff0c;注册完要定时发送心跳&#xff0c;否则服务会被nacos删掉。 import nacos import timeSERVER_ADDRESSES "http://1.2.3.4:8848" …...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...