精华内容
下载资源
问答
  • 图像分割方法总结

    一、灰度阈值法
    1、全局阈值法
    整幅图像使用同一个阈值做分割处理,适用于目标物和背景有明显对比的图像。这种方法是只考虑像素值不考虑空间特征的,例如峰谷法、最大熵分割法。
    如下是一维最大熵分割法实现。
    最大熵原理:只掌握关于未知分布的部分知识时,应选取符合这些知识的但是熵值最大的概率分布。因为熵越大,随机变量的不确定性越大,做准确预测的难度越大。

    int maxEntropy(IplImage *src);
    float calEntropy(CvHistogram *hist,int begin,int end);
    
    int main()
    {
        //Mat img =imread("E:/b.jpg",1);
    
        IplImage *src=cvLoadImage("G:\\4.bmp",CV_LOAD_IMAGE_GRAYSCALE); 
        IplImage *dst = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
        int threshold=maxEntropy(src);
         cvThreshold(src, dst, threshold, 255, CV_THRESH_BINARY); 
         //imshow("一维最大熵分割",img);
         cvNamedWindow("一维最大熵分割");
         cvShowImage("一维最大熵分割", dst);
         cvWaitKey(0);
         return 0;
    }
    
    int maxEntropy(IplImage *src)
    {
        assert(src);
        assert(src->depth == 8);  
        assert(src->nChannels == 1);
    
        int sizes=256;
        float range[2]={0,255};
        float *ranges[1]={&range[0]};
        CvHistogram *hist = cvCreateHist(1, &sizes, CV_HIST_ARRAY, ranges, 1);
        cvCalcHist(&src,hist,0,0);
        cvNormalizeHist(hist,0);
    
        int threthold=0;
        int Entropy_max=0;
        for(int i=0;i<sizes;i++)
        {
            float entropy=calEntropy(hist,0,i)+calEntropy(hist,i,sizes);
            if(entropy>=Entropy_max)
                Entropy_max=entropy;
        }
        return Entropy_max;
    }
    
    float calEntropy(CvHistogram *hist,int begin,int end)
    {
        float total=0;//总概率
        for(int i=begin;i<end;i++)
        {
            total+=cvQueryHistValue_1D(hist,i);
        }
        float entropy=0;
        for(int i=begin;i<end;i++)
        {
            float probability=cvQueryHistValue_1D(hist,i);
            if(probability==0)
                continue;
            probability/=total;
            entropy-=probability*log(probability);
        }
    
        return entropy;
    }

    2、自适应阈值法
    在许多情况下,目标和背景的对比度在图像中的各处是不一样的,实际处理时,需要按照具体情况将图像分成若干子区域分别选择阈值,或者动态地根据一定的邻域范围选择每点处的阈值,进行图像分割。
    OTSU法,它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。
    以前在遥感影像灰度阈值分割上尝试了一些自适应阈值法,影像背景地物比较复杂,效果都不太理想。

    3、人工确定

    二、基于边缘的图像分割
    1、差分法
    图像差分,就是把两幅图像的对应像素值相减,以削弱图像的相似部分,突出显示图像的变化部分。【原理都是共通的,这个也是比较简单的变化检测方法】
    2、梯度法
    Canny\Sobel算子等,他们是里面包含有计算梯度幅值和方向的步骤,也是一种梯度算子吧。
    Canny 步骤:首先消除噪声,一般使用高斯平滑滤波;然后计算梯度幅值和方向;然后非极大值抑制,也就是说选择局部范围内的极大值,抑制局部范围内的非极大值;最后是双阈值算法检测和连接,这一步是为了保留主要边缘,剔除不连续的、次要的边缘。
    这里原理解释主要参考:
    http://blog.csdn.net/likezhaobin/article/details/6892176
    代码根据该博主提供的代码进行了重构
    http://blog.csdn.net/likezhaobin/article/details/6892629

    
    /*****
        int* M ;                       //梯度幅值 
        unsigned char* N;            //非极大值抑制结果
        double* Theta ;             //梯度方向 
    ******/
    void Gaussian(unsigned char* nImageData,unsigned char* pCanny,int nWidth,int nHeight);
    void nonMaximumSuppression(unsigned char *pCanny,unsigned char* N,int* M,double* Theta,int nWidth,int nHeight);//极大值抑制
    void doubleThresholdDetection(unsigned char* N,int* M,int nWidth,int nHeight);//双阈值检测
    void TraceEdge(int y, int x, int nThrLow, unsigned char* pResult, int *pMag, int nWidth,int nHeight);
    void main()
    {
        /******彩色图灰度化********/
        IplImage *colorImage=cvLoadImage("G:\\5.jpg",-1);//读图,获取彩色图指针
    
        int width=colorImage->width;
        int height=colorImage->height;
        int sizeofPixel=width*height;//像素数
        IplImage *opencvGrayImage=cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1);//定义变换后的灰度图指针
        char *gray=(char*)malloc(sizeof(char)*width*height);//存放转换后的灰度像素值
        for(int i=0;i<sizeofPixel;i++)
        {
            uchar b=(uchar)colorImage->imageData[3*i+0];
            uchar g = (uchar)colorImage->imageData[3 * i + 1];  
            uchar r = (uchar)colorImage->imageData[3 * i + 2];  
            gray[i] = r * 0.299 + g * 0.587 + b * 0.114;  
        }
        opencvGrayImage->imageData=gray;
    
        unsigned char* nImageData = new unsigned char[width*height];   //保存图像中的数据 
        for(int j=0; j<height; j++)                                     //获取数据
        {  
            for(int i=0; i<width; i++)  
                nImageData[j*width+i] = (unsigned char)opencvGrayImage->imageData[j*width+i];  
        }  
    
        unsigned char*pCanny = new unsigned char[width*height];        //为平滑后的图像数据分配内存  
        Gaussian(nImageData,pCanny,width,height);//高斯模糊,不太理解
    
        int* M = new int[width*height];                       //梯度幅值 
        unsigned char* N =new unsigned char[width*height];  //非极大值抑制结果
        double* Theta = new double[width*height];             //梯度方向 
        nonMaximumSuppression(pCanny,N,M,Theta,width,height);//非极大值抑制结果
        doubleThresholdDetection(N,M,width,height);//双阈值检测
        opencvGrayImage->imageData=(char*)N;
        cvNamedWindow("GrayImage",CV_WINDOW_AUTOSIZE);  
        cvShowImage("GrayImage",opencvGrayImage);               //显示灰度图  
        cvWaitKey(0);  
        cvDestroyWindow("GrayImage");
    }
    
    void Gaussian(unsigned char* nImageData,unsigned char* pCanny,int nWidth,int nHeight)
    {
        /****高斯滤波****/
        double nSigma = 0.4;                            //定义高斯函数的标准差  
        int nWidowSize = 1+2*ceil(3*nSigma);            //定义滤波窗口的大小  
        int nCenter = (nWidowSize)/2;                   //定义滤波窗口中心的索引
    
    
        double* nData = new double[nWidth*nHeight];                      //两次平滑的中间数据  
    
        //////////////////////生成一维高斯滤波系数//////////////////////////////////    
        double* pdKernal_2 = new double[nWidowSize*nWidowSize]; //定义一维高斯核数组  
        double  dSum_2 = 0.0;                                   //求和,进行归一化        
        ///////////////////////二维高斯函数公式////////////////////////////////////      
        ////                         x*x+y*y                        ///////////////  
        ////                   -1*--------------                ///////////////  
        ////         1             2*Sigma*Sigma                ///////////////  
        ////   ---------------- e                                   ///////////////  
        ////   2*pi*Sigma*Sigma                                     ///////////////  
        ///////////////////////////////////////////////////////////////////////////  
        for(int i=0; i<nWidowSize; i++)  
        {  
            for(int j=0; j<nWidowSize; j++)  
            {  
                int nDis_x = i-nCenter;  
                int nDis_y = j-nCenter;  
                pdKernal_2[i+j*nWidowSize]=exp(-(1/2)*(nDis_x*nDis_x+nDis_y*nDis_y)  
                    /(nSigma*nSigma))/(2*3.1415926*nSigma*nSigma);  
                dSum_2 += pdKernal_2[i+j*nWidowSize];  
            }  
        }  
        for(int i=0; i<nWidowSize; i++)  
        {  
            for(int j=0; j<nWidowSize; j++)                 //进行归一化  
            {  
                pdKernal_2[i+j*nWidowSize] /= dSum_2;  
            }  
        }  
        int x;  
        int y;  
        for(int i=0; i<nHeight; i++)  
        {  
            for(int j=0; j<nWidth; j++)  
            {  
                double dFilter=0.0;  
                double dSum = 0.0;  
                for(x=(-nCenter); x<=nCenter; x++)                     //行  
                {  
                    for(y=(-nCenter); y<=nCenter; y++)             //列  
                    {  
                        if( (j+x)>=0 && (j+x)<nWidth && (i+y)>=0 && (i+y)<nHeight) //判断边缘  
                        {  
                            dFilter += (double)nImageData [(i+y)*nWidth + (j+x)]  
                            * pdKernal_2[(y+nCenter)*nWidowSize+(x+nCenter)];  
                            dSum += pdKernal_2[(y+nCenter)*nWidowSize+(x+nCenter)];  
                        }  
                    }  
                }  
                pCanny[i*nWidth+j] = (unsigned char)dFilter/dSum;  
            }  
        }
    }
    
    void nonMaximumSuppression(unsigned char *pCanny,unsigned char* N,int* M,double* Theta,int nWidth,int nHeight)
    {
        //////////////////同样可以用不同的检测器/////////////////////////  
        /////    P[i,j]=(S[i,j+1]-S[i,j]+S[i+1,j+1]-S[i+1,j])/2     /////  
        /////    Q[i,j]=(S[i,j]-S[i+1,j]+S[i,j+1]-S[i+1,j+1])/2     /////  
        /////////////////////////////////////////////////////////////////  
        double* P = new double[nWidth*nHeight];                 //x向偏导数  
        double* Q = new double[nWidth*nHeight];                 //y向偏导数  
    
    
        //计算x,y方向的偏导数  
        for(int i=0; i<(nHeight-1); i++)  
        {  
            for(int j=0; j<(nWidth-1); j++)  
            {  
                P[i*nWidth+j] = (double)(pCanny[i*nWidth + min(j+1, nWidth-1)] - pCanny[i*nWidth+j] + pCanny[min(i+1, nHeight-1)*nWidth+min(j+1, nWidth-1)] - pCanny[min(i+1, nHeight-1)*nWidth+j])/2;  
                Q[i*nWidth+j] = (double)(pCanny[i*nWidth+j] - pCanny[min(i+1, nHeight-1)*nWidth+j] + pCanny[i*nWidth+min(j+1, nWidth-1)] - pCanny[min(i+1, nHeight-1)*nWidth+min(j+1, nWidth-1)])/2;  
            }  
        }  
        //计算梯度幅值和梯度的方向  
        for(int i=0; i<nHeight; i++)  
        {  
            for(int j=0; j<nWidth; j++)  
            {  
                M[i*nWidth+j] = (int)(sqrt(P[i*nWidth+j]*P[i*nWidth+j] + Q[i*nWidth+j]*Q[i*nWidth+j])+0.5);  
                Theta[i*nWidth+j] = atan2(Q[i*nWidth+j], P[i*nWidth+j]) * 57.3;  
                if(Theta[i*nWidth+j] < 0)  
                    Theta[i*nWidth+j] += 360;              //将这个角度转换到0~360范围  
            }  
        }  
    
        //非极大值抑制
    
        int g1=0, g2=0, g3=0, g4=0;                            //用于进行插值,得到亚像素点坐标值  
        double dTmp1=0.0, dTmp2=0.0;                           //保存两个亚像素点插值得到的灰度数据  
        double dWeight=0.0;                                    //插值的权重
    
        //对边界进行初始化
        for(int i=0; i<nWidth; i++)  
        {  
            N[i] = 0;  
            N[(nHeight-1)*nWidth+i] = 0;  
        }  
        for(int j=0; j<nHeight; j++)  
        {  
            N[j*nWidth] = 0;  
            N[j*nWidth+(nWidth-1)] = 0;  
        }  
        //局部最大值查找
        for(int i=1; i<(nWidth-1); i++)  
        {  
            for(int j=1; j<(nHeight-1); j++)  
            {  
                int nPointIdx = i+j*nWidth;       //当前点在图像数组中的索引值  
                if(M[nPointIdx] == 0)  
                    N[nPointIdx] = 0;         //如果当前梯度幅值为0,则不是局部最大对该点赋为0  
                else  
                {  
                    ////////首先判断属于那种情况,然后根据情况插值///////  
                    ////////////////////第一种情况///////////////////////  
                    /////////       g1  g2                  /////////////  
                    /////////           C                   /////////////  
                    /////////           g3  g4              /////////////  
                    /////////////////////////////////////////////////////  
                    if( ((Theta[nPointIdx]>=90)&&(Theta[nPointIdx]<135)) ||   
                        ((Theta[nPointIdx]>=270)&&(Theta[nPointIdx]<315)))  
                    {  
                        //////根据斜率和四个中间值进行插值求解  
                        g1 = M[nPointIdx-nWidth-1];  
                        g2 = M[nPointIdx-nWidth];  
                        g3 = M[nPointIdx+nWidth];  
                        g4 = M[nPointIdx+nWidth+1];  
                        dWeight = fabs(P[nPointIdx])/fabs(Q[nPointIdx]);   //反正切  
                        dTmp1 = g1*dWeight+g2*(1-dWeight);  
                        dTmp2 = g4*dWeight+g3*(1-dWeight);  
                    }  
                    ////////////////////第二种情况///////////////////////  
                    /////////       g1                      /////////////  
                    /////////       g2  C   g3              /////////////  
                    /////////               g4              /////////////  
                    /////////////////////////////////////////////////////  
                    else if( ((Theta[nPointIdx]>=135)&&(Theta[nPointIdx]<180)) ||   
                        ((Theta[nPointIdx]>=315)&&(Theta[nPointIdx]<360)))  
                    {  
                        g1 = M[nPointIdx-nWidth-1];  
                        g2 = M[nPointIdx-1];  
                        g3 = M[nPointIdx+1];  
                        g4 = M[nPointIdx+nWidth+1];  
                        dWeight = fabs(Q[nPointIdx])/fabs(P[nPointIdx]);   //正切  
                        dTmp1 = g2*dWeight+g1*(1-dWeight);  
                        dTmp2 = g4*dWeight+g3*(1-dWeight);  
                    }  
                    ////////////////////第三种情况///////////////////////  
                    /////////           g1  g2              /////////////  
                    /////////           C                   /////////////  
                    /////////       g4  g3                  /////////////  
                    /////////////////////////////////////////////////////  
                    else if( ((Theta[nPointIdx]>=45)&&(Theta[nPointIdx]<90)) ||   
                        ((Theta[nPointIdx]>=225)&&(Theta[nPointIdx]<270)))  
                    {  
                        g1 = M[nPointIdx-nWidth];  
                        g2 = M[nPointIdx-nWidth+1];  
                        g3 = M[nPointIdx+nWidth];  
                        g4 = M[nPointIdx+nWidth-1];  
                        dWeight = fabs(P[nPointIdx])/fabs(Q[nPointIdx]);   //反正切  
                        dTmp1 = g2*dWeight+g1*(1-dWeight);  
                        dTmp2 = g3*dWeight+g4*(1-dWeight);  
                    }  
                    ////////////////////第四种情况///////////////////////  
                    /////////               g1              /////////////  
                    /////////       g4  C   g2              /////////////  
                    /////////       g3                      /////////////  
                    /////////////////////////////////////////////////////  
                    else if( ((Theta[nPointIdx]>=0)&&(Theta[nPointIdx]<45)) ||   
                        ((Theta[nPointIdx]>=180)&&(Theta[nPointIdx]<225)))  
                    {  
                        g1 = M[nPointIdx-nWidth+1];  
                        g2 = M[nPointIdx+1];  
                        g3 = M[nPointIdx+nWidth-1];  
                        g4 = M[nPointIdx-1];  
                        dWeight = fabs(Q[nPointIdx])/fabs(P[nPointIdx]);   //正切  
                        dTmp1 = g1*dWeight+g2*(1-dWeight);  
                        dTmp2 = g3*dWeight+g4*(1-dWeight);  
                    }  
                }         
                //////////进行局部最大值判断,并写入检测结果////////////////  
                if((M[nPointIdx]>=dTmp1) && (M[nPointIdx]>=dTmp2))  
                    N[nPointIdx] = 128;  
                else  
                    N[nPointIdx] = 0;  
            }  
        }  
    }
    
    void doubleThresholdDetection(unsigned char* N,int* M,int nWidth,int nHeight)
    {
        //双阈值检测,最大的梯度幅值为360
        int nHist[1024];   
        int nEdgeNum;             //可能边界数  
        int nMaxMag = 0;          //最大梯度数  
        int nHighCount; 
        //统计直方图
        for(int i=0;i<1024;i++)  
            nHist[i] = 0;  
        for(int i=0; i<nHeight; i++)  
        {  
            for(int j=0; j<nWidth; j++)  
            {  
                if(N[i*nWidth+j]==128)  
                    nHist[M[i*nWidth+j]]++;  
            }  
        }  
        //获取最大幅值及潜在边缘个数
        nEdgeNum = nHist[0];  
        nMaxMag = 0;                    //获取最大的梯度值        
        for(int i=1; i<1024; i++)           //统计经过“非最大值抑制”后有多少像素  
        {  
            if(nHist[i] != 0)       //梯度为0的点是不可能为边界点的  
            {  
                nMaxMag = i;  
            }     
            nEdgeNum += nHist[i];   //经过non-maximum suppression后有多少像素  
        }  
        //这段代码的意思是,按照灰度值从低到高的顺序,选取前79%个灰度值中的最大的灰度值为高阈值,低阈值大约为高阈值的一半,这是根据经验数据的来的.
        double  dRatHigh = 0.79;  
        double  dThrHigh;  
        double  dThrLow;  
        double  dRatLow = 0.5;  
        nHighCount = (int)(dRatHigh * nEdgeNum + 0.5);  
        int j=1;  
        nEdgeNum = nHist[1];  
        while((j<(nMaxMag-1)) && (nEdgeNum < nHighCount))  
        {  
            j++;  
            nEdgeNum += nHist[j];  
        }  
        dThrHigh = j;                                   //高阈值  
        dThrLow = (int)((dThrHigh) * dRatLow + 0.5);    //低阈值 
        for(int i=0; i<nHeight; i++)  
        {  
            for(int j=0; j<nWidth; j++)  
            {  
                if((N[i*nWidth+j]==128) && (M[i*nWidth+j] >= dThrHigh))  
                {  
                    N[i*nWidth+j] = 255;  
                    TraceEdge(i, j, dThrLow, N, M, nWidth,nHeight);  
                }  
            }  
        }
    }
    
    void TraceEdge(int y, int x, int nThrLow, unsigned char* pResult, int *pMag, int nWidth,int nHeight)  
    {  
        //对8邻域像素进行查询  
        int xNum[8] = {1,1,0,-1,-1,-1,0,1};  
        int yNum[8] = {0,1,1,1,0,-1,-1,-1};  
            long yy,xx,k;  
        for(k=0;k<8;k++)  
        {  
            yy = y+yNum[k];  
            xx = x+xNum[k];  
            if(pResult[yy*nWidth+xx]==128 && pMag[yy*nWidth+xx]>=nThrLow )  
            {  
                //该点设为边界点  
                pResult[yy*nWidth+xx] = 255;  
                //以该点为中心再进行跟踪  
                TraceEdge(yy,xx,nThrLow,pResult,pMag,nWidth,nHeight);  
            }  
        }  
    }  
    

    3、高通滤波
    图像的边缘、细节主要是集中在高频部分,为了突出边缘,则采用高通滤波器让高频通过,而削弱低频部分,对条带状噪声的影像做去噪工作也是有奇效,但是,高通滤波容易产生振铃现象或者伪现象。
    常用滤波器有二维理想高通滤波器、巴特沃斯高通滤波器、指数高通滤波器、梯形高通滤波器。

    步骤:
    1、首先对一副图像进行如下二维傅里叶变换。
    这里写图片描述
    然后F[0,0]是直流分量
    这里写图片描述
    直流分量表示图像较为平滑没有太大变化的部分,其他为交流分量,所以理想的情况应该是保持直流分量而对其他部分进行增幅,但是滤波器是会衰减直流分量的,所以会产生振铃效应,导致图像盲恢复会产生图像信息的损失。
    2、然后选择高通滤波器,如
    二维理想高通滤波器:
    这里写图片描述
    D0是滤波器的阻带半径,而D(u,v)是点到滤波器中央的距离。理想高通的滤波器的振幅特性如下所示
    这里写图片描述
    巴特沃斯高通滤波器:
    这里写图片描述
    巴特沃斯高通滤波器也可以通过改变次数n,对过度特性进行调整。过大的n会造成振铃现象。
    这里写图片描述
    高斯高通滤波器
    这里写图片描述
    高斯滤波器的过度特性很好,所以不会发生振铃现象。
    这里写图片描述

    参考资料:
    https://www.cnblogs.com/fuhaots2009/p/3465149.html
    4、模板匹配
    模板匹配与图像分割交集比较少,目前看的基本是基于形状模型的图像分割,模板匹配对其有一定帮助,局限性挺大的。我同学的毕设老师说模板匹配太低级了,不给用哈哈哈哈
    模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,这就是一个匹配问题。它是图像处理中最基本、最常用的匹配方法。模板匹配具有自身的局限性,主要表现在它只能进行平行移动,若原图像中的匹配目标发生旋转或大小变化,该算法无效。
    在 OpenCV 中,提供了相应的函数完成这个操作。
    matchTemplate 函数:在模板和输入图像之间寻找匹配,获得匹配结果图像
    minMaxLoc 函数:在给定的矩阵中寻找最大和最小值,并给出它们的位置
    其中OpenCV 提供了 6 种计算两幅图像相似度的方法。
    差值平方和匹配 CV_TM_SQDIFF
    标准化差值平方和匹配 CV_TM_SQDIFF_NORMED
    相关匹配 CV_TM_CCORR
    标准相关匹配 CV_TM_CCORR_NORMED
    相关匹配 CV_TM_CCOEFF
    标准相关匹配 CV_TM_CCOEFF_NORMED
    具体方法公式可以参考以下链接。

    参考资料http://blog.csdn.net/zhi_neng_zhi_fu/article/details/51029864
    http://blog.csdn.net/liyuanbhu/article/details/49837661

    三、基于区域的图像分割
    1、区域生长法
    首先对每个需要分割的区域找出一个种子像素作为生长的七点,然后将种子像素周围邻域中与种子有相同或相似性质的像素(根据事先确定的生长或相似准则来确定)合并到种子像素所在的区域中。而新的像素继续作为种子向四周生长,直到再没有满足条件的像素可以包括进来,一个区域就生长而成了。
    这个方法有两个关键:一个是种子点怎么选,一个是阈值的限定。原理代码以前po过。
    http://blog.csdn.net/weixin_37051000/article/details/60142281
    2、分水岭算法
    这个怕是一时半会讲不清,地学领域很爱用。
    这里又发展出了多种面向对象的分割方法

    四、分割后处理
    1、数学形态学:膨胀、腐蚀、开运算、闭运算
    2、图像平滑、锐化

    展开全文
  • 数字图像处理----图像旋转

    千次阅读 2019-04-14 07:06:06
    注:旋转中心很重要 坐标系为像素,按照像素位置(第(m,n)做为坐标) 以坐标原点为中心旋转的原理: 点p0绕坐标原点逆时针方向旋转θ角度得到点p1. 以任意图形中心点为坐标原点旋转原理: 从上图可知以任意...

    注:旋转中心很重要 坐标系为像素,按照像素位置(第(m,n)做为坐标)

    以坐标原点为中心旋转的原理:

    点p0绕坐标原点逆时针方向旋转θ角度得到点p1.

    以任意图形中心点为坐标原点旋转原理:

    从上图可知以任意图形中心点为坐标原点旋转我们需要三步:
    (1)将坐标系Ⅰ变成坐标系Ⅱ
    (2)在坐标系Ⅱ中旋转θ角
    (3)将坐标系Ⅱ变成坐标系Ⅰ

    (1)将坐标系Ⅰ变成坐标系Ⅱ
    由Figure1得到Figure2可知,变换矩阵为:

    (2)在坐标系Ⅱ中旋转θ角
    见上面以坐标原点为中心旋转的原理
    (3)将坐标系Ⅱ变成坐标系Ⅰ

    最近邻插值matlab代码

    function [ A ] = myimrotate(B,degree,method)                                 %定义旋转函数,degree为要旋转的角度
    [r,c,d]=size(B);                                                      %获取输入图像B的行r、列c和通道数d,为了旋转彩色图像所以有必要得到通道数d
    nH=round(r*abs(cosd(degree))+c*abs(sind(degree)));                    %旋转图像后得到的新高度,“round()函数四舍五入“
    nW=round(c*abs(cosd(degree))+r*abs(sind(degree)));                    %旋转图像后得到的新宽度
    A=zeros(nH,nW,d);                                                     %定义生成目标图像的行列以及通道数
    M1=[1 0 0;
        0 -1 0;
        -0.5*nW 0.5*nH 1 ];                                  %坐标系变换矩阵M1
    M2=[cosd(degree) -sind(degree) 0;
        sind(degree) cosd(degree) 0;
        0 0 1];  %角度旋转变换矩阵M2,我用的是顺时针方向
    M3=[1 0 0;
        0 -1 0;
        0.5*c 0.5*r 1];                                      %坐标系变换矩阵M3
        for i=1:nW
            for j=1:nH
                temp=[i j 1]*M1*M2*M3;                                    %得到旋转后的矩阵temp
                y_r=temp(1,2);                                              %y取矩阵temp的第一行第二列,y对应j,为高度
                x_r=temp(1,1);                                              %x取矩阵temp的第一行第一列,x对应i,为宽度
                y=round(y_r);                                               %y四舍五入取整
                x=round(x_r);                                               %x四舍五入取整
               if(x>=1&&x<=c)&&(y>=1&&y<=r)                               %判断的得到的(x,y)点是否在原图像上
                    %最近邻插值
                   if strcmp(method,'near')
                      A(j,i,:)=B(y,x,:);                                     %将原图像的像素点赋值给对应的旋转后图像上的点
                   end                                                       %(”有人疑惑为啥不是A(i,j,:)=B(x,y,:);因为i,x对应的是列,即宽,而j,y对应的是行,即高“),我这里以x为横坐标,y为竖向纵坐标
                   %线性插值
                   if strcmp(method,'linear')       
                      b = x_r-x;
                      a = y+1-y_r;
                      if(x>=1&&x+1<=c)&&(y>=1&&y+1<=r)
                          
                        
                          P1 = b*B(y, x) + (1-b)*B(y+1, x);
                          P2 = b*B(y, x+1) + (1-b)*B(y+1, x+1);
                          P = a*P1 + (1-a)*P2;
                      else
                          P=B(y,x,:);
                      end
                      A(j,i,:)=P;
                   end  
                   %三次插值
                   if strcmp(method,'trib')    
                       x = floor(x_r);
                       y = floor(y_r);
                                
                       if(x>1&&x+2<=c)&&(y>1&&y+2<=r)     
                           b = x_r-x;
                           a = y_r-y;
                           A_A=[s(1+a) s(a) s(1-a) s(2-a)]; 
                           C=[s(1+b);s(b);s(1-b);s(2-b)]; 
                           B_B=[B(y-1,x-1) B(y-1,x) B(y-1,x+1) B(y-1,x+2)  
                              B(y,x-1)   B(y,x)  B(y,x+1)   B(y,x+2)  
                              B(y+1,x-1)   B(y+1,x) B(y+1,x+1) B(y+1,x+2)  
                              B(y+2,x-1) B(y+2,x) B(y+2,x+1) B(y+2,x+2)];
                          A(j,i,:) = (double(A_A)*double(B_B)*double(C));
    %                    else
    %                        if x<1
    %                            x = x+1;
    %                        end
    %                        if y<1
    %                            y = y+1;
    %                        end
    %                        A(j,i,:)=B(y,x,:);
                       end
                   end
               end   
            end
        end
    end
    %计算S值
    function [ re ] = s(x) 
        x = abs(x);
        if x<1 && x>=0
            re = 1-2*x^2 + x^3;
        end
        if x>=1 &&x<2
            re = 4 - 8*x +5*x^2-x^3;
        end
        if x>=2
            re = 0;
        end
    end
    复制代码
    展开全文
  • 快速图像旋转算法的c++实现

    千次阅读 2018-01-10 18:44:56
    在数字图像处理技术中,图像旋转算法是最基本的操作之一。本文实现一种快速的图像旋转算法,并和原始方法以及opencv提供的旋转方案进行速度上的比较。 1 基本原理 图像旋转有两种计算坐标的思路,分别是前向映射和...

    0 引言

    在数字图像处理技术中,图像旋转算法是最基本的操作之一。本文实现一种快速的图像旋转算法,并和原始方法以及opencv提供的旋转方案进行速度上的比较。

    1 基本原理

    图像旋转有两种计算坐标的思路,分别是前向映射和反向映射。其中后向映射在插值运算时较为方便,前向映射时计算插值则需要先知道全局的映射关系。如下图所示,反向映射是从旋转后图像出发,寻找其在原图中的位置,如果该位置不是整数坐标的话,则利用周围像素值进行插值运算。同理前向映射也是类似,但是差别是,前向映射四周的像素灰度值在当前并不是已知的,所以要算完整图的权重后再进行归一化。

    ![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/381fad556ab6837aa53ec7e30b72ccd0.png)

    正向和反向映射的公式见:http://blog.csdn.net/lkj345/article/details/50555870 在网上所有的公式中,这个是比较靠谱的,考虑了笛卡尔坐标系和图像坐标系的差别。

    2 优化方法

    由上面的原理可以得到朴素的求解方法,以反向映射为例,计算旋转后图像中每个整数坐标在原图中的精确坐标。从上面给出的公式可以知道每个坐标点的计算有若干次浮点乘法和加法。有一个想法是,既然知道旋转角度,知道每行第一个元素的坐标,即可用图形学中直线绘制的算法计算出这一行中剩下点的坐标。例如bresenham直线算法、DDA直线算法等。

    这样可以避免多次乘法运算,只计算每行第一个像素的精确坐标,剩下的坐标使用浮点加法计算(DDA)即可。更进一步的,只要知道原图第一行的精确坐标,剩下每一行的第一个坐标也可用此思想计算。这样整个图只有第一个像素点需要使用浮点乘法计算精确坐标,剩下的像素坐标可以通过浮点加法计算出来。
    该思想具体内容可参考文献[1]。但是个人觉得其中伪代码的描述不算是bresenham算法,bresenham算法中利用一个递推的表达式完全去掉了浮点运算(包括乘法和加法),倒更像是DDA算法的思想。

    3 代码清单

    这里给出cpp版本的图像旋转实现

    代码1 最近邻插值和双线性插值的实现

    /*
    % INTERPOLATE 插值方法
    % f 原图 sz图像大小
    % m 原图y整数坐标
    % n 原图x整数坐标
    % ex x和亚像素坐标误差
    % ey x和亚像素坐标误差
    % way 1为最近邻插值,2为双线性插值方法
    */
    inline BYTE Interpolate(BYTE f[], int sz[], int m, int n, float ex, float ey, char way){
    	BYTE gray=0;
    	float fr1, fr2, fr3;
    	//1. 误差统一到0到1之间
    	if (ex < 0){
    		ex = 1 + ex;
    		n--;
    	}
    	if (ey < 0){
    		ey = 1 + ey;
    		m--;
    	}
    	if (m <= 0 || n <= 0)
    		return gray;
    
    	//2. 最邻近差值
    	if (way == 1){
    		if (ex > 0.5)
    			n++;
    		if (ey > 0.5)
    			m++;
    		if (m > sz[0] || n > sz[1])
    			return gray;
    		gray = f[sz[1]*m+n];
    		return gray;
    	}
    
    	// 3.双线性插值
    	if (((m + 1) > sz[0]) || ((n + 1) > sz[1]))
    		return gray;
    	if (way == 2){
    		fr1 = (1 - ex)*float(f[sz[1] * m + n]) + ex*float(f[sz[1] * m + n + 1]);
    		fr2 = (1 - ex)*float(f[sz[1] * (m + 1) + n]) + ex*float(f[sz[1] * (m+1) + n + 1]);
    		fr3 = (1 - ey)*fr1 + ey*fr2;
    		gray = BYTE(fr3);
    	}
    	return gray;
    }
    

    代码2 原始的反向映射算法

    BYTE* normalRoate(BYTE img[],int w,int h,double theta,int *neww,int *newh){
    	float fsin, fcos,c1,c2,fx,fy,ex,ey;
    	int w1, h1,xx,yy;
    	int sz[2] = {h,w};
    	//1. 计算基本参数
    	fsin = sin(theta);
    	fcos = cos(theta);
    	*newh=h1 = ceilf(abs(h*fcos) + abs(w*fsin));
    	*neww=w1 = ceilf(abs(w*fcos) + abs(h*fsin));
    	auto I1 = new(std::nothrow) BYTE[w1*h1];
    	if (!I1)
    		return NULL;
    	memset(I1, 0, w1*h1);
    	c1 = (w - w1*fcos - h1*fsin) / 2;
    	c2 = (h + w1*fsin - h1*fcos) / 2;
    	//2. 计算反向坐标并计算插值
    	for (int y = 0; y < h1; y++){
    		for (int x = 0; x < w1; x++){
    			//计算后向映射点的精确位置 每个点都使用原始公式计算
    			fx = x*fcos + y*fsin + c1; //四次浮点乘法和四次浮点加法
    			fy = y*fcos - x*fsin + c2;
    			xx = roundf(fx);
    			yy = roundF(fy);
    			ex = fx - float(xx);
    			ey = fy - float(yy);
    			I1[w1*y+x] = Interpolate(img,sz,yy, xx, ex, ey, 2);//双线性插值
    		}
    	}
    	return I1;
    }
    

    代码3 基于直线算法的图像旋转

    BYTE* DDARoateFast(BYTE img[], int w, int h, double theta, int *neww, int *newh){
    	float fsin, fcos, c1, c2, fx, fy, ex, ey;
    	int w1, h1, xx, yy;
    	int sz[2] = { h, w };
    	//1. 计算旋转参数
    	fsin = sin(theta);
    	fcos = cos(theta);
    	*newh = h1 = ceilf(abs(h*fcos) + abs(w*fsin));
    	*neww = w1 = ceilf(abs(w*fcos) + abs(h*fsin));
    	auto I1 = new(std::nothrow) BYTE[w1*h1];
    	if (!I1)
    		return NULL;
    	memset(I1, 0, w1*h1);
    	c1 = (w - w1*fcos - h1*fsin) / 2; //见文献[1]的公式
    	c2 = (h + w1*fsin - h1*fcos) / 2;
    	fx = c1-fsin;
    	fy = c2-fcos;
    	//2. 计算反向坐标并计算插值
    	for (int y = 0; y < h1; y++){//整个二层循环中计算坐标点时都没有浮点乘法运算
    		//计算第一个后向映射点的精确位置
    		fx = fx + fsin;
    		fy = fy + fcos;
    		// 计算第一个点对应的栅格位置
    		xx = roundf(fx);
    		yy = roundF(fy);
    		ex = fx - float(xx);//误差
    		ey = fy - float(yy);
    		for (int x = 0; x < w1; x++){
    			I1[w1*y + x] = InterpolateFast(img, sz, yy, xx, ex, ey, 2);
    			ex = ex + fcos;
    			ey = ey - fsin;
    			if (ex>0.5){
    				xx++;
    				ex = ex - 1;
    			}
    			if (ey < -0.5){
    				yy--;
    				ey = ey + 1;
    			}
    		}
    	}
    	return I1;
    }
    

    代码4 前向映射的opencv实现

    void FastRotateImage(Mat &srcImg, Mat &roateImg, float degree){
    	assert((srcImg.cols > 0) && (srcImg.rows > 0));
    	float fsin, fcos, c1, c2, fx, fy, xx, yy;
    	int w1, h1,w,h;
    	w = srcImg.cols;
    	h = srcImg.rows;
    	int sz[2] = { srcImg.rows, srcImg.cols };
    	Mat map1_x, map2_y,m1,m2,m3,sPoint,newMap; //m1 m2 m3 为前文推荐博客中的基本矩阵
    	//1. 计算旋转参数
    	fsin = sin(degree);
    	fcos = cos(degree);
    	h1 = ceilf(abs(h*fcos) + abs(w*fsin));
    	w1 = ceilf(abs(w*fcos) + abs(h*fsin));
    	roateImg.create(h1, w1, CV_8UC1); //srcImg.type
    	map1_x.create(srcImg.size(), CV_32FC1);
    	map2_y.create(srcImg.size(), CV_32FC1);
    	c1 = (w - w1*fcos - h1*fsin) / 2;
    	c2 = (h + w1*fsin - h1*fcos) / 2;
    	//2. 计算前向的第一个点坐标
    	m1=Mat::eye(3, 3, CV_32FC1);
    	m1.at<float>(2, 0) =-w / 2;
    	m1.at<float>(2, 1) =h / 2;
    	m1.at<float>(1, 1) = -1;
    	m2=Mat::eye(3, 3, CV_32FC1);
    	m2.at<float>(0, 0) = fcos;
    	m2.at<float>(0, 1) = -fsin;
    	m2.at<float>(1, 0) = fsin;
    	m2.at<float>(1, 1) = fcos;
    	m3=Mat::eye(3, 3, CV_32FC1);
    	m3.at<float>(2, 0) =w1 / 2;
    	m3.at<float>(2, 1) =h1 / 2;
    	m3.at<float>(1, 1) = -1;
    	sPoint=Mat::zeros(1,3,CV_32FC1);
    	sPoint.at<float>(0, 2) = 1;
    	Mat snPoint = sPoint*m3*m2*m1;
    	//cout << snPoint << endl;
    	fx =snPoint.at<float>(0,0) -fsin;
    	fy = snPoint.at<float>(0, 1) - fcos;
    	//3. 用直线画法计算剩余其他的映射点坐标
    	for (int y = 0; y < h; y++){
    		//计算第一个前向映射点的精确位置
    		fx = fx + fsin;
    		fy = fy + fcos; 
    		xx = fx-fcos;
    		yy = fy+fsin;
    		float *ptrx = map1_x.ptr<float>(y);
    		float *ptry = map2_y.ptr<float>(y);
    		for (int x = 0; x < w; x++){
    			xx = xx + fcos;
    			yy = yy - fsin;
    			*(ptrx++) = xx;
    		    *(ptry++) = yy;
    		}
    	}
    	//3.利用opencv的重映射函数完成前向映射的插值运算
    	remap(srcImg, roateImg, map1_x, map2_y, INTER_CUBIC, BORDER_CONSTANT, Scalar(0, 0, 0));
    }
    

    代码5 opencv的旋转实现

    //逆时针旋转图像degree角度(原尺寸)  
    void rotateImage(Mat &img, Mat &img_rotate, int degree)
    {
    	//旋转中心为图像中心  
    	CvPoint2D32f center;
    	center.x = float(img.cols / 2.0 + 0.5);
    	center.y = float(img.rows / 2.0 + 0.5);
    	//计算二维旋转的仿射变换矩阵  
    	Mat M(2, 3, CV_32FC1);
    	M = getRotationMatrix2D(center, degree, 1);
    	clock_t t1 = clock();
    	//变换图像,并用黑色填充其余值  
    	warpAffine(img, img_rotate, M, img.size(), INTER_CUBIC);
    	clock_t t2 = clock();
    	cout << (t2 - t1)*1.0 / CLOCKS_PER_SEC/10;
    }
    

    4 效率对比

    在debug模式下,512*512的灰度图效率如下

    代码编号平均时间
    516ms
    419ms
    319ms
    241ms

    在release模式下,代码3和代码4大概是代码2的四倍。同时代码5的速度要更优于代码3。注意到后向映射更便于实现,且对旋转图像放大的操作也更自然。代码5和4中旋转后的图像和原图尺寸一致,若要获取完整的图像需要在原图四周加补充像素。总结,还是opencv的自带的仿射变换做旋转速度快,但是你若受限于条件不能使用opencv库,可以考虑这种快速旋转方法,比朴素方法在各个图像尺度上(512512,10241024,2048*2048)都快四倍。

    ![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/618c8ddecfd028db0c96b3e1a0e00e49.png) 代码3结果图
    ![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/9cebeda0b44f92eebfa581bb8b19ed21.png) 代码2结果图

    参考文献

    [1]:石慎, 张艳宁, 郗润平,等. 基于Bresenham画线算法的图像快速-高精度旋转算法[J]. 计算机辅助设计与图形学学报, 2007, 19(11):1387-1392.

    展开全文
  • 【20200415】数字图像处理DIP课程课业...2、图像旋转方法3之反变换法伪代码 3、图像旋转方法3之反变换法代码实现 叮嘟!这里是小啊呜的学习课程资料整理。好记性不如烂笔头,今天也是努力进步的一天。一起加油进阶吧!


    叮嘟!这里是小啊呜的学习课程资料整理。好记性不如烂笔头,今天也是努力进步的一天。一起加油进阶吧!
    在这里插入图片描述

    一、DIP课程课业打卡六

    1、阅读下面代码并回答相应问题:

    function [im]=rot3_inv(I,delta_ang)  
    [M,N,K] = size(I);
    a=delta_ang*pi/180;   %%----------(a行)
    
     
    x1=1; x2=M;x3=M;x4=1;            %%----------(b行)
    y1=1; y2=1;y3=N;y4=N;
    x=[x1,x2,x3,x4];
    y=[y1,y2,y3,y4];  
    x_2 = round(x*cos(a)-y*sin(a));
    y_2 = round(x*sin(a)+y*cos(a));%%----------(c行)
    xmin=min(x_2);
    xmax=max(x_2);
    ymin=min(y_2);
    ymax=max(y_2);
    
    
    if xmin<=0          %%-------(d行)
        deltaX = abs(xmin)+1;
    else
        deltaX = 0;
    end                  %%-------(e行)
    if ymin<=0
        deltaY = abs(ymin)+1;
    else
        deltaY = 0;
    end
    
    M_2 =xmax- xmin+1;         %%-------(f行)
    N_2 =ymax- ymin+1;
    im = ones(M_2,N_2,K)*-1;  %%-------(g行) 
    
    for i=1:M_2 
        for j=1:N_2              
            x = round((i-deltaX)*cos(a)+(j-deltaY)*sin(a));
            y = round(-(i-deltaX)*sin(a)+(j-deltaY)*cos(a));     
            if(x>0 && x<=M && y>0 && y<=N)  %%-------(h行)
                im(i,j,:)=I(x,y,:);
            end                                  %%-------(i行)
        end
    end
    im=uint8(im );
    

    (1) [单选题]
    解释第a行代码的作用

    A、将旋转的弧度转化为角度
    B、将旋转的角度转化为弧度
    C、计算四个顶点旋转之后的坐标

    正确答案:B 
    

    (2) [单选题]
    解释第b行到第c行代码的作用:

    A、计算四个顶点旋转的弧度
    B、计算四个顶点旋转的角度
    C、计算四个顶点旋转之后的坐标

    正确答案:C
    

    (3) [单选题]
    解释第d行到第e行代码的作用:

    A、计算行的方向上的偏移量
    B、计算新的图像有几行
    C、计算新的图像有几列

    正确答案:A 
    

    (4) [单选题]
    解释第f行到第g行代码的作用:

    A、计算新的图像有几行、几列,并得到新的图像
    B、计算新的图像有几行、几列,并初始化新的图像
    C、计算新的图像有几列

    正确答案:B
    

    (5) [单选题]
    解释第h行到第i行代码的作用:

    A、新图像的行列坐标i和j,经过旋转逆变换后的坐标为x、y,如果x、y在原图像的合法范围内,就将原图像x、y处的像素值赋给新图像i、j处的像素
    B、原图像的行列坐标i和j,经过旋转变换后的坐标为x、y,将原图像i、j处的像素值赋给新图像x、y处的像素

    正确答案:A 
    

    二、知识巩固

    1、关于图像的旋转——反变换【效果最好】

    反变换方法就是从新图形的像素坐标反过来计算对应原图像坐标点的坐标。
    即将 图像的旋转计算公式:
    x1 =x0 cos(a)-y0 sin(a);
    y1 =x0 sin(a)+y0 cos(a);
    改写为:

    x0 =x1 cos(a)+y1 sin(a);
    y0 =-x1 sin(a)+y1 cos(a);
    

    2、图像旋转方法3之反变换法伪代码

    输入参数:图像矩阵F,旋转角度a
    输出参数:旋转后的图像矩阵G
    处理:
    1、获得图像尺寸,计算图像的四个顶点坐标。
    2、根据旋转公式:x_2 = round(x*cos(a)-y*sin(a));
    y_2 = round(x*sin(a)+y*cos(a)),计算图像的四个顶点旋转后的坐标。
    (x、y为原始图像的坐标,x_2、y_2为对应点旋转后的坐标)
    3、根据四个顶点旋转后的坐标,计算画布扩展后的大小M2、N2,并计算x和y方向的偏移量。初始化新图像G
    4、对新图像G的每一行for i=1:M_2
    5、对新图像G的每一列for j=1:N_2
    5.1 根据旋转反变换公式和x、y方向的偏移量,计算新图像的像素点G(i,j,:)旋转前在原图像F中对应的坐标x、y。
    5.2 如果(x>0 && x<=M && y>0 && y<=N),则将F(x,y,:)的像素值采样并且赋给G(i,j,:)
    

    3、图像旋转方法3之反变换法代码实现

    function [im]=rot3_inv(I,delta_ang)  
    [M,N,K] = size(I);
    a=delta_ang*pi/180;  
    
    x1=1; x2=M;x3=M;x4=1;            
    y1=1; y2=1;y3=N;y4=N;
    x=[x1,x2,x3,x4];
    y=[y1,y2,y3,y4];  
    x_2 = round(x*cos(a)-y*sin(a));
    y_2 = round(x*sin(a)+y*cos(a));
    xmin=min(x_2);
    xmax=max(x_2);
    ymin=min(y_2);
    ymax=max(y_2);
    
    if xmin<=0        
        deltaX = abs(xmin)+1;
    else
        deltaX = 0;
    end                  
    if ymin<=0
        deltaY = abs(ymin)+1;
    else
        deltaY = 0;
    end
    
    M_2 =xmax- xmin+1;         
    N_2 =ymax- ymin+1;
    im = ones(M_2,N_2,K)*-1; 
    
    for i=1:M_2 
        for j=1:N_2              
            x = round((i-deltaX)*cos(a)+(j-deltaY)*sin(a));
            y = round(-(i-deltaX)*sin(a)+(j-deltaY)*cos(a));     
            if(x>0 && x<=M && y>0 && y<=N)  
                im(i,j,:)=I(x,y,:);
            end                                
        end
    end
    im=uint8(im );
    imshow(I);title('原图');figure;
    imshow(im);title('转置后');
    

    函数调用:

    //此处旋转角度为60度;
    
    >> I = imread('football.jpg');
    >> [im]=rot3_inv(I,60);
    

    效果展示:

    在这里插入图片描述

    Ending!
    更多课程知识学习记录随后再来吧!

    就酱,嘎啦!
    

    在这里插入图片描述

    注:
    人生在勤,不索何获。

    展开全文
  • 一、遥感图像的几何变形有两层含义 一是指卫星在运行过程中,由于... 注:遥感图像的总体变形是平移、缩放、旋转、偏扭、弯曲 及其他变形综合作用的结果。 二、几何变形误差的影响因素 (1) 遥感器本身引起的畸变
  • 2021年前端面试题及答案

    万次阅读 多人点赞 2020-02-11 19:29:34
    如何区分 HTML 和 HTML5?  新特性: HTML5 现在已经不是 SGML 的子集,主要是关于图像,位置,存储,多任务等功能的增加。 拖拽释放(Drag and drop) API 语义化更好的内容标签(header,nav,footer,aside,article,...
  • 前端面试题

    万次阅读 多人点赞 2019-08-08 11:49:01
    CSS3新增类有那些? 37 如何居中div,如何居中一个浮动元素? 38 浏览器的内核分别是什么?经常遇到的浏览器的兼容性有哪些?原因,解决方法是什么,常用hack的技巧 ? 39 列出display的值,说明他们的作用。...
  • 函数的拉普拉斯算子也是该数的黑塞矩阵的迹,可以证明,它具有各向同性,即与坐标轴方向无关,坐标轴旋转后梯度结果不变。 Laplacian 算子对噪声比较敏感,所以图像一般先经过平滑处理,因为平滑处理也是用模板...
  • 自己写的一个贴图算法,虽然很简单,很容易就实现了,...//旋转后坐标 y2 = (int)(exa_y1cos(rot) + exa_x1sin(rot)) ; 身为小白,自己实现了这个简单的算法,也是很高兴的。希望对其他人也有所帮助。 rz和cz...
  • MATLAB--数字图像处理 图像几何变换

    千次阅读 2019-12-12 22:33:36
    1.将图像图像中心顺时针旋转30度,旋转之后的图像尺寸保持为原图像的尺寸。 2.将原图像放大2倍 3.得到该图像的水平镜像图片 4.得到该图像的垂直错切图像 四、实验仪器与设备 Win10 64位电脑 MATL...
  • C#基础教程-c#实例教程,适合初学者

    万次阅读 多人点赞 2016-08-22 11:13:24
    CLR执行中间语言代码前,要对中间语言代码的安全性,完整性进行验证,防止病毒对中间语言代码的修改。  版本支持:系统中的组件或动态联接库可能要升级,由于这些组件或动态联接库都要在注册表中注册,由此可能...
  • AlphaGo Zero:笔记与伪代码

    千次阅读 2017-11-03 06:42:57
    AlphaGo Zero 论文发布之后,阿尔伯塔大学 Yuxi Li 博士对该程序的工作原理进行了分析,并使用伪代码的方式对其训练过程进行了描述。原文链接请参见 https://pan.baidu.com/s/1jI481xW 。   1 引言   ...
  • 前一阵朋友碰到这么一道题:将图像原地顺时针旋转90度,不开辟新空间。此题看似平易(题目简短),仔细研究发现着实不容易。经过一番探索后,终于找到了正确的算法,但是当使用opencv实现时,有碰到了困难而且费了...
  • 读者可到http://www.tupwk.com.cn/downpage/index.asp下载本书演示程序的全部源代码、相关的字库文件以及所需的图像。 本书可作为图像处理编程人员的参考书,以及少学时、应用型数字图像处理课程的教材。书中包含...
  • 图像旋转 二值化 图像放大 缩小 镜像 边缘测试 图像求和差 彩色 线性变换 去噪等等..
  • 医学图像配准实现代码(matlab篇)

    万次阅读 多人点赞 2015-01-21 19:04:48
    这里主要讲解的是多模态或者说是多序列MRI图像配准。采用的图片是人体膝盖图。配准暂且分为五部 Step1. 下载图片 Step2. 初始配准(粗配准) Step3. 提高配准精度 Step4. 利用初始条件提高配准精度配准 ...
  • 数字图像处理课后习题汇总

    千次阅读 2020-12-22 16:17:41
    文章目录绪论Chapter 0 Course 1 WhatChapter 0 Cours 2Chapter 0 Course 3第一章Chapter 1 Introduction 1 HistoryChapter 1 Introduction 2 ...imagingChapter 2 digitization 3 step数字图像马赛克第三章Chapter 3 Color 1 M
  • 数字水印技术作为信息隐藏技术的一种,不可见性和鲁棒性是它的两个主要特点,通常在水印嵌入时需要确定水印的嵌入量,来平衡不可见性和鲁棒性之间...下面,我将介绍数字水印常见的几种攻击类型,分别从理论,代码和实例
  • 28 peppers vs 调节亮度色彩饱和度的peppers 汉明距离:4 peppers vs 调节大小后的peppers 汉明距离:1 算法性能分析 由实验结果可以看出,pHash对于图像旋转、翻转不具有鲁棒性,但由于经过DCT变换后提取了其低频...
  • 目录前言实例CSS实现旋转图像代码:效果:CSS实现一面图像一面文字合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左...
  • 本文转自博客园博主SChen1024的博文:opencv-9-图像噪声以及评估指标 PSNR 与SSIM 开始之前# 我们在将 opencv 的图像显示在了 qt 的label 上, 我们能够将图显示在label 上, 用于显示我们的算法, 我们在 opencv 上...
  • 【实践】数字图像处理DIP课程课业打卡实验2... 1、实现图像水平镜像的代码 2、实现直角坐标系中的图像缩小算法 叮嘟!这里是小啊呜的学习课程资料整理。好记性不如烂笔头,今天也是努力进步的一天。一起加油进阶吧!
  • 文章目录 一、图片旋转 二、MFC 控件PictureControl 清除显示 三、彩 四、直方图 五、为按钮添加背景图标 六、设置图标 七、改变组框外观 一、图片旋转 建立图片控件 改变图片控件ID 建立四个按钮 改变四个控件ID ...
  • 数字图像处理期末考试备考整理大全,一文搞定!
  • 图像倒转90度(Rotate Image)

    千次阅读 2019-06-27 20:22:40
    1.旋转90度,等于将图像对折,然后左右对调; 2.vector矩阵的声明和赋值; 二、代码学习: #include<iostream> #include<stdlib.h> #include<vector> #include<algorithm> class Solution {...
  • OpenGL:绘制一个旋转的正方形

    千次阅读 2016-03-24 22:41:56
    使用OpenGL提供的双缓存技术,绘制一个旋转的正方形(动画)。 什么是双缓存技术? 我们可以这样理解,若将屏幕刷新的频率放慢到肉眼可见,如果不使用双缓存技术,那么当屏幕重新绘制时,绘制的过程人也是可以...
  • 论文关键词:图像拼接 图像配准 图像融合 全景图 论文摘要:图像拼接(image mosaic)技术是将一组相互间重叠部分的图像序列进行空间匹配对准,经重采样合成后形成一幅包含各图像序列信息的宽视角场景的、完整的、...
  • 车牌识别(四)旋转校正

    千次阅读 2018-08-02 12:05:28
    如果给定的车牌斜掉了,必须旋转校正,要不然没办法识别出里面...每一步都保存了运算过后的图像,方便理解 #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&...
  • 百度飞桨PaddlePaddle-21天零基础实践深度学习-图像分类摘要数据集介绍眼疾分类数据集iChallenge-PM数据准备数据读取器定义数据输出形状卷积神经网络LeNetLeNet-5网络 (3conv+2fc)LeNet识别手写数字LeNet 识别眼疾...
  • 图像的阈值分割(Optimum Thresholding)

    万次阅读 多人点赞 2015-02-03 17:45:14
    图像分割就是把图像中具有特殊涵义的不同区域分开来,这些区域是互不相交的,每个区域都满足特定区域的一致性。图像分割是图像处理中的重要问题,也是计算机视觉研究中的一个经典难题。计算机视觉中的图像理解包括...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,436
精华内容 2,574
关键字:

旋转图像的伪代码