• ベストアンサー

BMP画像の画像データ本体をテキストデータとして保存したい

BMP画像を読み込んで、ヘッダ箇所などの本体と関係ない部分を読み飛ばして、画像本体をテキストファイルとして保存するプログラムを作ったのですが、実際の画像の大きさ(512x480)と出力結果(質問箇所最下に記載)の画像の大きさが異なってしまいます。あまり画像に詳しくないので、どこが間違っているのか分かりません。どこを修正すべきか教えてください。以下が作成したプログラムです。 public class bmpTotxt {  public static void main(String[] args){   try {    FileReader fr = new FileReader("./1.bmp");    BufferedReader br = new BufferedReader(fr);    FileWriter fw = new FileWriter("./pic.txt");    int i,count=0,len=0,width=512*4,height=0;    while((i = br.read()) != -1){     /* 画像本体箇所なら */     if(len >= 54){      fw.write(i+" ");      len++;      count++;            if(count == width){        fw.write("\n");        height++;        count = 0;      }     }     else{       len++;     }    }    System.out.println("ヘッダ長:"+54);    System.out.println("画像データ長:"+(len-54));    System.out.println("width:"+width/4);    System.out.println("height:"+height);    fr.close();    fw.close();   }   catch (Exception e) {    e.printStackTrace();   }  } } /* 出力結果   ヘッダ長:54   画像データ長:648480   width:512   height:316 */

  • Java
  • 回答数3
  • ありがとう数3

質問者が選んだベストアンサー

  • ベストアンサー
  • noocyte
  • ベストアンサー率58% (171/291)
回答No.3

> 66 77 54 64 11 > 0 0 0 0 0 54 > 0 0 0 40 0 0 > 0 0 2 0 0 224 > 1 0 0 1 0 24 > 0 0 0 0 0 0 > 64 11 0 > 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 > 0 0 0 > 画像データが特殊なんでしょうか? 10進ダンプを見る限り,画像データは「全く正常」です. (バイナリファイルをダンプするときは,10進じゃなくて16進にしてくださいね.  変換が面倒だから.(苦笑)) BITMAPFILEHEADER  bfType = 0x4D42 = 'B' | ('M' << 8);  bfSize = 0x000B4036 = 737334  bfReserved1 = 0x0000 = 0  bfReserved2 = 0x0000 = 0  bfOffBits = 0x00000036 = 54 BITMAPINFOHEADER  biSize = 0x00000028 = 40  biWidth = 0x00000200 = 512  biHeight = 0x000001E0 = 480  biPlanes = 0x0001 = 1  biBitCount = 0x0018 = 24  biCompression = 0x00000000 = 0 = BI_RGB  biSizeImage = 0x000B4000 = 737280  biXPelsPerMeter = 0x00000000 = 0  biYPelsPerMeter = 0x00000000 = 0  biClrUsed = 0x00000000 = 0  biClrImportant = 0x00000000 = 0 > biWidth = 0 > biBitCount = 24.0 > bfOffbits = 54 > 1行バイト数(width) = 0 > > と計算され、1行バイト数が0となり、うまくいきません。 > 画像データが特殊なんでしょうか? biWidth のバイト列は10進ダンプ中の "0 2 0 0" の部分ですが, ちゃんと4バイト読んで biWidth に変換していますか? たぶん引数なしの FileInputStream.read() で最初の1バイト (0) だけを読んで, それをそのまま biWidth に代入しているだけなんじゃないんですか?  FileInputStream in = new FileInputStream(…);  biWidth = in.read(); // 誤り biBitCount (2バイト) も bfOffBits (4バイト) も,2バイト目 (以後) が すべて0なので,ちゃんと読めてるように見えるだけで,もしこれらの値が 256以上になったら biWidth と同じバグが発症します. (実際には biBitCount も bfOffBits もそういう値になる可能性はほとんど  ないので発症しないだけ.) 複数バイト整数を読むには,そのバイト数だけ (引数なしの) read() を呼んで それらの戻り値を一つの整数に変換するか,read(byte [] b, int off, int len) を使って複数バイトをまとめてバイト配列に読み込んで整数に変換しなきゃ. ●参考  FileInputStream in = new FileInputStream(…);  byte [] buffer = new byte[4]; // 2または4バイト整数読み込み用バッファ  (途中略)  // biWidth (4バイト) を読む.  int biWidth = readInteger(in, buffer, 4);  // biHeight (4バイト) を読む.  int biHeight = readInteger(in, buffer, 4);  // biPlanes (2バイト) を読む.  short biPlanes = (short)readInteger(in, buffer, 2);  (以下略)  //------------------------------------------------------------------------  // 機能 :nバイト整数 (リトルエンディアン) を読む (n≦4).  // 入力 :(1) in:バイナリファイルからの入力ストリーム.  //     (2) buffer[0 ~ n-1]:読み込み用バッファ.  //     (3) n (≦4):読み込む整数のバイト長.  // 戻り値:読み込んだ整数値.  //------------------------------------------------------------------------  private static int readInteger(FileInputStream in, byte [] buffer, int n)   throws IOException, InvalidBmpFileException {   // buffer[] にnバイト読み込む.   if(in.read(buffer, 0, n) < n) {    // ファイルが途中で終わっている場合:不正な BMP ファイル    throw new InvalidBmpFileException();   }   // buffer[0 ~ n-1] を整数値に変換して返す.   return convertLittleEndian(buffer, 0, n);  }  //------------------------------------------------------------------------  // 機能 :nバイトのバイト列 bytes[index ~ index+(n-1)] を整数に変換する.  //     (リトルエンディアン)  // 戻り値:変換後の整数値.  //------------------------------------------------------------------------  private static int convertLittleEndian(byte [] bytes, int index, int n) {   int value = 0; // 変換後の整数値   int i = index + n;   while(--i >= index) {    // byte 型は符号付なので,符号拡張を防ぐため 0xFF でマスクする.    value = (value << 8) | (bytes[i] & 0xFF);   }   return value;  }

marucha
質問者

お礼

回答ありがとうございました。 ご指摘された点が間違っていたようでした。 以上の点を修正して完成させたいと思います。 詳細な回答ありがとうございました。

その他の回答 (2)

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.2

> 小さな画像(16x16や61x26)についてはうまく動作しました。 > しかし、512x480のような大きな画像については、うまくいきません。 もし幅または高さが256以上の画像の場合にうまくいかないということであれば,たぶん… ヘッダ構造体のほとんどのメンバは複数 (2または4) バイト整数ですが, その読み込み方法は理解してますか? 「リトルエンディアン」って知ってます? ・ヘッダが数種類存在するのは #1 さんのおっしゃるとおりですが,  普通は BITMAPINFOHEADER だけを想定しておけば十分なはずです.  (今時 BITMAPCOREHEADER を使っている BMP ファイルは見たことがないし,   BITMAPV4HEADER,BITMAPV5HEADER は BITMAPINFOHEADER の拡張 (上位互換) なので.) ・ビットマップデータ本体は,ヘッダの直後から始まるとは限らない点に注意.  ヘッダの直後にカラーテーブルが存在する場合もあるし,ヘッダ (+カラーテーブル)  の後に (無駄な) 隙間が存在する可能性も (仕様上は) あります.  正しいビットマップデータの開始位置は BITMAPFILEHEADER.bfOffBits. BMPファイルのフォーマット http://www5d.biglobe.ne.jp/~noocyte/Programming/Windows/BmpFileFormat.html

marucha
質問者

補足

とりあえず、読み出したヘッダ情報は以下のようになりました。 読み出した画像は、512x480。 66 77 54 64 11 0 0 0 0 0 54 0 0 0 40 0 0 0 0 2 0 0 224 1 0 0 1 0 24 0 0 0 0 0 0 64 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ここから、 biWidth = 0 biBitCount = 24.0 bfOffbits = 54 1行バイト数(width) = 0 と計算され、1行バイト数が0となり、うまくいきません。 画像データが特殊なんでしょうか? ここ↓ を参考にプログラムをつくりました。 http://www.snap-tck.com/room03/c02/cg/cg02_02.html

  • Bonjin
  • ベストアンサー率43% (418/971)
回答No.1

まず、BMPファイルは「バイナリファイル」と呼ばれる類のファイルです。これを「テキストファイル」を読み込むためのFileReaderで読み込んでいること自体が間違いです(JavaDocをちゃんと読みましょう)。FileInputStreamやRandomAccessFileを使ってファイルを読み込んでください。 あと、どこからBMPファイルのファイルフォーマットの情報を得たのか知りませんが、BMPファイルのヘッダ情報は常に54バイトなわけではありません。BMPのバージョン等によって変わります。データ開始のオフセットはヘッダ情報から取得するようにしてください。

marucha
質問者

補足

アドバイス通りに  FileInputStreamに変更  データ開始のオフセットをヘッダ情報から取得  1行バイト数=int([int({biWidth×biBitCount+7}/8)+3]/4)×4 と変更しましたところ、小さな画像(16x16や61x26)についてはうまく動作しました。 しかし、512x480のような大きな画像については、うまくいきません。 おそらくヘッダ情報からうまく取り出せていないものと思われますが、 画像の大きさが変化するとヘッダ情報のオフセットは変化するんでしょうか?

関連するQ&A

  • ファイル操作 ファイルにTABを入れる

    ファイルの入出力をやっています。一文字を読み込んだ後にOUTPUTファイルにカンマの代わりにTABキーを入れ込みたいのですが、それがどうしてもうまくいきません・・・ public class FileRead { // ファイル名 private static final String INFILE_NAME = "input01.csv"; private static final String OUTFILE_NAME = "output01.tsv"; public static void main(String[] args) { // FileReaderクラス宣言 BufferedReader fr = null; FileWriter fos = null; try { // FileReaderクラス生成 fr = new BufferedReader(new FileReader(INFILE_NAME)); fos = new FileWriter(OUTFILE_NAME); // 読み込み int i; while ((i = fr.read()) != -1) { //System.out.print((char)i); //char chra=(char)i; fos.write(i); } System.out.println("--- 読み込み完了 ---"); } catch (IOException e) { System.out.println("Exception : " + e); } finally { try { // FileReaderクラスを閉じる 続く・・・ 教えてください!!

  • java ファイル操作

    現在javaでファイル操作しファイルの文字列を検索したりファイルに文字列を追加したりする ものを作成しています。 現状のコードはこのような感じです。 class addresskensaku{ public static void main(String[] args)throws IOException{ System.out.println("数字を選択\n1.名前で検索\n2.年齢で検索\n3.出身地で検索\n4.追加"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = br.readLine(); char res = str.charAt(0); //1が選択された場合 if(res == '1' || res == '1'){ System.out.println("名前を入力してください。"); BufferedReader fn = new BufferedReader(new InputStreamReader(System.in)); String str1 = fn.readLine(); try{ FileReader freader = new FileReader("addressdata.txt"); BufferedReader breader = new BufferedReader(freader); String tmp = null; //検索 System.out.println("検索結果 :"); while((tmp=breader.readLine())!=null){ String[] words = tmp.split(","); //部分一致 if(words[0].indexOf(str1) != -1){ System.out.println(tmp); } } breader.close(); }catch(IOException e){ System.out.println("e"); } } //2が入力された場合 if(res == '2' || res == '2'){ System.out.println("年齢を入力してください。"); BufferedReader o = new BufferedReader(new InputStreamReader(System.in)); String ol = o.readLine(); try{ FileReader freader = new FileReader("addressdata.txt"); BufferedReader breader = new BufferedReader(freader); String tmp =null; //検索 while((tmp=breader.readLine())!=null){ String[] words = tmp.split(","); //完全一致 if (ol.equals(words[1])){ // System.out.println("検索結果:"); System.out.println(tmp); } } breader.close(); }catch(IOException e){ System.out.println("e"); } } //3が入力された場合 if(res == '3' || res == '3'){ System.out.println("出身地を入力してください。"); BufferedReader p = new BufferedReader(new InputStreamReader(System.in)); String pref = p.readLine(); try{ FileReader freader = new FileReader("addressdata.txt"); BufferedReader breader = new BufferedReader(freader); String tmp = null; //検索 while((tmp=breader.readLine())!=null){ String[] words = tmp.split(","); //部分一致 if(words[2].indexOf(pref) != -1){ System.out.println(tmp); } } breader.close(); }catch(IOException e){ System.out.println("e"); } } //4が入力された場合 追加 if(res == '4' || res == '4'){ //氏名入力 System.out.println("氏名を入力してください。"); BufferedReader n = new BufferedReader(new InputStreamReader(System.in)); String str2 = n.readLine(); //年齢入力 System.out.println("年齢を入力してください。"); BufferedReader o = new BufferedReader(new InputStreamReader(System.in)); String str3 = o.readLine(); //住所入力 System.out.println("住所を入力してください。"); BufferedReader a = new BufferedReader(new InputStreamReader(System.in)); String str4 = a.readLine(); //ファイルに書き込み try{ File file = new File("addressdata.txt"); FileWriter filewriter = new FileWriter(file, true); filewriter.write(str2+","+str3+","+str4+"\r\n"); filewriter.close(); }catch(IOException e){ System.out.println("e"); } } } } これをカプセル化し 各機能ごとにメソッドを分けて作ろうかなと思っています。 結果の出力はmainでやる予定です。

  • エラーの原因がわかりません

    JAVA初心者ですが、ファイル入力を行うプログラムを実行 すると、エラーが出ますが、そのエラーの原因が まったく見当がつきません。 誰か教えてください。 ------------読み込みデータ(loc.dat) 2 11 1 4 1 5 7 1 3 7 7 2 12 11 1 4 1 11 12 4 1 5 3 1 1 11 13 4 1 5 2 1 -------------プログラム import java.io.*; class ExReader {   public static void main(String[] args) { try { //読み込みファイル指定 FileReader fr = new FileReader("loc.dat"); BufferedReader br = new BufferedReader(fr); //ファイルから1行ずつ読み込む String s; while ((s = br.readLine()) != null) { System.out.println(s); } //読み込みファイルをクローズする br.close(); } catch (Exception e) { System.out.println("err: "+e); System.exit(1); } } } ---------------コンパイル結果 C:\JAVA\sim>javac ExReader.java ExReader.java:7: シンボルを解決できません。 シンボル: コンストラクタ FileReader (java.lang.String) 場所 : FileReader の クラス FileReader fr = new FileReader("loc.dat"); ^ ExReader.java:8: シンボルを解決できません。 シンボル: コンストラクタ BufferedReader (FileReader) 場所 : java.io.BufferedReader の クラス BufferedReader br = new BufferedReader(fr); ^ エラー 2 個

    • ベストアンサー
    • Java
  • CSVファイルを二次元配列に格納するプログラム

    前回のソースコードを改変してみました String Str = ""; int Count = 0; try { File inFile = new File("./res/test.csv"); FileReader fr = new FileReader(inFile); BufferedReader br = new BufferedReader(fr); while (br.ready()) { Str = br.readLine(); String[] temp = Str.split(","); int[] m = new int[temp.length]; for (int i = 0; i < temp.length; i++) { m[i] = Integer.parseInt(temp[i]); System.out.print(m[i] + ","); } System.out.println(); Count++; } br.close(); fr.close(); } catch (Exception e) { System.out.println("ファイル読み込みに関するエラー"); } 今一次元配列になっているのですが、これを二次元配列に代入したいのです Countで何回ループしたかによって二次元配列の縦の要素数としたかったのですが、 そうすると横の要素数になるループ中の一次元配列が使えないので困っています

  • BufferedReaderで配列のデータを取り出したい

    PrintWriterで配列のデータを書き込み、 BufferedReaderで書き込んだデータを呼び出したい のですが、下記のソースで書き込むと [Ljava.lang.String;@1add2dd のように表示されてしまいます。 テキストファイルに 1,Server1 のような感じで 書き込んで読み込むにはどのようにしたら いいでしょうか? すみません、ご教授下さい。 よろしくお願いします。 import java.io.*; public class IOTest { public static void main(String[] args) { try{ PrintWriter pr = new PrintWriter( new BufferedWriter(new FileWriter("test.txt")),true); String index = "1"; String serverName = "Server1"; String Server [] = {index, serverName}; pr.println(Server); System.out.println("登録したデータは:" + Server + "です。\n"); BufferedReader br = new BufferedReader( new FileReader("test.txt")); String line; while((line = br.readLine()) != null){ System.out.println("取り出したデータは:" + line + "です。"); } }catch(Exception e){ e.printStackTrace(); } } }

    • ベストアンサー
    • Java
  •  DataをtxtFileに書かせて、同じFileから読み込みです、エラーの意味が不明です

    JAVA初心者です、宜しくお願いします。  DataをtxtFileに書かせて、同じFileから読み込ませるコードですが、書き込みは正常に出来、(test1.txt)Fileも出来、内容もOKなのですが、同じFileからの読み込みがエラーになります。 一体どこが間違っているのでしょうか。 Fileは同じプロジェクトにおいています。(当然同一Fileからの読み込みです。) 諸兄の方々宜しくお願いします・・・正直泣いています。 public class Write_Read_070620{ public static void main(String args[]){ try{ PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("test1.txt"))); pw.println("Hello!!"); pw.println("Goodbye!!"); pw.close(); BufferedReader br0 = new BufferedReader(new FileReader("text1.txt")) ; String str0 = br0.readLine() ; System.out.println("入力データーを確認します\n" + " Team " + str0); br0.close(); } catch(IOException e){ System.out.println("入力エラ-です。\n "); } } }

    • ベストアンサー
    • Java
  • 数値化された値が欲しい

    作成して頂かなければ無いですが、RESULTファイルにある文字列(0~9)を数値に変換しようと思います。 この時、得た数値idtを2つ目にあるtry文の中のaDateFilterに入れたいのですが型の違いから上手く入れる事ができません。 どのようにidtから値を受け取るプログラムを作れば良いのか教えてください。お願いします。 //文字列から数値に変換するプログラム class ChStr { private String sa; int idt; public ChStr() { try { System.out.println("第1ステップ実行中・・・"); PrintWriter aDate = new PrintWriter(new BufferedWriter(new FileWriter("DateNum"))); File aDateFilter = new File("RESULT"); BufferedReader CFDF = new BufferedReader(new FileReader(aDateFilter)); while((sa = CFDF.readLine()) != null) { try { idt = Integer.parseInt(sa); System.out.println("数値=" + idt); //PrintWriter PWDF = new PrintWriter(new BufferedWriter(new FileWriter(aDateFilter))); //PWDF.println(sa); PWDF.close(); } catch(Exception e1) { System.out.println("Erorr1"); System.exit(1); } } } catch(Exception e2) { System.out.println("Erorr2"); System.exit(1); } System.out.println("数値化完了\n"); } }

  • プログラムの添削をお願いします。

    読み込んだテキストファイルを十文字ごとに改行して、別のファイルにコピーするプログラムです。 import java.io.*; public class CopyText{ public static void main(String[] args) throws IOException { FileReader in = new FileReader("inputtext.txt"); FileWriter out = new FileWriter("outputtext.txt"); int ccode, count; while ((ccode = in.read()) ! = -1){ out.write(ccode); count=count+1; if (count=10){fw.write('\n');, count=0}; } in.close(); out.close(); } } 7行目のところで、「')'がありません」「互換性のない型」というエラーが出るのですが、どう直したらいいのかがわからないのでご指導お願いします。

    • ベストアンサー
    • Java
  • javaでファイル操作し検索するものを作ってます

    目的はテキストファイル内の文字列に検索をかけヒットしたものを表示する というものです import java.io.*; class kensakuTest{ public static void main(String[] args)throws IOException{ System.out.println("検索方法を選択してください。\n0:学籍番号で検索\n1:名前で検索\n2:出身地で検索"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = br.readLine(); char res = str.charAt(0); //0が選択された場合 if(res == '0'){ System.out.println("学籍番号を入力してください。"); BufferedReader bn = new BufferedReader(new InputStreamReader(System.in)); String str0 = fn.readLine(); char num = str0.charAt(0); try{ FileReader freader = new FileReader("addressdata.txt"); BufferedReader breader = new BufferedReader(freader); String tmp = null; while((tmp=breader.readLine())!=null){ //完全一致 if(){ System.out.println("検索結果:"); System.out.println(tmp); } } breader.close(); }catch(IOException e){ System.out.println("e"); } } //1が選択された場合 if(res == '1'){ System.out.println("名前を入力してください。"); BufferedReader fn = new BufferedReader(new InputStreamReader(System.in)); String str1 = fn.readLine(); char name = str1.charAt(0); try{ FileReader freader = new FileReader("data.txt"); BufferedReader breader = new BufferedReader(freader); String tmp = null; //検索 //部分一致 while((tmp=breader.readLine())!=null){ if(tmp.indexOf(str1) > -1){ System.out.println("結果は:"); System.out.println(tmp); } } breader.close(); }catch(IOException e){ System.out.println("e"); } } //2が入力された場合 if(res == '2'){ System.out.println("出身地を入力してください。"); BufferedReader bl = new BufferedReader(new InputStreamReader(System.in)); String belong = bl.readLine(); try{ FileReader freader = new FileReader("addressdata.txt"); BufferedReader breader = new BufferedReader(freader); String tmp = null; while((tmp=breader.readLine())!=null){ //完全一致 if(){ System.out.println("検索結果:"); System.out.println(tmp); } } breader.close(); }catch(IOException e){ System.out.println("e"); } } } } 現状はここまでできましたが完全一致の検索のやり方がわかりません。 あと問題点があるのですが 読み取るファイルがこのような場合 2000,田中 太郎,福島県 2001,福島 太郎,東京都 名前で福島 と検索をかけたとします この場合答えが 2000,田中 太郎,福島県 2001,福島 太郎,東京都 と表示されてしまいます このカンマで区切られている学籍番号、名前、出身地をうまく分け それぞれ判別する方法はないのでしょうか? よろしくおねがいします。

    • ベストアンサー
    • Java
  • 入力(ファイル)の入力チェックにつきまして

    いつも参考にさせて頂いております、佐藤と申します。 入力ファイルの項目チェックを行おうとしておりましたが、 どうもコンパイルエラーが発生してしまいます。 原因が分かる方がいらっしゃいましたら、是非ともご教授願います。 以下がコーディングの一部となります。 FileReader text = new FileReader(args[0]); BufferedReader data = new BufferedReader(text); String check; FileWriter fw = new FileWriter(args[1]); while((check = data.readLine())!=null){ char[] src = check.toCharArray(); //第1~2パラメータをワークに代入 try{ n = Integer.parseInt(check[0]); n2=Integer.parseInt(check[1]); }catch(java.lang.NumberFormatException e){ System.out.println("第1~2パラメータが不正です"); return; } どうも、「//第1~2パラメータをワークに代入」の下でエラーとなっているようです。 エラーの内容は、「配列が要求されましたが、Java.lang.Stringが見つかりました」と表示されてしまいます。 どうか宜しくお願い致します。

専門家に質問してみよう