DOBON.NET DOBON.NETプログラミング掲示板過去ログ

JPEG画像をロスレス回転する。

環境/言語:[Framework1.0]
分類:[.NET]

お世話になっております。
今回の質問はJPEG画像の回転時に画像の劣化をしないで回転するには?なのですが実は既にそれらしきソースがありましたので、ご紹介したいと思います。
つきましてはソースコードがC#.NETでして、私はVB.NETしか分かりません、ほんとにロスレスでJPEG画像を回転出来るのか知りたいのです。願わくばVB.NETにしたソースに、どなたか書き直していただけませんでしょうか?。又分かってしまった方、ソースだけもらっっチャオなんて思わないで救いの手を〜(結構見つけるの苦労したものでごほうびに!!!あっなんかこんなんだれでも知ってるよって場合もあるかな(汗))
それではソースのご紹介&URL
http://www.eggheadcafe.com/articles/20030706.asp

/////////////////////////////////////////////////
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for(j = 0; j < encoders.Length; ++j)
{
if(encoders[j].MimeType == mimeType)
return encoders[j];
} return null;
}

private void WriteNewDescriptionInImage(string Filename,string NewDescription)
{
Image Pic;
PropertyItem[] PropertyItems;
byte[] bDescription=new Byte[NewDescription.Length];
int i;
string FilenameTemp;
Encoder Enc=Encoder.Transformation;
EncoderParameters EncParms=new EncoderParameters(1);
EncoderParameter EncParm;
ImageCodecInfo CodecInfo=GetEncoderInfo("image/jpeg");

// copy description into byte array
for (i=0;i<NewDescription.Length;i++) bDescription[i]=(byte)NewDescription[i];

// load the image to change
Pic=Image.FromFile(Filename);

// put the new description into the right property item
PropertyItems=Pic.PropertyItems;
PropertyItems[0].Id=0x010e; // 0x010e as specified in EXIF standard
PropertyItems[0].Type=2;
PropertyItems[0].Len=NewDescription.Length;
PropertyItems[0].Value=bDescription;
Pic.SetPropertyItem(PropertyItems[0]);

// we cannot store in the same image, so use a temporary image instead
FilenameTemp=Filename+".temp";

// for lossless rewriting must rotate the image by 90 degrees!
EncParm=new EncoderParameter(Enc,(long)EncoderValue.TransformRotate90);
EncParms.Param[0]=EncParm;

// now write the rotated image with new description
Pic.Save(FilenameTemp,CodecInfo,EncParms);

// for computers with low memory and large pictures: release memory now
Pic.Dispose();
Pic=null;
GC.Collect();

// delete the original file, will be replaced later
System.IO.File.Delete(Filename);

// now must rotate back the written picture
Pic=Image.FromFile(FilenameTemp);
EncParm=new EncoderParameter(Enc,(long)EncoderValue.TransformRotate270);
EncParms.Param[0]=EncParm;
Pic.Save(Filename,CodecInfo,EncParms);

// release memory now
Pic.Dispose();
Pic=null;
GC.Collect();

// delete the temporary picture
System.IO.File.Delete(FilenameTemp);
}

////////////////////////////////
2004/03/30(Tue) 15:00:02 編集(投稿者)

■No3371に返信(アフロさんの記事)
まるまる移植しました。動作確認はしておりません。
一部そのままではキャストできない部分がありましたが、
たぶん大丈夫かと思います。

ただ、どぼんさんのTipsページにC#とVB.NETのサンプルはたくさんありますので、
今後はご自分で移植する努力をしたほうがいいと思います。
.NET Framework上ではC++よりもC#の方がサンプルが多いので。
#とかいいつつ、自分はまだC#やったことないけど(^^;

今Tips見てたら、そのものズバリがあるではないですか!!

C#のコードをVB.NETへ変換する
http://dobon.net/vb/dotnet/links/convertcs2vb.html

というわけで、まぁ〜自力で移植しちゃったのでそのままにしますが、
今度からは参照して勉強してください。

-----
Imports System.Drawing.Imaging

Public Class Class1

    Private Shared Function GetEncoderInfo(ByVal mimeType As String) As ImageCodecInfo
        Dim j As Integer
        Dim encoders As ImageCodecInfo()
        encoders = ImageCodecInfo.GetImageEncoders
        For j = 0 To encoders.Length
            If (encoders(j).MimeType = mimeType) Then
                Return encoders(j)
            Else
                Return Nothing
            End If
        Next
    End Function

    Private Sub WriteNewDescriptionInImage(ByVal Filename As String, ByVal NewDescription As String)
        Dim Pic As Image
        Dim PropertyItems As PropertyItem()
        Dim bDescription(NewDescription.Length) As Byte
        Dim i As Integer
        Dim FilenameTemp As String
        Dim Enc As Encoder = Encoder.Transformation
        Dim EncParms As EncoderParameters = New EncoderParameters(1)
        Dim EncParm As EncoderParameter
        Dim CodecInfo As ImageCodecInfo = GetEncoderInfo("image/jpeg")

        '// copy description into byte array
        For i = 0 To i < NewDescription.Length
            '** Char型→Byte型へのキャストはVBではできない。Unicodeのため。
            '** 文字列内の1文字をByte型にすると言うことは、Valだと思われ。
            bDescription(i) = Val(NewDescription.Chars(i))
        Next

        '// load the image to change
        Pic = Image.FromFile(Filename)

        '// put the new description into the right property item
        PropertyItems = Pic.PropertyItems
        PropertyItems(0).Id = &H10E '// 0x010e as specified in EXIF standard
        PropertyItems(0).Type = 2
        PropertyItems(0).Len = NewDescription.Length
        PropertyItems(0).Value = bDescription
        Pic.SetPropertyItem(PropertyItems(0))

        '// we cannot store in the same image, so use a temporary image instead
        FilenameTemp = Filename + ".temp"

        '// for lossless rewriting must rotate the image by 90 degrees!
        EncParm = New EncoderParameter(Enc, CLng(EncoderValue.TransformRotate90))
        EncParms.Param(0) = EncParm

        '// now write the rotated image with new description
        Pic.Save(FilenameTemp, CodecInfo, EncParms)

        '// for computers with low memory and large pictures: release memory now
        Pic.Dispose()
        Pic = Nothing
        GC.Collect()

        '// delete the original file, will be replaced later
        System.IO.File.Delete(Filename)

        '// now must rotate back the written picture
        Pic = Image.FromFile(FilenameTemp)
        EncParm = New EncoderParameter(Enc, CLng(EncoderValue.TransformRotate270))
        EncParms.Param(0) = EncParm
        Pic.Save(Filename, CodecInfo, EncParms)

        '// release memory now
        Pic.Dispose()
        Pic = Nothing
        GC.Collect()

        '// delete the temporary picture
        System.IO.File.Delete(FilenameTemp)

    End Sub

End Class

-----
P.S.
このクラスをそのまま、ファイルに落としても動かないと思います。
元情報をそのまま移植したため。お勉強お勉強w
■No3385に返信(どらごらさんの記事)
どらごらさん、ありがとうございます。

> C#のコードをVB.NETへ変換する
> http://dobon.net/vb/dotnet/links/convertcs2vb.html
あちゃやってしまいました。あるんですね〜
>
> というわけで、まぁ〜自力で移植しちゃったのでそのままにしますが、
> 今度からは参照して勉強してください。
分かりました。
>
動作確認いたしましたが、やっぱり劣化しますね〜
Exif情報取得できてもエンコーダー使ってしまっては意味なですものね
バイナリで操作してエンコーダーをバイパスするか、
IJG(http://www.ijg.org/)のソースをコンパイルて使用するしか無さそうです。

いじった所はIF文ぐらいで動くようになりました。
> Private Shared Function GetEncoderInfo(ByVal mimeType As String) As ImageCodecInfo
> Dim j As Integer
> Dim encoders As ImageCodecInfo()
> encoders = ImageCodecInfo.GetImageEncoders
> For j = 0 To encoders.Length
> If (encoders(j).MimeType = mimeType) Then
> Return encoders(j)
> 'Else
> ' Return Nothing
> End If
> Next
> Return Nothing
> End Function
>
> いじった所はIF文ぐらいで動くようになりました。

紹介していただいた私のページにも書いてありますが、配列の要素数も間違えて変換されます(というより、変換されません)。

例えば、
byte[] bDescription=new Byte[NewDescription.Length];

Dim bDescription(NewDescription.Length) As Byte
と変換されますが、実際は、
Dim bDescription(NewDescription.Length - 1) As Byte
ですね。
■No3433に返信(管理人さんの記事)

管理人さん、ありがとうございます
> 例えば、
> byte[] bDescription=new Byte[NewDescription.Length];
> は
> Dim bDescription(NewDescription.Length) As Byte
> と変換されますが、実際は、
> Dim bDescription(NewDescription.Length - 1) As Byte
> ですね。
そうだったのですか、、、本当に勉強不足だなこりゃ

動作確認中間報告

一回だけ回転した時は40K〜50Kぐらいデータ容量が落ちます。
数回回転した場合1度落ちたデータ容量が回転するたびに10K〜20Kぐらいづつ
増えていきます。(なんじゃこりゃ〜)
ですが画像そのものは5〜6回、回転して画像を250%拡大して元画像と見比べ
ましたがそんなに劣化は起こしていないようです。
その後

画像回転時のファイルサイズの変動がいまいち把握出来ないので
IJGのソースをコンパイルして使用することにしました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板