图像旋转 imgrotation

根据输入的角度进行旋转变换

很简单,套公式。

遍历输出的图像的像素,去原图中找对应像素,超出边界置0 

如果遍历原图像素,去构建输出图像像素则会有很多噪声,因为浮点数计算的误差

 

数据校验及数据转换

void CBMPDlg::OnRota() 
{
    // TODO: Add your control notification handler code here
    
    CDC *pDC = m_image1.GetDC();
    CRect myRECT;
    m_image1.GetClientRect(&myRECT);

    //数据校验
    GetDlgItem(IDC_ANGLE)->GetWindowText(m_angle);
    if(m_angle.GetLength()==0)
    {
        MessageBox("Input Angle First~ ","Ou no~",MB_OK);
        return;
    }
    else
    {
        int cnt_point=0;
        for(int i=0;i<m_angle.GetLength();i++)
        {
            char c=m_angle.GetAt(i);
            if(c>='0'&&c<='9'||c=='.')
            {
                if(c=='.')cnt_point++;
                if(cnt_point<=1)continue;
            }
            MessageBox("wrong input~ ","wrong~",MB_OK);
            return;
        }
    }

    if(hasopened==false)
    {
        MessageBox("Open the file~ ","I miss it~",MB_OK);
        return;
    }
    double angle=atof((LPCSTR(m_angle)));//CString to double  
    imgrotation im(filepath);
    im.setAngle(angle);

    //检查角度
    CString str;
    str.Format("Rotation Angle:%.2lf",im.getAngle());
    SetDlgItemText(IDC_OUTPUT,str);

    im.rotation(pDC,myRECT);

    m_image1.ReleaseDC(pDC);

}

旋转公式图解:

void imgrotation::rotation(CDC *pDC, CRect rc)
{
    angle/=57.3;
    int ox=vImageWidth()/2;
    int oy=vImageHeight()/2;
    img_r=(unsigned char*)malloc(sizeof(unsigned char)*vPixelBytes());
    unsigned int _img_w = vImageWidth()*3;
    if(_img_w%4!=0)_img_w=(_img_w/4+1)*4;
    memcpy(img_r,raw_img,sizeof(unsigned char)*vPixelBytes());
    int x,y,i,j;

    for(y=0;y<vImageHeight();y++)
    {
        for(x=0;x<vImageWidth();x++)
        {
            int xx=(x-ox)*cos(angle)-(y-oy)*sin(angle)+ox;
            int yy=(x-ox)*sin(angle)+(y-oy)*cos(angle)+oy;
            if(xx>=0&&xx<vImageWidth()&&yy>=0&&yy<vImageHeight())
            {
                raw_img[y*_img_w+x*3]  =img_r[yy*_img_w+xx*3];   //B
                raw_img[y*_img_w+x*3+1]=img_r[yy*_img_w+xx*3+1]; //G
                raw_img[y*_img_w+x*3+2]=img_r[yy*_img_w+xx*3+2]; //R
            }
            else
            {
                raw_img[y*_img_w+x*3]  =0;  //B
                raw_img[y*_img_w+x*3+1]=0;     //G
                raw_img[y*_img_w+x*3+2]=0 ;    //R
            }
        }
    }
    imshow2(pDC, rc);
}

效果图:

 再来看张图

锯齿非常严重,放大后简直不忍直视:

 

双线性插值

为了解决以上问题,我用了双线性插值算法

void imgrotation::rotation2(CDC *pDC, CRect rc)
{
    //使用raw_img,遍历输出图像在原图中找对应像素,超出部分置0
    angle/=57.3;
    int ox=vImageWidth()/2;
    int oy=vImageHeight()/2;
    //img_r=(unsigned char*)malloc(sizeof(unsigned char)*vPixelBytes());
    unsigned int _img_w = vImageWidth()*3;
    if(_img_w%4!=0)_img_w=(_img_w/4+1)*4;
    memcpy(img_r,raw_img,sizeof(unsigned char)*vPixelBytes());
    int i,j,k;
    double fcos=cos(angle);
    double fsin=sin(angle);

    for(i=0;i<vImageHeight();i++)
    {
        for(j=0;j<vImageWidth();j++)
        {
            //双线性法时x,y一定要用double型,否则下面的权重u,v将不起作用
            double x=(j-ox)*fcos-(i-oy)*fsin+ox;
            double y=(j-ox)*fsin+(i-oy)*fcos+oy;

            if(x<0 || x>=vImageWidth() || y<0 || y>=vImageHeight())
            {
                //设置背景色
                for(k=0;k<3;k++)
                {
                    raw_img[i*_img_w+j*3+k]=0;
                }
                continue;
            }

            int x1,x2,y1,y2;//距离当前点最近的4个点坐标
            x1=(int)x;
            x2=x1+1;
            y1=(int)y;
            y2=y1+1;
            
            //设置双线性法权值
            double u,v;
            u=x-x1;
            v=y-y1;

            //处理边界问题
            if(x2>=vImageWidth())u=0;
            if(y2>=vImageHeight())v=0;
            
            unsigned int Q11,Q21,Q12,Q22,R1,R2,P;
            for(k=0;k<3;k++)
            {
                Q11=img_r[y1*_img_w+x1*3+k];
                Q21=img_r[y1*_img_w+x2*3+k];
                if(y2<vImageHeight()){//防止计算越界
                    Q12=img_r[y2*_img_w+x1*3+k];
                    Q22=img_r[y2*_img_w+x2*3+k];
                }
                R1=(1-u)*Q11+u*Q21;
                R2=(1-u)*Q12+u*Q22;
                P=(1-v)*R1+v*R2;
                raw_img[i*_img_w+j*3+k]=P;
            }
        }
    }
    imshow2(pDC, rc);
}

效果对比图:

 还有一个明显的问题:无法显示所有像素,那么就要重新输出图像的宽高,还要重新计算偏移量:

void imgrotation::rotation(CDC *pDC, CRect rc)
{
    //使用imgbasic中的img数据,outHeight,outWidth为输出图像高宽
    angle/=57.3;
    double fcos=cos(angle);
    double fsin=sin(angle);
    
    //计算图片实际的高和宽
    int srcx1,srcx2,srcx3,srcx4;
    int srcy1,srcy2,srcy3,srcy4;
    srcx1 = 0;                srcy1 = 0;
    srcx2 = 0;                srcy2 = vImageWidth()-1;
    srcx3 = vImageHeight()-1; srcy3 = 0;
    srcx4 = vImageHeight()-1; srcy4 = vImageWidth()-1;
    double tranx1,tranx2,tranx3,tranx4;
    double trany1,trany2,trany3,trany4;
    tranx1=srcy1*fcos-srcx1*fsin; trany1=srcy1*fsin+srcx1*fcos;
    tranx2=srcy2*fcos-srcx2*fsin; trany2=srcy2*fsin+srcx2*fcos;
    tranx3=srcy3*fcos-srcx3*fsin; trany3=srcy3*fsin+srcx3*fcos;
    tranx4=srcy4*fcos-srcx4*fsin; trany4=srcy4*fsin+srcx4*fcos;
    //计算输出图像的高和宽
    unsigned int outHeight,outWidth,pixelByte;
    outHeight=(unsigned int)(max(fabs(trany4-trany1),fabs(trany3-trany2))+1.5);
    outWidth=(unsigned int)(max(fabs(tranx4-tranx1),fabs(tranx3-tranx2))+1.5);
 
    unsigned int _imgr_w = outWidth*3;

    //img_r=(unsigned char*)malloc(sizeof(unsigned char)*outHeight*outWidth*3);

    double dx=-0.5*outWidth*fcos-0.5*outHeight*fsin+0.5*vImageWidth();
    double dy=0.5*outWidth*fsin-0.5*outHeight*fcos+0.5*vImageHeight();

    unsigned int _raw_w=vImageWidth()*3;
    
    int i,j,k;

    for(i=0;i<outHeight;i++)
    {
        for(j=0;j<outWidth;j++)
        {
            double x=j*fcos+i*fsin+dx;
            double y=-j*fsin+i*fcos+dy;

            if(x<0 || x>=vImageWidth() || y<0 || y>=vImageHeight())
            {
                //设置背景色
                for(k=0;k<3;k++)
                {
                    img_r[i*_imgr_w+j*3+k]=200;
                }
                continue;
            }

            int x1,x2,y1,y2;//距离当前点最近的4个点坐标
            x1=(int)x;
            x2=x1+1;
            y1=(int)y;
            y2=y1+1;
            
            //设置双线性法权值
            double u,v;
            u=x-x1;
            v=y-y1;

            //处理边界问题
            if(x2>=vImageWidth())u=0;
            if(y2>=vImageHeight())v=0;
            
            unsigned int Q11,Q21,Q12,Q22,R1,R2,P;
            for(k=0;k<3;k++)
            {
                Q11=img[y1*_raw_w+x1*3+k];
                Q21=img[y1*_raw_w+x2*3+k];
                if(y2<vImageHeight()){//防止计算越界
                    Q12=img[y2*_raw_w+x1*3+k];
                    Q22=img[y2*_raw_w+x2*3+k];
                }
                R1=(1-u)*Q11+u*Q21;
                R2=(1-u)*Q12+u*Q22;
                P=(1-v)*R1+v*R2;
                img_r[i*_imgr_w+j*3+k]=P;
            //    img_r[i*_imgr_w+j*3+k]=img[y1*_raw_w+x1*3+k]; 双线性处理前后对比
            }
        }
    }
    //暂时无法实现快速显示,用慢速的
    for(i=0;i<outHeight;i++){
        for(j=0;j<outWidth;j++){
            unsigned char R,G,B;
            R = img_r[i*_imgr_w+j*3+0];
            G = img_r[i*_imgr_w+j*3+1];
            B = img_r[i*_imgr_w+j*3+2];
            pDC->SetPixel((rc.Width()-outWidth)/2+j,(rc.Height()-outHeight)/2+i,RGB(R,G,B));
        }
    }
}

旋转85度的效果图:(边缘的锯齿还有待改善

 

posted on 2015-05-09 18:48  kylehz  阅读(838)  评论(0编辑  收藏  举报