我想用VB将JPG图片旋转90度
在网上找了好多源代码,一般都是放在PICBOX上转成BMP,思路如下:
JPG->BMP->旋转90度->BMP->JPG
不过这样速度太慢了,因为我的JPG原图一般都是6M左右.一般转一个都需要好几分钟
请问各位有没有更好的方法,速度越快越好!
另,我要的是VB源代码,不要老告诉我用第三方软件什么去手动转.
还有,如果机子装了ACDsee,至需要在图片右键选择旋转多少度就可以了,速度也非常快!
,我听说可以引用ACDSee的控件什么,具体怎么做呢?

解决方案 »

  1.   

    在 windows中,彩色图像在内存中一般都是按RGB(RGBA)格式存放的,也就是说不管你的原始图像在物理文件上是啥格式,要显示出来都要转换到这种格式。
      

  2.   

    6M的JPG图片,也够大啊!
    我猜测你是不是要把数码相机里面倒置的图片摆正?如果是这样,没必要编程,用ACDSee选择多张倒置的图片,可以一次摆正。
      

  3.   

    其实我没要求把图片现实出来
    我只要求把JPG图片自动旋转90度,旋转后的图片格式还是压缩过的JPG,谢谢
      

  4.   

    我知道你不需要显示出来,但是你总要把图像加载到内存后才能处理啊,也就是WINDOWS不能直接识别以JPG格式压缩后的图像数据,要对图像进行处理,必须先把图像转换RGB格式,才能处理,也就是说你6MB大小的图像,加载到内存后可能需要占用45MB左右的内存(这个要看你的JPG的压缩比)。 如果我告诉你在我的破机器上,旋转5000*4000大小的JPG需要5秒时间 (就是给我一个图像到保存旋转后的图像所需时间)你满意不?
      

  5.   

    我下载的旋转图片的源代码旋转图片也用时不多,但是在将BMP保存为JPG的时候太慢了
      

  6.   

    其实旋转算法本身很快,拿5000*4000这个大小的图片来说,在我的机器上只需要1S左右, 从物理文件读取图像数据到内存需要1.5s,旋转都保存图像需要大概需要3S。你从网络上得到的代码都是一些没有优化的比较原始的,并且旋转90度因其特殊性可优化空间很大。我给你一个函数,你自己看看速度如何,基本上比网络上的都快不过我没有写任何注释。
    Private Type GdiplusStartupInput
        GdiplusVersion           As Long
        DebugEventCallback       As Long
        SuppressBackgroundThread As Long
        SuppressExternalCodecs   As Long
    End Type
    Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
    End TypePrivate Type RECTF
        nLeft As Single
        nTop As Single
        nWidth As Single
        nHeight As Single
    End TypePrivate Type BitmapData
       Width As Long
       Height As Long
       Stride As Long
       PixelFormat As Long
       scan0 As Long
       Reserved As Long
    End Type
    Private Enum ImageLockMode
       ImageLockModeRead = &H1
       ImageLockModeWrite = &H2
       ImageLockModeUserInputBuf = &H4
    End Enum
    Private Enum EncoderParameterValueType
        [EncoderParameterValueTypeByte] = 1
        [EncoderParameterValueTypeASCII] = 2
        [EncoderParameterValueTypeShort] = 3
        [EncoderParameterValueTypeLong] = 4
        [EncoderParameterValueTypeRational] = 5
        [EncoderParameterValueTypeLongRange] = 6
        [EncoderParameterValueTypeUndefined] = 7
        [EncoderParameterValueTypeRationalRange] = 8
    End EnumPrivate Type EncoderParameter
        GUID(0 To 3)   As Long
        NumberOfValues As Long
        Type           As EncoderParameterValueType
        Value          As Long
    End Type
    '-- Encoder Parameters structure
    Private Type EncoderParameters
        Count     As Long
        Parameter As EncoderParameter
    End Type
    Private Type ImageCodecInfo
        ClassID(0 To 3)   As Long
        FormatID(0 To 3)  As Long
        CodecName         As Long
        DllName           As Long
        FormatDescription As Long
        FilenameExtension As Long
        MimeType          As Long
        flags             As Long
        Version           As Long
        SigCount          As Long
        SigSize           As Long
        SigPattern        As Long
        SigMask           As Long
    End Type
    Private Const PixelFormat32bppARGB = &H26200A
    Private Declare Function GdiplusStartup Lib "gdiplus" (Token As Long, inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As Long
    Private Declare Sub GdiplusShutdown Lib "gdiplus" (ByVal Token As Long)Private Declare Function GdipLoadImageFromFile Lib "gdiplus" (ByVal FileName As Long, hImage As Long) As Long
    Private Declare Function GdipDisposeImage Lib "gdiplus" (ByVal Image As Long) As Long
    Private Declare Function GdipCreateFromHDC Lib "gdiplus" (ByVal hDC As Long, Graphics As Long) As Long
    Private Declare Function GdipDeleteGraphics Lib "gdiplus" (ByVal Graphics As Long) As Long
    Private Declare Function GdipDrawImageRectRectI Lib "gdiplus" (ByVal Graphics As Long, ByVal hImage As Long, ByVal dstX As Long, ByVal dstY As Long, ByVal dstWidth As Long, ByVal dstHeight As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal SrcWidth As Long, ByVal SrcHeight As Long, ByVal srcUnit As Long, Optional ByVal imageAttributes As Long = 0, Optional ByVal callback As Long = 0, Optional ByVal callbackData As Long = 0) As Long
    Private Declare Function GdipBitmapLockBits Lib "gdiplus" (ByVal bitmap As Long, Rct As RECT, ByVal flags As ImageLockMode, ByVal PixelFormat As Long, lockedBitmapData As BitmapData) As Long
    Private Declare Function GdipBitmapUnlockBits Lib "gdiplus" (ByVal bitmap As Long, lockedBitmapData As BitmapData) As LongPrivate Declare Function GdipGetImageWidth Lib "gdiplus" (ByVal Image As Long, Width As Long) As Long
    Private Declare Function GdipGetImageHeight Lib "gdiplus" (ByVal Image As Long, Height As Long) As Long
    Private Declare Function lstrlenW Lib "kernel32" (ByVal psString As Any) As Long
    Private Declare Function CLSIDFromString Lib "ole32" (ByVal lpszProgID As Long, pCLSID As Any) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDst As Any, lpSrc As Any, ByVal ByteLength As Long)
    Private Declare Function GdipSaveImageToFile Lib "gdiplus" (ByVal hImage As Long, ByVal sFilename As Long, clsidEncoder As Any, encoderParams As Any) As LongPrivate Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
    Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
    Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (ByRef Ptr() As Any) As Long
    Private Declare Function GdipGetImageBounds Lib "gdiplus.dll" (ByVal nImage As Long, srcRect As RECTF, srcUnit As Long) As Long
    Private Declare Function GdipGetImageEncodersSize Lib "gdiplus" (numEncoders As Long, Size As Long) As Long
    Private Declare Function GdipGetImageEncoders Lib "gdiplus" (ByVal numEncoders As Long, ByVal Size As Long, Encoders As Any) As Long
    Private Declare Function GdipCreateBitmapFromScan0 Lib "gdiplus" (ByVal Width As Long, ByVal Height As Long, ByVal Stride As Long, ByVal PixelFormat As Long, scan0 As Any, bitmap As Long) As LongPrivate Declare Function GdipLoadImageFromStream Lib "gdiplus" (ByVal Stream As IUnknown, Image As Long) As Long
    Private Declare Function GetTickCount Lib "kernel32" () As Long
      

  7.   


    Private Function Rotate90(FileName As String) As Long
        Dim i                   As Long, j                      As Long
       
        Dim Token               As Long
        Dim Gsp                 As GdiplusStartupInput
        Dim BmpData             As BitmapData, Image            As Long
        Dim Dimensions          As RECTF, Rct                   As RECT
        
        Dim DataArr(0)          As Long, pDataArr(0 To 0)       As Long
        Dim OldArrPtr           As Long, OldpArrPtr             As Long
        Dim LineAddBytes        As Long
        
        Dim DataArrC(0)         As Long, pDataArrC(0 To 0)      As Long
        Dim OldArrPtrC          As Long, OldpArrPtrC            As Long
        Dim mPtrC               As Long
        
        Dim Width               As Long, Height                 As Long
        Dim Stride              As Long, Pointer                As Long
        
       Rotate90 = GetTickCount - Rotate90
        Gsp.GdiplusVersion = 1
        GdiplusStartup Token, Gsp
        GdipLoadImageFromFile StrPtr(FileName), Image
        
        GdipGetImageBounds Image, Dimensions, 2
        Rct.Right = Dimensions.nWidth
        Rct.Bottom = Dimensions.nHeight
       
        GdipBitmapLockBits Image, Rct, ImageLockModeRead, PixelFormat32bppARGB, BmpData
          
        
        mPtrC = GlobalAlloc(GPTR, BmpData.Stride * BmpData.Height)
        CopyMemory ByVal mPtrC, ByVal BmpData.scan0, BmpData.Stride * BmpData.Height
         
        MakePoint VarPtrArray(DataArr), VarPtrArray(pDataArr), OldArrPtr, OldpArrPtr
        MakePoint VarPtrArray(DataArrC), VarPtrArray(pDataArrC), OldArrPtrC, OldpArrPtrC
        Pointer = BmpData.scan0
        Width = BmpData.Width
        Height = BmpData.Height
        Stride = BmpData.Stride
        pDataArr(0) = mPtrC
        For j = 1 To Width
            pDataArrC(0) = Pointer + 4 * (Width - j)
            For i = 1 To Height
                DataArr(0) = DataArrC(0)
                pDataArr(0) = pDataArr(0) + 4
                pDataArrC(0) = pDataArrC(0) + Stride
            Next
        Next
        FreePoint VarPtrArray(DataArr), VarPtrArray(pDataArr), OldArrPtr, OldpArrPtr
        FreePoint VarPtrArray(DataArrC), VarPtrArray(pDataArrC), OldArrPtrC, OldpArrPtrC    GdipBitmapUnlockBits Image, BmpData
        GdipDisposeImage Image
          
        GdipCreateBitmapFromScan0 Height, Width, Height * 4, PixelFormat32bppARGB, ByVal mPtrC, Image
      
        SavePictureToFile Image, FileName
      
        GdipDisposeImage Image
        GlobalFree mPtrC
        
        GdiplusShutdown Token
        Rotate90 = GetTickCount
    End FunctionPrivate Function SavePictureToFile(Image As Long, FileName As String, Optional ByVal Quality As Long = 80) As Boolean
        Dim aEncParams()         As Byte
        Dim uEncCLSID(0 To 3)   As Long, uEncParams         As EncoderParameters
        Const EncoderQuality As String = "{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}"
        If GetEncoderClsID("image/jpeg", uEncCLSID) <> -1 Then
            uEncParams.Count = 1                                        ' 设置自定义的编码参数,这里为1个参数
            ReDim aEncParams(1 To Len(uEncParams))
            With uEncParams.Parameter
                .NumberOfValues = 1
                .Type = [EncoderParameterValueTypeLong]                 ' 设置参数值的数据类型为长整型
                Call CLSIDFromString(StrPtr(EncoderQuality), .GUID(0))  ' 设置参数唯一标志的GUID,这里为编码品质
                If Quality < 0 Then
                    Quality = 0
                ElseIf Quality > 100 Then
                    Quality = 100
                End If
                .Value = VarPtr(Quality)                                ' 设置参数的值:品质等级,最高为100,图像文件大小与品质成正比
            End With
            CopyMemory aEncParams(1), uEncParams, Len(uEncParams)
            'If FileExist(FileName) Then Kill FileName
            SavePictureToFile = (GdipSaveImageToFile(Image, StrPtr(FileName), uEncCLSID(0), aEncParams(1)) = 0&)
        End If
    End FunctionPrivate Function GetEncoderClsID(strMimeType As String, ClassID() As Long) As Long
        Dim Num         As Long
        Dim Size        As Long
        Dim i           As Long
        Dim Info()      As ImageCodecInfo
        Dim Buffer()    As Byte
        GetEncoderClsID = -1
        '得到解码器数组的大小
        Call GdipGetImageEncodersSize(Num, Size)
        If (Size = 0) Then Exit Function ' 失败
        ReDim Info(1 To Num) As ImageCodecInfo  '给数组动态分配内存
        ReDim Buffer(1 To Size) As Byte
     
        Call GdipGetImageEncoders(Num, Size, Buffer(1))           '得到数组和字符数据
        Call CopyMemory(Info(1), Buffer(1), (Len(Info(1)) * Num))      '复制类头
        
        For i = 1 To Num             '循环检测所有解码
            If (StrComp(PtrToStrW(Info(i).MimeType), strMimeType, vbTextCompare) = 0) Then         '必须把指针转换成可用的字符
                CopyMemory ClassID(0), Info(i).ClassID(0), 16 '保存类的ID
                GetEncoderClsID = i      '返回成功的索引值
                Exit For
            End If
        Next
        Erase Info
        Erase Buffer
    End FunctionPublic Function PtrToStrW(ByVal lpsz As Long) As String
        Dim Out         As String
        Dim lLen        As Long
        lLen = lstrlenW(lpsz)    If (lLen > 0) Then
            Out = StrConv(String$(lLen, vbNullChar), vbUnicode)
            Call CopyMemory(ByVal Out, ByVal lpsz, lLen * 2)
            PtrToStrW = StrConv(Out, vbFromUnicode)
        End If
    End FunctionPrivate Sub MakePoint(ByVal DataArrPtr As Long, ByVal pDataArrPtr As Long, ByRef OldArrPtr As Long, ByRef OldpArrPtr As Long)
        Dim Temp As Long, TempPtr As Long
        CopyMemory Temp, ByVal DataArrPtr, 4        '得到DataArrPtr的SAFEARRAY结构的地址
        Temp = Temp + 12                            '这个指针偏移12个字节后就是pvData指针
        CopyMemory TempPtr, ByVal pDataArrPtr, 4    '得到pDataArrPtr的SAFEARRAY结构的地址
        TempPtr = TempPtr + 12                      '这个指针偏移12个字节后就是pvData指针
        CopyMemory OldpArrPtr, ByVal TempPtr, 4     '保存旧地址
        CopyMemory ByVal TempPtr, Temp, 4           '使pDataArrPtr指向DataArrPtr的SAFEARRAY结构的pvData指针
        CopyMemory OldArrPtr, ByVal Temp, 4         '保存旧地址
    End Sub'*****************************************************************************************
    '**    过 程 名 :  FreePoint
    '**    输    入 :
    '**    功能描述 :  取消绑定模拟数组
    '**    开发日期 :  2007-4-02
    '**    作    者 :  laviewpbt
    '**    修改日期 :
    '**    版    本 :  Version 1.2.1
    '****************************************************************************************Private Sub FreePoint(ByVal DataArrPtr As Long, ByVal pDataArrPtr As Long, ByVal OldArrPtr As Long, ByVal OldpArrPtr As Long)
        Dim TempPtr As Long
        CopyMemory TempPtr, ByVal DataArrPtr, 4         '得到DataArrPtr的SAFEARRAY结构的地址
        CopyMemory ByVal (TempPtr + 12), OldArrPtr, 4   '恢复旧地址
        CopyMemory TempPtr, ByVal pDataArrPtr, 4        '得到pDataArrPtr的SAFEARRAY结构的地址
        CopyMemory ByVal (TempPtr + 12), OldpArrPtr, 4  '恢复旧地址
    End Sub
    Public Function FileExist(FileName As String) As Boolean
        
        On Error GoTo Handler
        If (GetAttr(FileName) And vbArchive) = vbArchive Then
             FileExist = True
        End If
        Exit Function
    Handler:End Function
      

  8.   


    给你提2点建议。
    1、照片分辩率不要设那么高。这样,在转换时,可以节约时间。
    2、实在不行,可考虑图片缩放,在内存中就可以进行,用API也很快。其实,你旋转摆正后,保存到服务器上的图片不需要原始图片那么大。
      

  9.   

    gdi+ 相当简单
     Image img = Image.FromFile("your.jpg");
           Image imgdest = new Bitmap(img.Height, img.Width);
           Graphics g = Graphics.FromImage(imgdest);
           g.RotateTransform(90.0F);
           g.DrawImage(img,0, -1*img.Height);
           imgdest.Save("new.jpg",System.Drawing.Imaging.ImageFormat.Jpeg);            
    这是c#的,如果嫌效率不高可以通过c++直接使用gdiplus 编非托管的
      

  10.   

    VB6么..
    LZ用GDI+的Flat API吧...
    vb.net可以直接用GDI+了.
    PS:GDI+ Flat API就是8L贴的东东.
    不是很复杂.不过声明API和结构很麻烦囧.
      

  11.   

    JPG图片旋转90度
    '以下代码在bas#############
    Private Type POINTAPI
    X As Long
    y As Long
    End TypePrivate Type Bitmap
    bmType As Long
    bmWidth As Long
    bmHeight As Long
    bmWidthBytes As Long
    bmPlanes As Integer
    bmBitsPixel As Integer
    BmBits As Long
    End Type
    Private Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
    Private Declare Function GetTickCount Lib "kernel32" () As Long
    Private Declare Function SelectObject Lib "gdi32" (ByVal hDC As Long, ByVal hObject As Long) As Long
    Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
    Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long
    Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
    Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
    Private Declare Function ReleaseDC Lib "user32" (ByVal hWnd As Long, ByVal hDC As Long) As Long
    Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
    Private Declare Function PlgBlt Lib "gdi32" (ByVal hdcDest As Long, lpPoint As POINTAPI, ByVal hdcSrc As Long, ByVal nXSrc As Long, ByVal nYSrc As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hbmMask As Long, ByVal xMask As Long, ByVal yMask As Long) As LongPublic Sub Rotate90(Pic As PictureBox)
    Dim DefPoints(3) As POINTAPI
    Dim MemDC As Long
    Dim BHandle As Long
    Dim OldObject As Long
    Dim picINFO As Bitmap
    Dim Width As Long, Height As Long, Temp As Long
    Pic.AutoSize = True: Pic.AutoRedraw = True
    GetObject Pic.Image, Len(picINFO), picINFO
    Width = picINFO.bmWidth
    Height = picINFO.bmHeight
    MemDC = CreateCompatibleDC(Pic.hDC)
    BHandle = CreateCompatibleBitmap(Pic.hDC, Width, Height)
    OldObject = SelectObject(MemDC, BHandle)
    BitBlt MemDC, 0, 0, Width, Height, Pic.hDC, 0, 0, vbSrcCopy
    DefPoints(0).X = 0
    DefPoints(0).y = Width
    DefPoints(1).X = 0
    DefPoints(1).y = 0
    DefPoints(2).X = Height
    DefPoints(2).y = Width
    Pic.Move Pic.Left, Pic.Top, Pic.Height, Pic.Width
    Set Pic.Picture = Nothing
    PlgBlt Pic.hDC, DefPoints(0), MemDC, 0, 0, Width, Height, 0, 0, 0
    SelectObject MemDC, OldObject
    DeleteDC MemDC
    DeleteObject BHandle
    End Sub'窗体代码#############
    Private Sub Command1_Click()'旋转JPG图片90度
    Dim t As Long
    t = GetTickCount
    Rotate90 Picture1
    End Sub
    Private Sub Command2_Click()'打开JPG图片
    CommonDialog1.Filter = " *.jpg |*.jpg"
      CommonDialog1.ShowOpen
      If CommonDialog1.Filename <> "" Then
         Picture1.Picture = LoadPicture(CommonDialog1.Filename)
    End Sub
      

  12.   

    c++ 注意的是
    一个要在使用gdi+之前调用gdiplusstartup
    之后要gdiplusshutdown
    图像格式不像托管的那样直接拿来就用   CLSID jpgClsid;
       GetEncoderClsid(L"image/jpeg", &jpgClsid);
       CLSID pngClsid;
       GetEncoderClsid(L"image/png", &pngClsid);
       
      

  13.   


    实际上,有两种选择:1 利用 PictureBox 加载图片,获取像素后做旋转运算,然后可以直接保存为 .jpg 文件(不需要 .bmp 过度)。这样做的好处是,不必解析 .jpg 文件结构。2 直接解析 .jpg 文件,取得像素,运算,结构成新的 .jpg 文件,写文件。这个需要熟悉 .jpg 的文件结构。
      

  14.   

    不用转化为RGB。
    JPG是压缩的YCbCr的数据,把JPG解码为YCbCr,然后旋转YCbCr,再编码为JPG,省去转化为RGB的时间,应该会快点。
      

  15.   

    如果旋转的是90,180,270的话,应该不需要转换。我觉得JPG文件中,应该有方向的选项。你可以自己试一下,把一个JPG文件用Windows来打开,下面有一个旋转按钮,可以以90度进行旋转,速度飞快。你可以试一下,在转换前,与转换后,比较一下JPG文件究竟哪些内容发生了变化。
      

  16.   

     打酱油 ,
     看到好多牛人 
     VB 有点犯晕 ,对于新入行的程序员来讲 VB是个大问题!!!
      

  17.   

    楼上的都搞错了,LZ问的是JPG旋转90度,这种特殊情况下不需要把JPG解压为BMP来处理,ACDSee是直接对JPG的数据来处理的,这种方法速度要快一个数量级,而且不会损失画质
      

  18.   


    有可能,但是ACDSEE你看到的旋转效果快,那是因为显示前他已经在内存中了,就一个纯旋转90度的算法来说,是很快的,我说过不到1s,显示的话就更快了。
      

  19.   

    这样的话图片显示不正确.
    我改了下..
                Image img = Image.FromFile(@"H:\123.jpg");
                Image imgdest = new Bitmap(img.Height, img.Width);
                Graphics g = Graphics.FromImage(imgdest);
                g.RotateTransform(90.0F);
                g.DrawImage(img, 0, -1 * img.Height,img.Width,img.Height);        // 在指定位置并且按指定大小绘制指定的 Image。
                imgdest.Save(@"H:\1234.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);应该是这样..刚在机子上试了下..
      

  20.   

    如果只是修改文件的话,修改exif信息就好了,别的不用动。
    具体是tiff ifd0的orientation域,改两个字节就行,详见exif标准
      

  21.   

    用GDI+ 矩阵旋转 效率超级高
      

  22.   

    img.RotateFlip(RotateFlipType.Rotate90FlipNone);
      

  23.   

    ddddddddddddddddddddddddddddddddddddddddd
      

  24.   

    我下载的旋转图片的源代码旋转图片也用时不多,但是在将BMP保存为JPG的时候太慢了
      

  25.   

    不懂,感觉写了一大堆程序,还不如直接用ACD see转好放进去好了
      

  26.   

    在 windows中,彩色图像在内存中一般都是按RGB(RGBA)格式存放的,也就是说不管你的原始图像在物理文件上是啥格式,要显示出来都要转换到这种格式。
      

  27.   

    问题好像没有得到根本解决,不过看后也学到些东西。。
    就看懂个用GDI的方式。。