ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

Perlコミュのとほほ、perl初心者

  • mixiチェック
  • このエントリーをはてなブックマークに追加
ImageMagickを使用して、画像に文字を書き込みをたいのですが、うまくいかないです。
どなたか、ご教授を御願い致しますexclamation ×2

やりたい事
htmlからcgiを呼び出して、cgiにて画像取得、画像書き込み、htmlに画像を出力する流れです。

エラーの内容
htmlの画像が取得できず、×の画像になる。
その画像を保存すると、gazou.cgi.jpgになっている。

確認出来ていること
logとperl -wc コマンドにて cgi事態にエラーはさそう。
画像ファイルのパスも間違いなさそう。

下記は実際のコードです。

呼び出し元のHTMLで、cgiを呼び出している箇所。

☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

<img src="../../gazou.cgi" />

☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆


gazou.cgiのコード

☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

#!/usr/bin/perl

use Jcode;
use Image::Magick;
use LWP::Simple;


open(OUT, ">> debug.log");
print(OUT "START");
close(OUT);

#メッセージを出力のパラメーター
#文字サイズ
$psize = "18";
#文字サイズ:黒
$fill = "#000000";
#文字エンコード
$enc = "utf8";

# フォントの指定
$font = "APJapanese.ttf";

# 画像を指定
$file = "../../img/def/1.jpg";

my $image = makeImage();

# 画像出力
print "Content-type: image/jpeg\n\n";
binmode STDOUT;
$image->Write('jpeg:-');
undef $image;
exit;

sub makeImage {

$text1 = "test";
# UTF-8へ変換
$text1 = jcode($text1)->utf8;

# オブジェクト作成
$image = Image::Magick->new;

# 画像読み込み
$image->Read($file);
$image->Minify();

# 文字を記入する
#名前
$image->Annotate(text=>$text1,x=>60, y=>330, fill=>$fill, pointsize=>$psize, encoding=>$enc, font=>$font );

return $image;
}

よろしく御願い致します。

コメント(21)

print "Content-type: image/jpeg\n\n";
をコメントアウトして、CGI としてではなく通常のコマンド実行するとどうなるか確認してみては?

perl gazou.cgi > hoge.jpg
>くっきーさん
コメントアウトして、実行したら。

Can't call method "Write" without a package or object reference at gazou.cgi line 36
となりました。
このソースのスタイルを最大限活かすとしたら、
my $image = makeImage();
これを、
makeImage();
に変えればよさそうな気がします。
sub makeImage()内で$imageは局所化されておらずグローバル変数扱いになるはずですので、サブルーチンからreturnしなくても、生成された$imageは有効です。

なお、これをベースにもっと色々と追加する予定があるのであれば、局所化する変数、そうでない変数の区別がきちんとなるように書き換えた方が良いと思います。
>あささんへ
変数の区別を勉強してときますね。

えーと、
my $image = makeImage();
これを、
makeImage();
に変えてみたのですが、結果が同じでした。

まず第一の疑問が、失敗した画像名が
gazou.cgi.jpgになっていること。
”jpg”がなぜついてしまうのかが疑問です。
> マナミ☆☆☆ さん
インターネットエクスプローラの設定で Content-type: image/jpeg であるファイルは保存時に勝手に拡張子に jpg が追加されるので、それは今回の問題とは全く関係がありません。
perl のプログラムで $image がオブジェクトになっていないのが問題になっています。

ImageMagickは凄いすぐれてますよね。 僕も頻繁にWEBアプリなどで使用します。
上記コードでまず気になる点として、フォントを指定していますが、そのフォントはサーバーにインストールされてますか?
font=>$font
を削除して実行すればうまくいくような気がする……
use strictを使うことを強くお勧めします。
(ImageMagickとはぜんぜん関係ないですが・・・)
fontの場所はサーバ内のフルパスで指定します。

ちなみにubuntuでIPAゴシックとか入れてると、

/var/lib/defoma/gs.d/dirs/fonts/ipag.ttf

だったりします。
> に変えてみたのですが、結果が同じでした。

結果が同じというのは、2と同じエラーが出ているということですね?
ということは$imageがグローバル変数として扱われていない事を意味しています。
そうするとサブルーチンをやめてベタで書き直してみるのが手っ取り早いと思います。
実際に動作するコードを自分で書いてみました。



#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use String::Random;
use Imager;
use IO::All;

my $text = qq{日本語ですよ};
my $file = './test1.jpg';

my $name = String::Random->new->randregex('[A-Za-z0-9]{32}') . '.jpg';

my $img = Imager->new;
$img->read(
file => $file,
type => 'jpeg',
) or die $img->errstr();

my $font = Imager::Font->new(
file => '/var/lib/defoma/gs.d/dirs/fonts/ipagp.ttf',
) or die $img->errstr();

$img->string(
font => $font,
size => '40',
utf8 => '1',
string => $text,
x => '8',
y => '48',
color => '#000000',
) or die $img->errstr();

$img->write(
file => $name,
) or die $img->errstr();

my $result < io($name);

print qq{Content-Type: image/jpeg\n\n};
print $result;
以前、実際に作成して使用したプログラムです。

#!/usr/bin/perl
use strict;
eval ("use Image::Magick;"); die "[err] Image::Magick がインストールされてません。 \n" if $@;

use less;
use Image::Magick;

die "Usage: $0 image_file [ image_name ]\n" unless @ARGV >= 1 && @ARGV <= 2;
my $image_file = shift (@ARGV);
my $image_name = shift (@ARGV);
($image_name = $image_file) =~ s|.*/|| if !defined $image_name;

my %mime_map = (
"gif" => "image/gif",
"jpg" => "image/jpeg",
"jpeg" => "image/jpeg",
"jpe" => "image/pjpeg",
"png" => "image/png"
);
my $mime_type = $mime_map{lc ($1)} if $image_file =~ /\.([^.]+)$/;
die "Cannot determine image MIME type\n" if !defined $mime_type;


my ($err, $image_data, $sample_image_data);
my $ORIGINAL_TEXT = 'sample';
my $FONT = '/home/tmp_work/sazanami-gothic.ttf';
my $STROKE = 'none';
my $FILL = 'grey';
my $UNDERCOLOR = 'none';
my $WIDTH = '20';
my $HEIGHT = '20';
my $code = '';

use Jcode;
my $text = $ORIGINAL_TEXT; #'utf8' eq $code ? jcode($ORIGINAL_TEXT)->utf8 : $ORIGINAL_TEXT;

#**********************************
# 画像の読みこみとデータ化
#**********************************
my $img = Image::Magick->new();
$err = $img->Read ($image_file);
die "Can't read image file: $err\n" if $err;
$image_data = $img->ImageToBlob ();

#**********************************
# 画像合成とデータ化
#**********************************
my $image = Image::Magick->new();
my ($label);
$label = Image::Magick->new(size=>"$WIDTH x $HEIGHT",pointsize=>20,font=>$FONT);
$label->Read($image_file);
$label->Annotate(
text=>$text,
gravity=>"north",
fill=>$FILL,
stroke=>$STROKE,
undercolor=>$UNDERCOLOR,
);

push (@$image, $label);
$image->Scale(geometry=>"60");
$sample_image_data = $image->ImageToBlob();

## ここからDBに格納したり…


exit (0);
> Can't call method "Write" without a package or object reference at gazou.cgi line 36
このエラーから判断すると,
makeImage() から返ってきた値がImage::Magick の
オブジェクトではないように見えますね.
(Read ではなく Write でエラーになっているため)

試しに単純なものが動くかやってみてはいかがでしょうか?
------------------------------------------
#!/usr/bin/perl

use strict;
use warnings;

use Image::Magick;

my $image = Image::Magick->new();
my $fname = "1.jpg";
$image->Read($fname);
$image->Write("jpeg:-");
-------------------------------------------

ちなみに,保存する際のデフォルトのファイル名は,
Content-Disposition ヘッダで指定できるので,
例えば,以下のようにすることでファイル名を指定できると思います.
--
print(CGI->header(
 -type => 'image/jpeg',
 -attachment => 'aaa.jpg',
));
--

参考
- http://gist.github.com/302434
- http://www.studyinghttp.net/header#Content-Disposition
- http://perldoc.jp/docs/modules/CGI-2.89/CGI.pod
$image のスコープが makeImage の外に出ている件を気にされている方が多いようですが、今回の件に関しては勧められる方法ではないものの正常に動作するコードです。

> Can't call method "Write" without a package or object reference at gazou.cgi line 36
のエラーコードと、そこまで正常に動いていたことから推測すると、

$image->Annotate(text=>$text1,x=>60, y=>330, fill=>$fill, pointsize=>$psize, encoding=>$enc, font=>$font );

で $image が壊れた可能性が高いと思います。この命令で一番の不安要素は font=>$font なので、これを削除して動くかどうか確認するのがてっとり早いかと。
ここで指摘するのが適切かどうかは別として、トピ主に忠告。

・トピックのタイトルは、内容が把握しやすいようにしてください。
 mixiの構造上、一覧表示されるタイトルに依存して閲覧者はトピック内容を予想します。
 予想しづらいと、もし問題をすばやく解決するのに充分なスキルを持った人が居たとしても、その人が見逃す恐れがあります。
 早い話が、ウザいから「初心者です」みたいに書くな。

・ソースが長くなる場合は外部ファイルにアンカーを。
 ソースが長いとトピック画面が縦長になり、読みづらくなります。
 また、インデントに用いられるタブや半角スペースがそのままだと反映されません。
 半角スペースを にするなどしないといけません。
 インデントがないとソースが読みづらくなるので、問題の本質ではないところに手間取ることになりかねません。
 早い話が、回答者の貴重な時間資源を割いてもらいたいならそれなりの配慮をしろ。


前回の質問で誰か心優しき人がメッセージなどでこっそり注意してるかと思ったけどそれもないようなので。
あれまたmixiは仕様変えよったのか?
プレビューと投稿内容が変わるとか、それが細かいところだとしてもくそ過ぎる。

> 半角スペースを にするなどしないといけません。
正しくは、
半角スペースを&nbsp;にするなどしないといけません。

でも二重に処理が行われることを考えると、&amp;nbsp;じゃないとダメかもしれんなー。
>皆様へ
いろいろとご指導ありがとうございます泣き顔
至らない事が多すぎて、迷惑をお掛けしました。スイマセンあせあせ(飛び散る汗)

>にゃんこ【・ω・】さん
ご指摘ありがとうございます。
以後気をつけます。

いろいろ試しても結果が同じでした。
ですので、
単純なコードに修正して、動くか確認してみたのですが、それでもエラー画像が返却されてきます。

下記が実際のコードになります。

http://gist.github.com/302560/
> マナミ☆☆☆さん
普通に動きますね。ただし、
use CGI;
を書いたらですが……。最初に提示していただいたプログラムでは CGI モジュール使ってなかったような……。
> くっきーさん

use CGI;を追加したら、動きました。
ありがとうございます。

これで、無事解決できました。

皆様も色々とご指摘していただいて、どーもありがとうございましたわーい(嬉しい顔)
> マナミ☆☆☆ さん

無事解決できたようで良かったです.

> use CGI;を追加したら、動きました。
う.すみません,私の例で CGI モジュールを使っているのに
use CGI について言及していなかったのはマズかったですね.

精進します.
> マナミ☆☆☆さん
無事に動いてなによりです。cgi の設置前に
 > perl -c hoge.cgi
を実行することをオススメします。文法やスペルの単純ミスを指摘してくれます。
今回の場合、
 .... @INC ....
というようなエラーが出ていたと思います。これはライブラリが見つからないというエラーですので、なぜ動くのかよくわからないコードを使っているとき、use や require によるライブラリの指定が不足していることを意味します。CGI は権限指定のエラーや http サーバーエラーなど perl 以外のエラーが頻発するので、問題を切り分ける方法を身につけておくと、開発が楽になりますよ。

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

Perl 更新情報

Perlのメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング