/* imgresiz for Sharp SLA300, B500, C7x0 Linux PDA Copyright (C) 2003-2004 Joe Kanemori. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* 2003/10/03 imgresiz 0.01 2003/10/04 imgresiz 0.02 以下のオプションを指定可能に。 ・変換サイズ ・画像品質 ・prefix文字 2004/06/15 imgresiz 0.1 ・リサイズ後画像のジャギーを軽減 2004/06/17 imgresiz 0.2 以下の機能を追加 ・フィルター強度 ・回転 ・サイズの縮尺指定 2004/07/08 imgresiz 0.5 ・縮小ロジック独自実装(面積平均化法) 2004/07/09 imgresiz 0.65 ・アンシャープマスク実装 2004/07/09 imgresiz 0.65a ・メッセージ出力をstderrに 2004/07/14 imgresiz 0.7 ・縮小ロジックの改善 2004/07/15 imgresiz 0.8 ・Exif情報の保持 2004/07/18 imgresiz 1.0 ・パラメータファイル指定 */ #include #include #include #include #include #include #include #include #include #include #include #include int filter[3][3]; bool argchk(int idx, int argc, QStringList& argv, QString chkstr, double& val) { if (argv[idx] == chkstr) { if (idx + 1 < argc) { QString cvbuf = argv[idx + 1]; bool flg; val = cvbuf.toDouble(&flg); return flg; } } return false; } bool argchk(int idx, int argc, QStringList& argv, QString chkstr, int& val) { if (argv[idx] == chkstr) { if (idx + 1 < argc) { int mul = 1; QString cvbuf = argv[idx + 1]; if (chkstr == "-w" || chkstr == "-h") { int pos = cvbuf.find('%'); if (-1 != pos) { cvbuf = cvbuf.left(pos); mul = -1; } } bool flg; val = mul * cvbuf.toInt(&flg); return flg; } } return false; } bool argchk(int idx, int argc, QStringList& argv, QString chkstr, QString& val) { if (argv[idx] == chkstr) { if (idx + 1 < argc) { val = argv[idx + 1]; return true; } } return false; } //------------------------------------------------------------------------------ //Pixel情報の抽出 //------------------------------------------------------------------------------ QRgb getPixel(QImage src, int x, int y, QRgb org) { int w = src.width(); int h = src.height(); if (0 > x || 0 > y || x >= w || y >= h) { return org; } return src.pixel(x, y); } //------------------------------------------------------------------------------ //EXIF情報コピー //------------------------------------------------------------------------------ void CopyExif(QString src, QString dst, QDir dir) { char hdr[4]; char* exifbuf = NULL; uint exiflen = 0; QFile fsoc(src); QFileInfo fisoc(fsoc); QFile fdst(dst); QFileInfo fidst(fdst); if ((fisoc.extension().lower() == "jpg" || fisoc.extension().lower() == "jpeg") && (fidst.extension().lower() == "jpg" || fidst.extension().lower() == "jpeg")) { fsoc.open(IO_ReadOnly); int pos = 2; while (1) { if (false == fsoc.at(pos)) { exiflen = 0; break; } fsoc.readBlock(hdr, sizeof(hdr)); uint len = (hdr[2] & 0x0ff) * 256 + (hdr[3] & 0x0ff); if (hdr[0] == (char)0xff && hdr[1] == (char)0xda) { break; } else if (hdr[0] == (char)0xff && hdr[1] == (char)0xe1) { exiflen = len + 2; fsoc.at(pos); exifbuf = new char[exiflen]; fsoc.readBlock(exifbuf, exiflen); break; } pos += 2 + len; } fsoc.close(); if (0 < exiflen) { fprintf(stderr, " (copy exif) "); QFileInfo fi(dir, "__imgresiz.tmp.jpg"); QFile f(fi.filePath()); if (f.open(IO_ReadWrite | IO_Append)) { try { if (fdst.open(IO_ReadOnly)) { if (-1 == f.putch(0xff)) { throw "exif tmpfile write error.(header1)"; } if (-1 == f.putch(0xd8)) { throw "exif tmpfile write error.(header2)"; } if (-1 == f.writeBlock(exifbuf, exiflen)) { throw "exif tmpfile write error.(exif block)"; } char buf[1024]; fdst.at(2); while (!fdst.atEnd()) { int rdlen = fdst.readBlock(buf, sizeof(buf)); if (-1 == rdlen) { break; } if (-1 == f.writeBlock(buf, sizeof(buf))) { throw "exif tmpfile write error.(jpeg body)"; } } } f.flush(); } catch(const char* msg) { fprintf(stderr, "exception:%s\n", msg); } fdst.close(); f.close(); } delete[] exifbuf; QFile::remove(fidst.filePath()); rename(fi.filePath(), fidst.filePath()); } else { fprintf(stderr, " (no exif) "); } } } //------------------------------------------------------------------------------ //画像縮小(面積平均法) //------------------------------------------------------------------------------ void ReduceImage(QImage& src, QImage& dst, int w, int h) { dst = QImage(w, h, src.depth()); for (int y = 0; y < h; ++y) { int sy = y * src.height() / h; int sy2 = (y + 1) * src.height() / h; for (int x = 0; x < w; ++x) { int sx = x * src.width() / w; int sx2 = (x + 1) * src.width() / w; QRgb org = src.pixel(sx, sy); int r = 0; int g = 0; int b = 0; int ah = sy2 - sy; int aw = sx2 - sx; int div = ah * aw; if (0 != div) { for (int dy = 0; dy < ah; ++dy) { for (int dx = 0; dx < aw; ++dx) { QRgb c = getPixel(src, sx + dx, sy + dy, org); r += qRed(c); g += qGreen(c); b += qBlue(c); org = c; } } r /= div; g /= div; b /= div; QColor cl(r, g, b); org = cl.rgb(); } dst.setPixel(x, y, org); } } } //------------------------------------------------------------------------------ //アンシャープマスク //------------------------------------------------------------------------------ void UnsharpMask(QImage& src, QImage& dst, int mov, int rng, int sr) { src = dst; int R = 0; int G = 0; int B = 0; int cksr = sr * sr; int dv = 0; for (int dy = -sr; dy <= sr; ++dy) { for (int dx = -sr; dx <= sr; ++dx) { if (cksr >= dx * dx + dy * dy) { ++dv; } } } for (int y = 0; y < src.height(); ++y) { if (0 == y % 100) { fprintf(stderr, "."); } for (int x = 0; x < src.width(); ++x) { //中心点を取得する。 QRgb org = src.pixel(x, y); R = 0; G = 0; B = 0; for (int dy = -sr; dy <= sr; ++dy) { for (int dx = -sr; dx <= sr; ++dx) { if (cksr >= dx * dx + dy * dy) { QRgb rgb = getPixel(src, x + dx, y + dy, org); R += qRed(rgb); G += qGreen(rgb); B += qBlue(rgb); } } } int oR = qRed(org); int oG = qGreen(org); int oB = qBlue(org); int dR = oR - R / dv; int dG = oG - G / dv; int dB = oB - B / dv; if (rng < abs(dR)) { R = oR + dR * mov / 100; } else { R = oR - dR / 2; } if (rng < abs(dG)) { G = oG + dG * mov / 100; } else { G = oG - dG / 2; } if (rng < abs(dB)) { B = oB + dB * mov / 100; } else { B = oB - dB / 2; } if (0 > R) {R = 0;} else if (255 < R) {R = 255;} if (0 > G) {G = 0;} else if (255 < G) {G = 255;} if (0 > B) {B = 0;} else if (255 < B) {B = 255;} QColor cl(R, G, B); dst.setPixel(x, y, cl.rgb()); } } } //------------------------------------------------------------------------------ //フィルター処理 //------------------------------------------------------------------------------ void Filtering(QImage& src, QImage& dst, int div, int offset) { src = dst; int R = 0; int G = 0; int B = 0; QRgb org; for (int y = 0; y < src.height(); ++y) { if (0 == y % 100) { fprintf(stderr, "."); } for (int x = 0; x < src.width(); ++x) { //中心点を取得する。 org = src.pixel(x, y); R = 0; G = 0; B = 0; for (int dy = -1; dy <= 1; ++dy) { for (int dx = -1; dx <= 1; ++dx) { QRgb rgb = getPixel(src, x + dx, y + dy, org); R += qRed(rgb) * filter[dx+1][dy+1]; G += qGreen(rgb) * filter[dx+1][dy+1]; B += qBlue(rgb) * filter[dx+1][dy+1]; } } R = (int)(R / div + 0.5) + offset; G = (int)(G / div + 0.5) + offset; B = (int)(B / div + 0.5) + offset; if (0 > R) {R = 0;} else if (255 < R) {R = 255;} if (0 > G) {G = 0;} else if (255 < G) {G = 255;} if (0 > B) {B = 0;} else if (255 < B) {B = 255;} QColor cl(R, G, B); dst.setPixel(x, y, cl.rgb()); } } } //------------------------------------------------------------------------------ //メイン・ルーチン //------------------------------------------------------------------------------ int main( int argc, char ** argv ) { QPEApplication a(argc, argv); //QImageを使用するために宣言する。 int offset = 0; int width = 320; int height = 240; int quality = 50; //圧縮率 int div = 0; QString prefix = "sml_"; QString destdir = ""; QString imgfmt = "JPEG"; QString imgext = "jpg"; QString ws = ""; QString hs = ""; QString fs = "nofilter"; QString us = "nounsharp"; bool isExifCopy = false; int sr = 1; int rotate = 0; int rng = 0; int mov = 0; QStringList files; int val = 0; //fprintf(stderr, "imgresiz\n"); //引数チェック if (argc < 2) { QMessageBox::about(0, "imgresiz v1.0", "image resize/convert format\nassistant\ncopyright(c)2004,jojo3"); exit(0); } //パラメータファイル抽出 QStringList args; QString paramfile = ""; for (int i = 1; i < argc; ++i) { //fprintf(stderr, "%s\n", argv[i]); if (0 == strcmp(argv[i], "-param")) { if (i + 1 < argc) { FILE *fp = fopen(argv[i + 1], "rt"); if (NULL != fp) { char buf[1024]; while (!feof(fp)) { fgets(buf, sizeof(buf), fp); QStringList sl = QStringList::split(' ', buf); for (uint j = 0; j < sl.count(); ++j) { QString s = sl[j].stripWhiteSpace(); s = s.replace(QRegExp("\""), ""); args << s; } } fclose(fp); } ++i; } } else { args.append(argv[i]); } } //引数設定 argc = args.count(); for (uint i = 0; i < args.count(); ++i) { if (argchk(i, argc, args, "-q", val)) { if (100 < val) { val = 100; } else if (0 >= val) { val = 1; } ++i; quality = val; } else if (argchk(i, argc, args, "-param", paramfile)) { //paramfileを読み飛ばす ++i; } else if (argchk(i, argc, args, "-rng", val)) { rng = val; ++i; } else if (argchk(i, argc, args, "-mov", val)) { mov = val; ++i; } else if (argchk(i, argc, args, "-sr", val)) { if (0 < val) { sr = val; } ++i; } else if (argchk(i, argc, args, "-f", fs)) { QFileInfo info(fs); if (info.isReadable()) { FILE *fp = fopen((const char*)fs, "rt"); char buf[1024]; fgets(buf, sizeof(buf), fp); div = QString(buf).toInt(); fgets(buf, sizeof(buf), fp); offset = QString(buf).toInt(); for (int y = 0; y < 3; ++y) { fgets(buf, sizeof(buf), fp); QStringList sl = QStringList::split(",", buf); for (uint x = 0; x < 3; ++x) { filter[y][x] = sl[x].toInt(); } } fclose(fp); if (0 == div) { fs = "nofilter"; } ++i; } else { fs = "nofilter"; } } else if (argchk(i, argc, args, "-r", val)) { ++i; rotate = val; } else if (argchk(i, argc, args, "-w", val)) { ++i; if (0 > val) { width = -val; ws = "%"; } else { width = val; } } else if (argchk(i, argc, args, "-h", val)) { ++i; if (0 > val) { height = -val; hs = "%"; } else { height = val; } } else if (argchk(i, argc, args, "-p", prefix)) { ++i; } else if (args[i] == "-exif") { isExifCopy = true; } else if (argchk(i, argc, args, "-d", destdir)) { ++i; } else if (argchk(i, argc, args, "-fmt", imgfmt)) { imgext = imgfmt; if (imgext == "JPEG") { imgext = "jpg"; } else { imgext = imgfmt.lower(); } ++i; } else { files << args[i]; } } //設定情報表示 if (0 != mov) { us = "rng=" + QString::number(rng) + " mov=" + QString::number(mov) + " sr=" + QString::number(sr); } fprintf(stderr, "size(%d%s, %d%s) rotate(%d) %s %s quality(%d\%) prefix[%s] dir[%s] format[%s] \n", width, (const char*)ws, height, (const char*)hs, rotate, (const char*)fs, (const char*)us, quality, (const char*)prefix, (const char*)destdir, (const char*)imgfmt ); fprintf(stderr, "converting...\n"); //加工処理 for (uint i = 0; i < files.count(); ++i) { QImage src; QFileInfo info(files[i]); if (true == info.isFile() && true == info.isReadable()) { fprintf(stderr, "%s", (const char*)files[i]); if (true == src.load(info.filePath())) { QDir dir = info.dir(); if (destdir != "") { dir.setPath(destdir); } QString fname = info.fileName(); QString ext = info.extension(); if (ext != "") { fname.truncate(fname.findRev(ext)); } fname += imgext; info.setFile(dir, prefix + fname); QImage dst; //リサイズ処理 int w = width; int h = height; if (ws == "%") { w = src.width() * width / 100; } if (hs == "%") { h = src.height() * height / 100; } if (width == 100 && ws == "%" && height == 100 && hs == "%") { dst = src; } else { if (w < src.width() && h < src.height()) { //面積平均法 ReduceImage(src, dst, w, h); } else { //QT/Eまかせ dst = src.smoothScale(w, h); } } //回転 if (0 != rotate % 360) { QPixmap pm; pm = dst; QWMatrix m; m.rotate(rotate); // 座標系の回転 dst = pm.xForm(m).convertToImage(); } //アンシャープマスク if (0 != mov) { UnsharpMask(src, dst, mov, rng, sr); } //フィルター処理 if (fs != "nofilter") { Filtering(src, dst, div, offset); } //結果保存 if (true == dst.save(info.filePath(), imgfmt, quality)) { if (isExifCopy) { CopyExif(files[i], info.filePath(), dir); } fprintf(stderr, " succeed.\n"); } else { fprintf(stderr, " save failed.\n"); } } else { fprintf(stderr, " load failed.\n"); } } } fprintf(stderr, " completed.\n"); exit(0); }