搞科研的,尤其是搞图像的,大部分都比较熟练matlab,而不太会写c代码。
因此
我们需要
matlab代码转c代码
强调:如果你的程序有较多的matlab库里面没有公开的函数,那么那些函数是没办法转换的。
以下通过实例实现一幅图像的RGB转BRG
1.matlab coder
1)创建函数和用于运行函数的脚本
//main
clear
clc
img_name = '12345.jpg';
my_GBR(img_name);
//function
function [] = my_GBR(inputArg1)
I = imread(inputArg1);
g = I;
I(:,:,1) = g(:,:,2);
I(:,:,2) = g(:,:,3);
I(:,:,3) = g(:,:,1);
end
2.应用coder
这里有一点需要注意,在matlab中很多参数不需要提前开辟存储空间
但是c语言,每一个参数使用之前必须定义以开辟储存空间,尤其是数组
因此matlab代码中,如果你在for循环中用到了矩阵,那么要预先定义矩阵的大小
选择你要生成c代码的函数
点击next到输入定义,这里表示清楚输入,我们输入的是一个图像名称的字符串,因此为(1,:)
选择为函数设置的实例脚本
然后检查代码
其他不用配置,直接点生成就好
然后我们获得了c代码
3.创建项目
虽然我们得到了c代码,但是整体处于一盘散沙,没有工程文件,因此我们需要去生成工程文件
两种方法:
1)在vs里面创建工程,然后在工程文件中把c和h添加进去
2)用CMake生成
这里我们选用CMake
1)下载并安装CMake
https://cmake.org/download/
2)创建CMakeLists.txt
告诉CMake,工程的名字,以及包含的文件
文件的.c和.h都要包含进去,其实挺多的,我们可以用脚本,提取文件中所有文件的名字
创建一个txt文件,里面输入
@ECHO OFF
tree /F > 111111.txt
然后把.txt文件的后缀改为.bat
然后双击,生成111111.txt文件
然后把.h 和.c的文件名提取出来,用空格隔开(把main.h和main.c也包含过来同时把examples中的对应文件放到主目录下方便调试)
然后在CMakeLists.txt输入
cmake_minimum_required(VERSION 3.18.0)//需要的CMake版本最低值
project(matlabcoder)//项目的名称
add_executable(demo my_GBR.c my_GBR.h my_GBR_data.c my_GBR_data.h my_GBR_emxAPI.c my_GBR_emxAPI.h my_GBR_emxutil.c my_GBR_emxutil.h my_GBR_initialize.c my_GBR_initialize.h my_GBR_terminate.c my_GBR_terminate.h my_GBR_types.h rtwtypes.h main.c main.h)
打开 CMake程序,选定源文件夹和工程建立后的位置
如果上面的步骤成功了,你会看到生成的工程文件
双击sln文件,打开vs
2.在VS中调试
1)配置vs
右击demo,设置demo为启动项(如果你打开的界面和二郎的不一样,你可以打开资源管理器)
然后点击本地调试器,会看见少了文件
去matlab文件夹找到对应的.h,然后拷到工程文件夹
然后在txt中添加
之后重新生成。这里缺哪个,就去matlab中找哪个。
如果出现由于matlab库引起的无法解析的外部符号,可以去看我的上一篇博客
3.在VS配置输入
1)找到主函数,进入
将我们想要的输入放进来
我们试着运行一下,可以看到整体是没有问题的。
如果你希望这个输入在main函数中实现,那么你需要把输入作为参数,在函数中传递
例如:你要传递个矩阵(数组),那么你可以这样定义
#define N 5
static void men_RGB(double (*P)[N], int M);
void men_RGB(double (*P)[N], int M)
{
double A = P[2][1];
}
void main
{
double mat_image[][N] = {1,2,3,4,5,1,2,3,4,5,1,2,3,4,5};
//由于我们定义了矩阵的列,所有行数也就确定了,是一个3行5列的数组
int M = sizeof(mat_image)/sizeof(mat_image[0]);
men_RGB(mat_image, M);
}
这里用指针传递数组其实比较简单,而且也减少了内存的占用。
用指针来表示数组,只能指向数组头部,同时加上[N],可以指示有多少列。
但是并没有指示出有多少行。
因此在传递时,需要告诉其他函数,这个指针指向的数组有多少行,这样调用时就不会出错了。
在我们看来,指针指向了储存数组的一大片存储,但是,其实这一片存储不一定是连续的,这些就说来话长了,我们只需要知道此时指针指向的就是一大片。
我们来探讨一下,为什么要写成double (*P)[N]
1)二维数据是由多个一维数组组成
例如:double A[3][5];
它由三个一维数组组成:A[0];A[1];A[2]。(这里不用管为啥是行向量,因为c语言是这么定义的)
A[0]
A[1]
A[2]
又因为一维数组的数组名表示数组第一个元素的地址
因此,A[0] 等于 &A[0][0],即int* ddd = A[0];或者int* ddd = &A[0][0];ddd就指向了一维数组A[0]的首地址
*(ddd + 1)等于A[0][1]
*(ddd + 2)等于A[0][2]
2)一维数组的名为A[0],那么它的元素是A[0][0]、A[0][1]等。A[0]为数组第一个元素的地址——&A[0][0]
二维数组的名为A,那么它的元素是A[0]、A[1]、A[2]。A为数组第一个元素的地址——&A[0],即&&A[0][0]
所以这么看来,二维数组也挺简单的,取一次地址,即A[…],就可以找到一维数组了,再取一次地址,即A[…][…]就可以找到单个数了。
因此指针的指针是不是可以用来表示二维数组了???????
**p
int **P = A;
这样玩是有问题的,它和数组还是有些差别的。
其实大家有可能感觉出来,数组名有两个寓意
1)它是内部元素中首个元素的地址。
2)它暗含着自己有多少个元素的信息,这点非常重要,也是和指针区别的有效方式。(二维数组有mxn个元素,一维有n个元素)
因此
1)int **P指示一个指向指针的指针,不能和二维数组A画等号
但是
int (*P)[3] = A;
就可以了,即表示了首地址的信息,又告诉了A中有多少列。
(有时候只有列也行,数据填充时,能算出行数,赋值时也用过int A[][3] = {})
其实再具体一点,应该给出行的信息,于是