2017-04-06 20:18:23 linshanxian 阅读数 1210
  • 海思平台ISP与图像的IQ调试-第9季

    本课程讲解sensor直接采集的图像都有各种不理想性,因此在编码前都会经过一个软件方式处理,这个处理就叫ISP,图像的IQ调试就是研究这些处理算法和实现的。常见的IQ调试技术如:线性纠正、噪声去除、黑电平校正、坏点去除、颜色插补、Gamma 校正、RGB2YUV 转换、主动白平衡处理、主动曝光控制、AE评估等。 随着通信行业发展,网速越来越快,网络也从文本时代发展越过语音时代到了现在视频时代,“优酷、爱奇艺”、“微视频”、“直播”等的出现也是视频逐步成为主流媒体的证据和表现。航拍、视频监控、刷脸过关检测、汽车ADAS系统等应用也是视频在主流行业中的应用,因此视频的采集、编解码、传输、识别等技术变得越来越有价值,涌现出了“海康威视”、“大华股份”、“深圳英飞拓”等一批明星企业,名扬海内外,动辄市值几百亿。同时在芯片级,国产华为海思HI35xx系列视频编解码方案也逐步取代进口芯片(如TI、安霸)成为主流方案。 视频行业技术含量高、难度大、学习周期长、发展变化快而资料少,因此行业比较缺人、工资较高。如海康威视,普通高校硕士研究生应届进入年薪普遍高于15-20万,在嵌入式linux领域也算高工资技术方向了。 本项目是朱老师及其团队推出的一个嵌入式linux领域重量级企业级实战项目,选用华为海思HI3518E方案,结合OV9712/AR0130 Sensor实现图像采集和H.264编码压缩,并通过ethernet和WIFI,以socket实时流和RTSP等方式,实现720P/960P高清视频传输。本项目共分11季,从海思SDK开发环境搭建,到sample程序讲解、SDK中API函数解析,到H.264编解码、RTSP流媒体传输、MP4文件打包,到图像IQ调试、图像识别等视频领域高阶内容,可以说从零基础入手,对图像采集、编解码、网络传输、图像存储和识别做了全方位的详细讲解和代码分析,是目前市面上**一套系统讲解图像和视频技术的视频课程。

    81 人正在学习 去看看 朱有鹏

图像缩放是将图像按照一定比例放大或者缩小,对于数字图像而言,像素的坐标是离散型非负整数,但是在进行缩放的过程中有可能产生浮点坐标值。例如,原图像坐标(9,9)在缩小一倍时会变成(4.5,4.5),这显然是一个无效的坐标。因此需要用到图像插值方法。常见的插值算法有最邻近插值法、双线性插值法,二次立方插值法,三次立方插值法等。本文主要介绍最邻近插值、双线性插值和三次立方插值,其他一些高阶的插值算法,以后再做研究。

1.最近邻插值

最近邻插值是最简单的图像缩放处理方法,其原理是提取原图像中与其邻域最近像素值来作为目标图像相对应的像素值。简单来说就是四舍五入,浮点坐标的像素值等于距离该点最近的输入图像的像素值。最邻近插值几乎没有多余的运算,速度相当快。但是这种邻近取值的方法是很粗糙的,会造成图像的马赛克、锯齿等现象。

假设原图像中的点A0(x0y0)经过缩放后目标图像中的坐标为A1(x1y1),x方向和y方向的缩放比例为kxky,则变换矩阵为:



在opencv中提供了3个浮点数转换成整数的函数,分别是cvRound、cvFloor和cvCeil。cvRound函数返回和参数最接近的整数值,四舍五入。CvFloot函数返回不大于参数的最大整数值,向下取整。cvCeil返回不小于参数的最下整数值,向上取整。

最近邻插值的实现代码如下:

void NearstInterpolation(const Mat& srcImage, Mat &dstImage, double kx, double ky)
{
    CV_Assert(srcImage.data != NULL);
    double inv_kx = 1.0 / kx;
    double inv_ky = 1.0 / ky;
    int srcRowNum = srcImage.rows;
    int srcColNum = srcImage.cols;
    int dstRowNum = cvRound(srcImage.rows * ky);
    int dstColNum = cvRound(srcImage.cols * kx);
    dstImage.create(dstRowNum, dstColNum, srcImage.type());

    for(int i = 0; i < dstRowNum; i++)
    {
        int y = cvRound(i * inv_ky);
        if(y > srcRowNum - 1)
        {
            y = srcRowNum - 1;
        }
        for(int j = 0; j < dstColNum; j++)
        {
            int x = cvRound(j * inv_kx);
            if(x > srcColNum - 1)
            {
                x = srcColNum - 1;
            }
            dstImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(y, x);
        }
    }
}

2.双线性插值

双线性插值是应用最广泛的图像插值方法,它的插值效果比最邻近插值要好很多,相应的计算速度也要慢上不少。双线性插值的主要思想是计算出浮点坐标周围的四个整数坐标,将这个四个整数坐标的像素值加权平均就可以求出浮点坐标的像素值。

假设要求坐标为(2.4,3)的像素值P,该点在(2,3)和(3,3)之间,如下图

uv分别是距离浮点坐标最近两个整数坐标像素在浮点坐标像素所占的比例(与距离成反比,离得越近,权值越大),那么P(2.4,3) = u * P(2,3)+ v * P(3,3)。上面是只在一条直线的插值,称为线性插值。双线性插值就是分别在X轴和Y轴做线性插值运算。下面利用三次的线性插值进行双线性插值运算。


(2.4,3)的像素值 F1 = (1 – m) * T1 + m* T
(2.4,4)的像素值 F2 =(1 – m) * T3+ m * T4 
(2.4,3.5)的像素值 F = (1 – n) * F1 + n* F

这样就可以求得浮点坐标(2.4,3.5)的像素值了。 上面就是双线性插值的基本过程,假设经过过图像缩放的逆变换后目标图像的某点(x,y)在原图像的坐标为(x0,y0),其像素值为f(x0,y0),双线性插值的公式如下:


由于浮点坐标由邻域内4个坐标加权后求得,一定程度上弱化了高频分量。但如果这4个坐标的像素值差别较大,插值后,会使得图像在颜色分界较为明显的地方变得比较模糊。

双线性插值的主要代码如下:

void Biline(const Mat& srcImage, Mat& dstImage, double kx, double ky)
{
    CV_Assert(srcImage.data != NULL);
    double inv_kx = 1.0 / kx;
    double inv_ky = 1.0 / ky;
    int srcRowNum = srcImage.rows;
    int srcColNum = srcImage.cols;
    int dstRowNum = srcImage.rows * kx;
    int dstColNum = srcImage.cols * ky;
    dstImage.create(dstRowNum, dstColNum, srcImage.type());
    for(int i = 0; i < dstRowNum; i++)
    {
       double srcy = (i + 0.5) * inv_ky - 0.5;
        int k = cvFloor(srcy);
        k = min(k, srcRowNum - 2);
        k = max(0, k);
        double n = srcy - k;
        for(int j = 0; j < dstColNum; j++)
        {
            double srcx = j * inv_kx;
            int l = cvFloor(srcx);
            l = min(l, srcColNum - 2);
            l = max(0, l);
            double m = srcx - l;
            dstImage.at<Vec3b>(i, j)[0] = (1 - m) * (1 - n) * srcImage.at<Vec3b>(k, l)[0] + m * (1 - n) * srcImage.at<Vec3b>(k, l + 1)[0]
 + n * (1 - m) * srcImage.at<Vec3b>(k + 1, l)[0] + m * n * srcImage.at<Vec3b>(k + 1, l + 1)[0];
            dstImage.at<Vec3b>(i, j)[1] = (1 - m) * (1 - n) * srcImage.at<Vec3b>(k, l)[1]+ m * (1 - n) * srcImage.at<Vec3b>(k, l + 1)[1]
+ n * (1 - m) * srcImage.at<Vec3b>(k + 1, l)[1] + m * n * srcImage.at<Vec3b>(k + 1, l + 1)[1];
            dstImage.at<Vec3b>(i, j)[2] = (1 - m) * (1 - n) * srcImage.at<Vec3b>(k, l)[2] + m * (1 - n) * srcImage.at<Vec3b>(k, l + 1)[2]
+ n * (1 - m) * srcImage.at<Vec3b>(k + 1, l)[2]+ m * n * srcImage.at<Vec3b>(k + 1, l + 1)[2];
        }
    }
}

值得一提的是上述程序中计算源坐标采用的是

原因很简单,就是坐标系的选择问题,或者说源图像和目标图像之间的对应问题。相关解释在其他博客上都可以找到,这里不再赘述。

3.双三次插值

双三次插值又叫立方卷积插值、双立方插值是一种更加复杂的插值方式,它能创造出比双线性插值更平滑的图像边缘。双三次插值利用待采样点周围16个点的灰度值作三次插值,不仅考虑到周围四个直接相邻像素点灰度值的影响,还考虑到它们灰度值变化率的影响。在这种方法中,函数 f 在点(x, y) 的值可以通过矩形网格中最近的十六个采样点的加权平均得到,在这里需要使用两个多项式插值三次函数,每个方向使用一个。更多介绍参考维基百科:

https://en.wikipedia.org/wiki/Bicubic_interpolation


其中,a取-0.75或-0.5。

假设经过逆变换后目标图像的某点(x,y)在原图像的坐标为(x0,y0),可按下列步骤求出f(x0,y0)


代码如下:

/****************************************************************
*Name:双三次插值
*Date: 2017.02.26
*****************************************************************/
void BicubicInterpolation(const Mat& srcImage, Mat &dstImage, double kx, double ky)
{
    CV_Assert(srcImage.data != NULL);
    double inv_kx = 1.0 / kx;
    double inv_ky = 1.0 / ky;

    int srcRowNum = srcImage.rows;
    int srcColNum = srcImage.cols;
    int dstRowNum = srcImage.rows * kx;
    int dstColNum = srcImage.cols * ky;

    dstImage.create(dstRowNum, dstColNum, srcImage.type());

    for(int i = 0; i < dstRowNum; i++)
    {
        double srcy = (i + 0.5) * inv_ky - 0.5;
        int k = cvFloor(srcy);
        k = min(k, srcRowNum - 3);
        k = max(1, k);
        double n = srcy - k;
        const double A = -0.75;
        double cbufY[4];
        cbufY[0] = ((A * (n + 1) - 5 * A) * (n + 1) + 8 * A) * (n + 1) - 4 * A;
        cbufY[1] = ((A + 2) * (n) - (A + 3)) * n * n + 1;
        cbufY[2] = ((A + 2) * (1 - n) - (A + 3)) * (1 - n) * (1 - n) + 1;
        cbufY[3] = ((A * (2 - n) - 5 * A) * (2 - n) + 8 * A) * (2 - n) - 4 * A;
        for(int j = 0; j < dstColNum; j++)
        {
            double srcx = (j + 0.5) * inv_kx - 0.5;
            int l = cvFloor(srcx);
            l = min(l, srcColNum - 3);
            l = max(1, l);
            double m = srcx - l;
            float cbufX[4];
            cbufX[0] = ((A * (m + 1) - 5 * A) * (m + 1) + 8 * A) * (m + 1) - 4 * A;
            cbufX[1] = ((A + 2) * (m) - (A + 3)) * m * m + 1;
            cbufX[2] = ((A + 2) * (1 - m) - (A + 3)) * (1 - m) * (1 - m) + 1;
            cbufX[3] = ((A * (2 - m) - 5 * A) * (2 - m) + 8 * A) * (2 - m) - 4 * A;
            for(int cn = 0; cn < srcImage.channels(); ++cn)
            {
                dstImage.at<cv::Vec3b>(i, j)[cn] = saturate_cast<uchar>(srcImage.at<Vec3b>(k - 1, l - 1)[cn] * cbufX[0] * cbufY[0] 
					                                                  + srcImage.at<Vec3b>(k, l - 1)[cn] * cbufX[0] * cbufY[1] 
																	  + srcImage.at<Vec3b>(k + 1, l - 1)[cn] * cbufX[0] * cbufY[2] 
																	  + srcImage.at<Vec3b>(k + 2, l - 1)[cn] * cbufX[0] * cbufY[3] 
																	  + srcImage.at<Vec3b>(k - 1, l)[cn] * cbufX[1] * cbufY[0] 
																	  + srcImage.at<Vec3b>(k, l)[cn] * cbufX[1] * cbufY[1] 
																	  + srcImage.at<Vec3b>(k + 1, l)[cn] * cbufX[1] * cbufY[2] 
																	  + srcImage.at<Vec3b>(k + 2, l)[cn] * cbufX[1] * cbufY[3] 
																	  + srcImage.at<Vec3b>(k - 1, l + 1)[cn] * cbufX[2] * cbufY[0] 
																	  + srcImage.at<Vec3b>(k, l + 1)[cn] * cbufX[2] * cbufY[1] 
																	  + srcImage.at<Vec3b>(k + 1, l + 1)[cn] * cbufX[2] * cbufY[2] 
																	  + srcImage.at<Vec3b>(k + 2, l + 1)[cn] * cbufX[2] * cbufY[3] 
																	  + srcImage.at<Vec3b>(k - 1, l + 2)[cn] * cbufX[3] * cbufY[0] 
																	  + srcImage.at<Vec3b>(k, l + 2)[cn] * cbufX[3] * cbufY[1] 
																	  + srcImage.at<Vec3b>(k + 1, l + 2)[cn] * cbufX[3] * cbufY[2] 
																	  + srcImage.at<Vec3b>(k + 2, l + 2)[cn] * cbufX[3] * cbufY[3]);
            }
        }
    }
}





2019-10-07 19:53:33 effort_study 阅读数 37
  • 海思平台ISP与图像的IQ调试-第9季

    本课程讲解sensor直接采集的图像都有各种不理想性,因此在编码前都会经过一个软件方式处理,这个处理就叫ISP,图像的IQ调试就是研究这些处理算法和实现的。常见的IQ调试技术如:线性纠正、噪声去除、黑电平校正、坏点去除、颜色插补、Gamma 校正、RGB2YUV 转换、主动白平衡处理、主动曝光控制、AE评估等。 随着通信行业发展,网速越来越快,网络也从文本时代发展越过语音时代到了现在视频时代,“优酷、爱奇艺”、“微视频”、“直播”等的出现也是视频逐步成为主流媒体的证据和表现。航拍、视频监控、刷脸过关检测、汽车ADAS系统等应用也是视频在主流行业中的应用,因此视频的采集、编解码、传输、识别等技术变得越来越有价值,涌现出了“海康威视”、“大华股份”、“深圳英飞拓”等一批明星企业,名扬海内外,动辄市值几百亿。同时在芯片级,国产华为海思HI35xx系列视频编解码方案也逐步取代进口芯片(如TI、安霸)成为主流方案。 视频行业技术含量高、难度大、学习周期长、发展变化快而资料少,因此行业比较缺人、工资较高。如海康威视,普通高校硕士研究生应届进入年薪普遍高于15-20万,在嵌入式linux领域也算高工资技术方向了。 本项目是朱老师及其团队推出的一个嵌入式linux领域重量级企业级实战项目,选用华为海思HI3518E方案,结合OV9712/AR0130 Sensor实现图像采集和H.264编码压缩,并通过ethernet和WIFI,以socket实时流和RTSP等方式,实现720P/960P高清视频传输。本项目共分11季,从海思SDK开发环境搭建,到sample程序讲解、SDK中API函数解析,到H.264编解码、RTSP流媒体传输、MP4文件打包,到图像IQ调试、图像识别等视频领域高阶内容,可以说从零基础入手,对图像采集、编解码、网络传输、图像存储和识别做了全方位的详细讲解和代码分析,是目前市面上**一套系统讲解图像和视频技术的视频课程。

    81 人正在学习 去看看 朱有鹏

插值算法

输出像素一个一个地映射回输入图像。如果一个输出像素映射到输入图像中的采样栅格的整数坐标处的像素点,此时其灰度值就需要基于整数坐标的灰度值进行推断,这就是插值。

最近邻插值

输出像素的值为输入图像中与其最邻近的采样点的像素值。

双线性插值

是用四个点加权平均得到的灰度值的值,作为输出像素灰度值。

高阶插值

双线性插值的平滑效果会使图像的细节退化,而其斜率的不连续性会导致变换产生不希望的结果。高阶插值常用卷积来实现,输出像素的值为输入图像中距离他最近的4x4领域内采样点像素值的加权平均值。

代码实现

a=imread('rectangle.bmp');
b=imrotate(a,30,'nearest');%最近邻插值
c=imrotate(a,30,'bilinear');%双线性插值
d=imrotate(a,30,'bicubic');%三次插值
figure;
subplot(2,2,1);imshow(a);
subplot(2,2,2);imshow(b);
subplot(2,2,3);imshow(c);
subplot(2,2,4);imshow(d);

在这里插入图片描述
可以较明显地看出,使用最近邻插值法后,纯白图像与黑色背景相接处地锯齿,要比其他两种插值算法的边缘粗糙。

图像配准

简单地说就是按照基准图像和原图像中作对应点,完成四个点后即可实现图像的转换的对应。

clear;
lin=imread('face.jpg');
lbase=imread('2.jpg');
[height,width,dim]=size(lin);
tform=maketform('affine',[1 0 0;0 -1 0;0 height 1;]);
c=imtransform(lin,tform,'nearest');%先把图像竖直镜像,以便观察效果
figure;
subplot(1,2,1);imshow(c);
subplot(1,2,2);imshow(lbase);

在这里插入图片描述

cpselect(c,lbase);%交互式选择基准点

在这里插入图片描述
依次按照基准图像的顺序选择图像对应的点,然后将变量输出到工作空间。键入以下代码

tform=cp2tform(fixedPoints,movingPoints,'affine');%仿射变换模型
iout=imtransform(c,tform);%进行变换,从而实现配准
figure;
subplot(1,2,1);imshow(iout);
subplot(1,2,2);imshow(lbase);

在这里插入图片描述

2015-02-02 10:18:32 tangshuai8888 阅读数 943
  • 海思平台ISP与图像的IQ调试-第9季

    本课程讲解sensor直接采集的图像都有各种不理想性,因此在编码前都会经过一个软件方式处理,这个处理就叫ISP,图像的IQ调试就是研究这些处理算法和实现的。常见的IQ调试技术如:线性纠正、噪声去除、黑电平校正、坏点去除、颜色插补、Gamma 校正、RGB2YUV 转换、主动白平衡处理、主动曝光控制、AE评估等。 随着通信行业发展,网速越来越快,网络也从文本时代发展越过语音时代到了现在视频时代,“优酷、爱奇艺”、“微视频”、“直播”等的出现也是视频逐步成为主流媒体的证据和表现。航拍、视频监控、刷脸过关检测、汽车ADAS系统等应用也是视频在主流行业中的应用,因此视频的采集、编解码、传输、识别等技术变得越来越有价值,涌现出了“海康威视”、“大华股份”、“深圳英飞拓”等一批明星企业,名扬海内外,动辄市值几百亿。同时在芯片级,国产华为海思HI35xx系列视频编解码方案也逐步取代进口芯片(如TI、安霸)成为主流方案。 视频行业技术含量高、难度大、学习周期长、发展变化快而资料少,因此行业比较缺人、工资较高。如海康威视,普通高校硕士研究生应届进入年薪普遍高于15-20万,在嵌入式linux领域也算高工资技术方向了。 本项目是朱老师及其团队推出的一个嵌入式linux领域重量级企业级实战项目,选用华为海思HI3518E方案,结合OV9712/AR0130 Sensor实现图像采集和H.264编码压缩,并通过ethernet和WIFI,以socket实时流和RTSP等方式,实现720P/960P高清视频传输。本项目共分11季,从海思SDK开发环境搭建,到sample程序讲解、SDK中API函数解析,到H.264编解码、RTSP流媒体传输、MP4文件打包,到图像IQ调试、图像识别等视频领域高阶内容,可以说从零基础入手,对图像采集、编解码、网络传输、图像存储和识别做了全方位的详细讲解和代码分析,是目前市面上**一套系统讲解图像和视频技术的视频课程。

    81 人正在学习 去看看 朱有鹏

 最简单的插值算法是最邻近插值,也称为零阶插值。它输出的像素灰度值就等于距离它映射到的位置最近的输入像素的灰度值,最邻近插值算法简单,在许多情况下都能得到令人满意的结果,但是当图像中包含像素之间灰度级有变化的细微结构时,最邻近算法会在图像中产生人为加工的痕迹。双线性插值算法计算量比零阶插值大,但缩放后图像质量高,不会出现像素值不连续的情况,这样就可以获得一个令人满意的结果。

   双线性插值是利用了需要处理的原始图像像素点周围的四个像素点的相关性,通过双线性算法计算得出的。对于一个目的坐标,通过向后映射法得到其在原始图像的对应的浮点坐标(i+u,j+v),其中i,j均为非负整数,u,v为[0,1]区间的浮点数,则这个像素的值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:f(i+u,j+v) =(1-u)×(1-v) ×f(i,j)+(1-u) ×v×f(i,j+1)+u×(1-v) ×f(i+1,j)+u×v×f(i+1,j+1),其中f(i,j)表示源图像(i,j)处的像素值,以此类推,这就是双线性内插值法。

如图1所示,已知(0,0)、(0,1)、(1,0)、(1,1)四点的灰度,可以由相邻像素的灰度值f(0,0)和f(1,0) 在X方向上线性插值求出(x,0)的灰度f(x,0),由另外两个相邻像素f(0,1)和f(1,1)在X方向上线性插值可求出(x,1)的灰度f(x,1),最后由f(x,0)、f(x,1)在Y方向上进行线性插值就可以得到(x,y)的灰度f(x,y)。


算法步骤:

假设原始图像大小为size=m×n,其中m与n分别是原始图像的行数与列<数。若图像的缩放因子是t(t>0),则目标图像的大小size=t×m×t×n。对于目标图像的某个像素点P(x,y)通过P*1/t可得到对应的原始图像坐标P’( x1,y1),其中x1=x/t,y1=y/t,由于x1,y1都不是整数所以并不存在这样的点,这样可以找出与它相邻的四个点的灰度f1、f2、f3、f4,使用双线性插值算法就可以得到这个像素点P’(x1,y1)的灰度,也就是像素点P(x,y)的灰度。

一个完整的双线性插值算法可描述如下:

(1)       通过原始图像和比例因子得到新图像的大小,并创建新图像。

(2)       由新图像的某个像素(x,y)映射到原始图像(x’,y’)处。

(3)       对x’,y’取整得到(xx,yy)并得到(xx,yy)、(xx+1,yy)、(xx,yy+1)和(xx+1,yy+1)的值。

(4)       利用双线性插值得到像素点(x,y)的值并写回新图像。

(5)       重复步骤(2)直到新图像的所有像素写完。

核心代码:

void ImageResize(const IplImage* src, IplImage* dst){


int src_width =  src->width;
int src_height = src->height;
int dst_width =  dst->width;
int dsr_height = dst->height;
int src_step = src->widthStep;
int dst_step = dst->widthStep;


unsigned char* src_data =(unsigned char*) src->imageData;
unsigned char* dst_data =(unsigned char*) dst->imageData;


float horScaleRatio = src_width / (float)(dst_width);
float verScaleRatio = src_height / (float)(dsr_height);
int int_srcx1,int_srcx2,int_srcy1,int_srcy2;
float weight_x1,weight_x2,weight_y1,weight_y2;


for(int y=0; y < dsr_height; y++){
float srcy1 = y * verScaleRatio;
int_srcy1 = (int)srcy1;
if(int_srcy1 == src_height){
int_srcy2 = int_srcy1;
}
else{
int_srcy2 = int_srcy1 + 1;
}
weight_y1 = srcy1 - int_srcy1;
weight_y2 = 1.0f - weight_y1;




for(int x=0; x< dst_width; x++){
float srcx1 = x * horScaleRatio;
int_srcx1 = (int)srcx1;
if(int_srcx1 == src_width){
int_srcx2 = int_srcx1;
}
else{
int_srcx2 = int_srcx1 + 1;
}
weight_x1 = srcx1 - int_srcx1;
weight_x2 = 1.0f - weight_x1;
double k = 0;
k = (weight_x1 * weight_y1) * src_data[int_srcy2*src_step + int_srcx2] + (weight_x2 * weight_y1) * src_data[int_srcy2*src_step + int_srcx1] + (weight_x2 * weight_y2) * src_data[int_srcy1*src_step + int_srcx1] +(weight_x1 * weight_y2) * src_data[int_srcy1*src_step + int_srcx2];
dst_data[y*dst_step + x] = (int)k;
}
}
}

代码实现图像:


2019-04-30 13:38:14 qq_18293213 阅读数 626
  • 海思平台ISP与图像的IQ调试-第9季

    本课程讲解sensor直接采集的图像都有各种不理想性,因此在编码前都会经过一个软件方式处理,这个处理就叫ISP,图像的IQ调试就是研究这些处理算法和实现的。常见的IQ调试技术如:线性纠正、噪声去除、黑电平校正、坏点去除、颜色插补、Gamma 校正、RGB2YUV 转换、主动白平衡处理、主动曝光控制、AE评估等。 随着通信行业发展,网速越来越快,网络也从文本时代发展越过语音时代到了现在视频时代,“优酷、爱奇艺”、“微视频”、“直播”等的出现也是视频逐步成为主流媒体的证据和表现。航拍、视频监控、刷脸过关检测、汽车ADAS系统等应用也是视频在主流行业中的应用,因此视频的采集、编解码、传输、识别等技术变得越来越有价值,涌现出了“海康威视”、“大华股份”、“深圳英飞拓”等一批明星企业,名扬海内外,动辄市值几百亿。同时在芯片级,国产华为海思HI35xx系列视频编解码方案也逐步取代进口芯片(如TI、安霸)成为主流方案。 视频行业技术含量高、难度大、学习周期长、发展变化快而资料少,因此行业比较缺人、工资较高。如海康威视,普通高校硕士研究生应届进入年薪普遍高于15-20万,在嵌入式linux领域也算高工资技术方向了。 本项目是朱老师及其团队推出的一个嵌入式linux领域重量级企业级实战项目,选用华为海思HI3518E方案,结合OV9712/AR0130 Sensor实现图像采集和H.264编码压缩,并通过ethernet和WIFI,以socket实时流和RTSP等方式,实现720P/960P高清视频传输。本项目共分11季,从海思SDK开发环境搭建,到sample程序讲解、SDK中API函数解析,到H.264编解码、RTSP流媒体传输、MP4文件打包,到图像IQ调试、图像识别等视频领域高阶内容,可以说从零基础入手,对图像采集、编解码、网络传输、图像存储和识别做了全方位的详细讲解和代码分析,是目前市面上**一套系统讲解图像和视频技术的视频课程。

    81 人正在学习 去看看 朱有鹏

推荐算法-DeepFM

一、DeepFM出现的原因

  在FM中,采用了一阶和二阶的特征组合,相比与只使用一阶线性组合效果要好很多。但是特征组合的能力还是有限的。即特征之间组合的力度,挖掘特征之间的关联性还是较差的。在图像处理的一些方法中,模型的深度都比较深经过了很多层的非线性变换,主要的目的是为了让模型充分的学习数据的分布以及更加抽象的表示,也就是希望利用高阶的特征。在模型的深层部分得到的特征就是数据的高阶表示,因此DeepFM就是在FM的基础上加上深度模型。让模型进行更多的非线性变换,得到更高阶的特征组合。

二、 FM与深度模型的组合方式

  FM与深度模型的组合有两种,一种是二者并行,另一种是二者串行。DeepFM就是并行的一种结构。并行就是FM将输入部分计算完之后单独拿出来,得到一组特征表示,然后再利用深度模型(多层全连接)对输入部分进行告阶的特征组合。最后把二者的特征进行concact,得到一组特征,最后对这组特征进行分类或者回归。其实这只是特征的一种组合方式,目的就是为了得到特征的高阶表示。
在这里插入图片描述
上图就是DeepFM的结构图,从图中也可以看出模型是比较简单的。这样模型的输出其实就是y=sigmoid(yFM+yDNN)y=sigmoid(y_{FM}+y_{DNN})这里的加号是特征级联的意思。FM部分如同我们上一章的做法,DNN部分是三层全连接。

三、数据处理

  刚刚接触推荐算法,也就是数据挖掘之类的问题。第一次处理感觉还是有些困难的,尤其是特征处理部分。不像对图像这些问题的处理那样方便,但是处理特征是这类工作的重中之重,所以还是要重视起来的。我也参考了大神的文章和代码,大神的文章地址在这里推荐系统遇上深度学习(三)–DeepFM模型理论和实践。我现在还是处于比着葫芦画瓢的阶段,完全是学习别人的东西。
  数据分为训练集和测试集两个csv文件。数据量也比较小,特征有数值类型的也有需要进行one-hot处理的,然后还有一部分特征直接没有处理直接从文件中去除。下面讲一下处理的思路:
1、首先是对于缺失的数据,每一行代表一个用户的数据。会有一些数据存在缺失,全部用-1填充。然后又生成一列新的特征’missing_feat’,这一列就是记录每一个用户缺失值的个数。
df["missing_feat"] = np.sum((df[cols] == -1).values, axis=1)
这个做法很简单,就是先找到每一行等于-1的位置,然后把统计每一行的个数。
在这里插入图片描述
2、接下来考虑对特征进行编码。因为特征有很多类型,有的是数值类型的比如年龄,有很多的取值,这种可以保留原始数值。有的是类别性的,比如性别,这种需要进行one-hot编码。

        self.feat_dict = {}
        tc = 0
        for col in df.columns:
            if col in self.ignore_cols:  # 去掉不需要的列
                continue
            if col in self.numeric_cols:  # 需要的列标记出来,变为数值,存入feat_dict
                self.feat_dict[col] = tc
                tc += 1                   # tc用于编码 此处是当前列全部编为tc
            else:                         # 指定类别的列 当前列的数值集中有几个数值,对这些数值进行编码 
                us = df[col].unique()     # 当前列去重
                # print(us)
                self.feat_dict[col] = dict(zip(us, range(tc, len(us)+tc)))  # us[0]:tc, us[1]:tc+1, us[2]:tc+2 对每一个数值标号
                # print(self.feat_dict[col])
                tc += len(us)             # 更新tc
        self.feat_dim = tc                # 所有数值特征的个数?

上面的做法很好理解,对于一些需要ignore的特征,直接忽略即可。对于数值类型的,直接把这一列特征记为tc,也就是这一列全部编码为tc。对于类别性的特征,首先是对这一列特曾进行去重,然后对每一种取值进行编码。编码的目的是为了我们输入时做Embedding来用的,tc的最大值就是Embedding层的featureSize,假如我们设置Embedding的大小为k。那么embedding部分的权重为featureSize*k的大小,我们在对输入进行编码时,根据每一个特征的取值索引去取对应位置的权重即可。接下来就是生成dfi和dfv两个表格,dfi记录每一个特征的每一个取值的tc编码,dfv对于one-hot类型的特征标为1,数值类型的特征保持原始数据不变。

    def parse(self, infile=None, df=None, has_label=False):
        assert not ((infile is None) and (df is None)), "infile or df at least one is set"
        assert not ((infile is not None) and (df is not None)), "only one can be set"
        if infile is None:
            dfi = df.copy()
        else:
            dfi = pd.read_csv(infile)
        if has_label:  # 针对训练集
            y = dfi['target'].values.tolist()  # 找出label
            dfi.drop(['id', 'target'], axis=1, inplace=True)  # 去掉这些列
        else:          # 针对测试集
            ids = dfi['id'].values.tolist()
            dfi.drop(['id'], axis=1, inplace=True)
        # dfi for feature index
        # dfv for feature value which can be either binary (1/0) or float (e.g., 10.24)
        dfv = dfi.copy()
        for col in dfi.columns:
            if col in self.feat_dict.ignore_cols:  # 不需要的列全部去除
                dfi.drop(col, axis=1, inplace=True)
                dfv.drop(col, axis=1, inplace=True)
                continue
            if col in self.feat_dict.numeric_cols:
                dfi[col] = self.feat_dict.feat_dict[col]  # 需要变为数值的列利用上一步的的tc值替换 tc代表
            else:                                         # 需要变为类别的列利用上一步的特征标号替换 tc, tc+1, tc+2..
                dfi[col] = dfi[col].map(self.feat_dict.feat_dict[col])
                dfv[col] = 1.
        xi = dfi.values.tolist()   # 每一个特征的记号或者索引 全部变为类别索引
        xv = dfv.values.tolist()   # 原始数据 除了需要变为类别的列之外全部为原始数据
        if has_label:
            return xi, xv, y
        else:
            return xi, xv, ids

这个地方其实很好理解,
在这里插入图片描述
xi中就是对特征的编码,xv中是原始的数据,在embedding的过程中,根据xi中的索引去获得对应的权重,然后再让权重与xv中的值对应相乘即可。

三、模型部分

  模型的处理部分,首先就是构建权重,然后就是模型的计算图,最后是各种数据的读取与评价指标等。
1、权重的构建。在上面我们对特征进行了编码,其实就相当于one-hot编码,只是在形式上没有体现出来,而是xi和xv的维度是一致的。如果显性的体现处one-hot,xi的宽度是要更大的。举个例子,数据的col共有filesSize=39个,但是编码之后的特征的编码最大值为featureSize=157,embedding的大小16。我们在embedding层需要构建featureSize*embedding大小的矩阵,然后可以根据每一个特征的索引去获得对应的embedding表示。首先看embedding部分的权重构建:

        weights = dict()
        # embedding
        weights['featureEmbedding'] = tf.Variable(tf.truncated_normal(shape=[self.featureSize, self.embeddingSize], mean=0.0,  
        stddev=0.001), dtype=tf.float32, name='featureEmbedding')  # f * k f=tc
        
        weights['featureBias'] = tf.Variable(tf.truncated_normal(shape=[self.featureSize, 1], mean=0.0, stddev=0.001),
                                              dtype=tf.float32, name='featureBias')  # f

weights[‘featureEmbeddings’] 存放的每一个值其实就是FM中的vik,所以它是F * K的。其中,F代表feture的大小(将离散特征转换成one-hot之后的特征总量),K代表dense vector的大小。
weights[‘feature_bias’]是FM中的一次项的权重。
然后就是DNN部分的权重:

        # deep
        numLayer = len(self.deepLayers)
        inputSize = self.fieldSize * self.embeddingSize  # (k*embedding)
        weights['layer0'] = tf.Variable(tf.truncated_normal(shape=[inputSize, self.deepLayers[0]], mean=0.0,
                                                            stddev=0.001), dtype=tf.float32)  # 全连接输入层
        weights['bias0'] = tf.Variable(tf.random_normal(shape=[1, self.deepLayers[0]], mean=0.0, stddev=0.001),
                                       dtype=tf.float32)

        for i in range(1, numLayer):
            weights['layer{}'.format(i)] = tf.Variable(tf.truncated_normal(shape=[self.deepLayers[i-1], self.deepLayers[i]],
                                                                            mean=0.0, stddev=0.001), dtype=tf.float32)
            weights['bias{}'.format(i)] = tf.Variable(tf.truncated_normal(shape=[1, self.deepLayers[i]], mean=0.0,
                                                                          stddev=0.001), dtype=tf.float32)

        if self.useFM and self.useDeep:
            inputSize = self.fieldSize + self.embeddingSize + self.deepLayers[-1]
        elif self.useFM:
            inputSize = self.fieldSize + self.embeddingSize
        elif self.useDeep:
            inputSize = self.deepLayers[-1]

        weights['concat_projection'] = tf.Variable(tf.truncated_normal(shape=[inputSize, 1], mean=0.0, stddev=0.001), dtype=tf.float32)
        weights['concat_bias'] = tf.Variable(tf.constant(0.01), dtype=np.float32)

        return weights

DNN部分的权重都是全连接部分的,比较简单。在最后将FM部分与DNN的部分的特征进行合并的时候,要注意feature的大小,一次项的长度为fieldSIze,FM部分的长度为embedding的大小,deep部分的长度为人为设置的大小。

**2、计算图的构建。**计算图的构建就是编写整个模型的结构。首先是输入部分,因为我们要输入特征的索引,以及特征的数值,还有label信息,因此需要三个输入。

self.feat_index = tf.placeholder(shape=[None, None], dtype=tf.int32, name='feat_index')
self.feat_value = tf.placeholder(shape=[None, None], dtype=tf.float32, name='feat_value')
self.label = tf.placeholder(shape=[None, 1], dtype=tf.float32, name='label')
# 设置dropout
self.dropout_keep_fm = tf.placeholder(tf.float32, shape=[None], name='dropout_keep_fm')
self.dropout_keep_deep = tf.placeholder(tf.float32, shape=[None], name='dropout_deep_deep')
self.train_phase = tf.placeholder(tf.bool, name='train_phase')

获得embedding的表示:

self.weights = self._initialWeights()
# 获得输入特征的embedding表示  f field_size   k embedding
self.embedding = tf.nn.embedding_lookup(self.weights['featureEmbedding'], self.feat_index)  # batch * f * k
feat_value = tf.reshape(self.feat_value, shape=[-1, self.fieldSize, 1])  # batch * f * 1
self.embedding = tf.multiply(self.embedding, feat_value)  # batch * f * k

lookup就是根据索引获得对应embedding权重。然后将输入数据调整shape,最后将对应的数值与权重部分相乘,也就获得了每一个输入部分的embedding部分的表示。
接下来计算一次项

# 一次项  w1*x1 , w2*x2 , w3*x3.. 长度为f
self.y_first_order = tf.nn.embedding_lookup(self.weights['featureBias'], self.feat_index)  # batch * 1
self.y_first_order = tf.reduce_sum(tf.multiply(self.y_first_order, feat_value), 2)  # (batch*1)x(batch*f*1)
self.y_first_order = tf.nn.dropout(self.y_first_order, self.dropout_keep_fm[0])  # batch * f

一次项的计算就是根据索引值获得每一个value对应的权重w,然后将value与w相乘即可。权重的shape=batch * 1,而feat_value的shape=batch * f * 1,二者相乘是对应位置相乘,得到的shape=batch * f * 1,reduce_sum是按照axis=2进行求和的,其实还是原来的数据,只是维度变成了shape=batch * 1。这样就得到了所有的特征一次项w1x1,w2x2...wnxnw1*x1, w2 * x2...wn * xn

接下来就是二次项的构建:

# sum(vx)^2
self.sum_feature_emb = tf.reduce_sum(self.embedding, 1)   # batch * k
self.sum_feature_emb_square = tf.square(self.sum_feature_emb)

# sum((vx)^2)
self.square_feature_emb = tf.square(self.embedding)      # batch*f*k
self.square_feature_emb_sum = tf.reduce_sum(self.square_feature_emb, 1)  # batch*k

# 二次项
self.y_second_order = 0.5 * tf.subtract(self.sum_feature_emb_square, self.square_feature_emb_sum)  # batch*k
self.y_second_order = tf.nn.dropout(self.y_second_order, self.dropout_keep_fm[1])

在这里插入图片描述
在这里插入图片描述
FM的公式展开如上式所示,分别计算减号左右两边的,然后再做差就可完成。
接下来就是DNN部分的构建,将特征的embedding表示展开成一个全连接向量进行几层全连接的计算就可以。

            # 深度部分
            self.y_deep = tf.reshape(self.embedding, shape=[-1, self.fieldSize * self.embeddingSize])
            self.y_deep = tf.nn.dropout(self.y_deep, self.dropDeep[0])

            for i in range(len(self.deepLayers)):
                print(i)
                self.y_deep = tf.add(tf.matmul(self.y_deep, self.weights['layer{}'.format(i)]), self.weights['bias{}'.format(i)])
                self.y_deep = self.acti(self.y_deep)
                self.y_deep = tf.nn.dropout(self.y_deep, self.dropout_keep_deep[i+1])

            if self.useFM and self.useDeep:
                concat_input = tf.concat([self.y_first_order, self.y_second_order, self.y_deep], axis=1)
            elif self.useFM:
                concat_input = tf.concat([self.y_first_order, self.y_second_order], axis=1)
            elif self.useDeep:
                concat_input = self.y_deep

            self.out = tf.add(tf.matmul(concat_input, self.weights['concat_projection']), self.weights['concat_bias'])

这样整个DeepFM的计算图就构建完成了。

最后就是对模型的训练了。
在重新写(其实是抄)这个代码的过程中,一直有一个疑问,就是我一直一为FM部分得到的是一个数值,而不是一段向量。后来发现是一次项和二次项都是用向量表示的,然后再和deep部分的特征concact,最后进行分类。
其实感觉理解的还不是很透彻,欢迎各位多多指教。

参考

https://www.jianshu.com/p/6f1c2643d31b
https://blog.csdn.net/qq_18293213/article/details/89647215
https://arxiv.org/pdf/1703.04247.pdf

2019-02-06 23:21:00 qq_42505705 阅读数 838
  • 海思平台ISP与图像的IQ调试-第9季

    本课程讲解sensor直接采集的图像都有各种不理想性,因此在编码前都会经过一个软件方式处理,这个处理就叫ISP,图像的IQ调试就是研究这些处理算法和实现的。常见的IQ调试技术如:线性纠正、噪声去除、黑电平校正、坏点去除、颜色插补、Gamma 校正、RGB2YUV 转换、主动白平衡处理、主动曝光控制、AE评估等。 随着通信行业发展,网速越来越快,网络也从文本时代发展越过语音时代到了现在视频时代,“优酷、爱奇艺”、“微视频”、“直播”等的出现也是视频逐步成为主流媒体的证据和表现。航拍、视频监控、刷脸过关检测、汽车ADAS系统等应用也是视频在主流行业中的应用,因此视频的采集、编解码、传输、识别等技术变得越来越有价值,涌现出了“海康威视”、“大华股份”、“深圳英飞拓”等一批明星企业,名扬海内外,动辄市值几百亿。同时在芯片级,国产华为海思HI35xx系列视频编解码方案也逐步取代进口芯片(如TI、安霸)成为主流方案。 视频行业技术含量高、难度大、学习周期长、发展变化快而资料少,因此行业比较缺人、工资较高。如海康威视,普通高校硕士研究生应届进入年薪普遍高于15-20万,在嵌入式linux领域也算高工资技术方向了。 本项目是朱老师及其团队推出的一个嵌入式linux领域重量级企业级实战项目,选用华为海思HI3518E方案,结合OV9712/AR0130 Sensor实现图像采集和H.264编码压缩,并通过ethernet和WIFI,以socket实时流和RTSP等方式,实现720P/960P高清视频传输。本项目共分11季,从海思SDK开发环境搭建,到sample程序讲解、SDK中API函数解析,到H.264编解码、RTSP流媒体传输、MP4文件打包,到图像IQ调试、图像识别等视频领域高阶内容,可以说从零基础入手,对图像采集、编解码、网络传输、图像存储和识别做了全方位的详细讲解和代码分析,是目前市面上**一套系统讲解图像和视频技术的视频课程。

    81 人正在学习 去看看 朱有鹏

本文主要介绍对《数字图像处理》第三章 比特平面分层 的介绍 p70

对于8位256灰度级图像来说,如果它的每个灰度值用二进制表示,选择将这8个数字用8个字节来表示,如32的二进制表示是00100000,将其储存为[0 0 1 0 0 0 0 0],则其二维图像可以理解为一个8层的三维图像,每一层代表一个比特平面

在这里插入图片描述

对比每个比特平面可以发现,高阶平面(如1所在第六比特平面)储存的信息比低阶平面的多。

下图显示了dollar的8比特平面图,依次分别是原图和比特平面8~1
在这里插入图片描述

下面是它的python代码实现(基于opencv)

import cv2
import numpy as np
import matplotlib.pyplot as plt

#更改为需要的图片路径
img = cv2.imread(r'C:\Users\xxx\Desktop\dollar.tif',0)
h,w = img.shape[0],img.shape[1]

#处理过程
new_img = np.zeros((h,w,8))
for i in range(h):
    for j in range(w):
        n = str(np.binary_repr(img[i,j],8))  
        for k in range(8):
            new_img[i,j,k] = n[k]

#依次显示
for i in range(8):
    cv2.imshow('image',new_img[:,:,i])
    cv2.waitKey(0)

第八比特平面
在这里插入图片描述
第七比特平面
在这里插入图片描述
第六比特平面
在这里插入图片描述
第五比特平面
在这里插入图片描述
第四比特平面
在这里插入图片描述
第三比特平面
在这里插入图片描述
第二比特平面
在这里插入图片描述
第一比特平面
在这里插入图片描述
重构是使用第n个平面的像素值乘以常数 2^(n-1)
用128乘以比特平面8,用64乘以比特平面7,然后将这两个平面相加,原图的主要特征便被复原了。

下图是使用几个高阶平面重建的图像
在这里插入图片描述
若要获取更多数字图像处理,python,深度学习,机器学习,计算机视觉等高清PDF以及 更多有意思的 分享,可搜一搜 微信公共号 “分享猿” 免费获取资源。也可扫描下面的二维码关注,期待你的到来~
在这里插入图片描述

没有更多推荐了,返回首页