メモリ上での画像データの圧縮

16 10月 2010 Under: opencv2.x-samples

imencode,imdecode関数により,ファイルではなく,メモリ上で圧縮ファイルを書込,読込を行います.この関数では非可逆圧縮であるjpeg,可逆圧縮であるpngを指定可能で,それぞれ圧縮品質を指定出来ます.主に,ネットワークで画像を通信するときなどに利用します.この圧縮品質の指定方法はimwrite関数と共通です.
この関数の内部で使われているlibjpegを用いたその他の実装と符号化性能はこちら[link]

C++

#include <iostream>
#include <fstream>
#include <cv.h>
#include <highgui.h>
using namespace std;
using namespace cv;

double getPSNR(Mat& src1, Mat& src2, int bb=0);

int main(int argc, char** argv)
{
	Mat src = imread("lenna.png");

	//(1) jpeg compression
	vector<uchar> buff;//buffer for coding
	vector<int> param = vector<int>(2);
	param[0]=CV_IMWRITE_JPEG_QUALITY;
	param[1]=95;//default(95) 0-100

	imencode(".jpg",src,buff,param);
	cout<<"coded file size(jpg)"<<buff.size()<<endl;//fit buff size automatically.
	Mat jpegimage = imdecode(Mat(buff),CV_LOAD_IMAGE_COLOR);

	//(2) png compression
	param[0]=CV_IMWRITE_PNG_COMPRESSION;
	param[1]=3;//default(3)  0-9.
	imencode(".png",src,buff,param);
	cout<<"coded file size(png)"<<buff.size()<<endl;
	Mat pngimage = imdecode(Mat(buff),CV_LOAD_IMAGE_COLOR);

	//(3) intaractive jpeg compression
	char name[64];
	namedWindow("jpg");
	int q=95;
	createTrackbar("quality","jpg",&q,100);
	int key = 0;
	while(key!='q')
	{
		param[0]=CV_IMWRITE_JPEG_QUALITY;
		param[1]=q;
		imencode(".jpg",src,buff,param);
		Mat show = imdecode(Mat(buff),CV_LOAD_IMAGE_COLOR);

		double psnr = getPSNR(src,show);//get PSNR
		double bpp = 8.0*buff.size()/(show.size().area());//bit/pixe;
		sprintf(name,"quality:%03d, %.1fdB, %.2fbpp",q,psnr,bpp);
		putText(show,name,Point(15,50), FONT_HERSHEY_SIMPLEX,1,CV_RGB(255,255,255),2);
		imshow("jpg",show);
		key = waitKey(33);

		if(key =='s')
		{
			//(4) data writing 
			sprintf(name,"q%03d_%.2fbpp.png",q,bpp);
			imwrite(name,show);

			sprintf(name,"q%03d_%.2fbpp.jpg",q,bpp);
			param[0]=CV_IMWRITE_JPEG_QUALITY;
			param[1]=q;
			imwrite(name,src,param);;
		}
	}
}
double getPSNR(Mat& src1, Mat& src2, int bb)
{
	int i,j;
	double sse,mse,psnr;
	sse = 0.0;

	Mat s1,s2;
	cvtColor(src1,s1,CV_BGR2GRAY);
	cvtColor(src2,s2,CV_BGR2GRAY);

	int count=0;
	for(j=bb;j<s1.rows-bb;j++)
	{
		uchar* d=s1.ptr(j);
		uchar* s=s2.ptr(j);

		for(i=bb;i<s1.cols-bb;i++)
		{
			sse += ((d[i] - s[i])*(d[i] - s[i]));
			count++;
		}
	}
	if(sse == 0.0 || count==0)
	{
		return 0;
	}
	else
	{
		mse =sse /(double)(count);
		psnr = 10.0*log10((255*255)/mse);
		return psnr;
	}
}


(1)jpeg圧縮

encode関数の最後の引数である圧縮オプションを指定するために,CV_IMWRITE_JPEG_QUALITYとその品質(0-100,デフォルトが95)を設定します.数字が多いほど高品質です.encode関数の2番目の引数はvectorで宣言したバッファを入力し,そのサイズは,ちょうど圧縮されたデータのサイズに伸縮します.decode関数はvectorのバッファをMatでキャストして使用します.

(2)png圧縮

jpegの時とほぼ同じです.ただし,圧縮のオプションが,CV_IMWRITE_PNG_COMPRESSION,その圧縮率が, 0-9(デフォルトが3)に変わっています.pngの場合,圧縮率を変化させても画質は原画像とまったく同じままですが,圧縮時間が長くなります.

(3)インタラクティブなjpeg圧縮

jpegの圧縮品質をトラックバーで変更しながら確認します.終了のキーは’q'です.
表示されている数字は品質を指定した数字,その画像品質を表すPSNR,1ピクセル当たり何bitで表現できているかを表すbpp(bit/pixel)です.グレイスケールの時は,8bppのときカラー画像の時は24bppのときに,圧縮がされていないデータを表し,最高品質の例では約1/3に画像が圧縮されています.

(4)画像の保存

‘s’キーで,圧縮により劣化した画像をpngで可逆圧縮,また,原画像をjpegで非可逆圧縮した画像をそれぞれ保存します.
ファイルに書き出す場合は,imwriteでもこのように圧縮率を指定可能です.指定しない場合(この場合pngの圧縮)はデフォルト引数でのパラメータとなります.

“メモリ上での画像データの圧縮” への3件のコメント

  1. なかじ より:

    idojun様
    アドバイスありがとうございます。
    ご指摘の通り、リンカへの入力にRelease用のライブラリを指定していたため、DLL呼び出しが正しくなかったようです。

    しかしこの部分を修正した後も「アプリケーションの初期化に失敗しました」エラーで実行できません。
    PATH環境変数を確認したり、DLLファイルを実行ディレクトリにコピーしたり、CMakeでライブラリの再構築なども試したのですが、Debugモード・Releaseモードともに実行できませんでした。

    一度、OpenCV関係のものをアンインストールして最初からやり直してみます。

  2. idojun より:

    ほぼ同様の条件(VC2010+OpenCV2.1)で実験してみましたが,当方の環境では問題なく動作しているようです.
    気になるのは,

    呼び出し履歴の場所:
    highgui210.dll!1001ee22()
    

    のエラーで,本来ならば「highgui210.dll」ではなく「highgui210d.dll」が呼ばれるはずなので,
    ライブラリの指定が間違っており(正しくは,highgui210.lib ではなく highgui210d.lib),さらにDebug用のdllが作成されたフォルダにパスを通していない,などの問題があるのかもしれません.
    ご確認ください.

  3. なかじ より:

    画像処理の自分用ユーティリティを作りたくて、OpenCV2.1を導入しようとしています。

    VC++ 2010 Expressにて、このサンプルプログラムを実行しようとしたのですが、Debugモードでビルド・実行すると「場所 0x013c7000 を読み込み中にアクセス違反が発生しました。」というエラーが出てしまいます。

    Visual StudioでインクルードディレクトリとライブラリディレクトリのそれぞれにOpenCVの対応するディレクトリを設定し、「リンカー」→「入力」→「追加の依存ファイル」にもライブラリファイルを追加しました。
    Win32コンソールアプリのプロジェクトにて、上記のソースの一行目に #include “StdAfx.h”のみ追加したcppファイルを作って実行すると、
    20: imencode(“.jpg”,src,buff,param);
    を呼び出したところでエラーになります。

    自分なりに追いかけてみた様子は以下の通りです。

    12: Mat src = imread(“lenna.png”);
    では src に正しくデータが読み込めているようです。
    # ”lenna.png”と異なるPNGファイルを読み込ませた場合にも、srcのメンバ変数に正しい縦横サイズが入っていたので、読み込みはできているようです。

    15-18:は問題なく通過します。

    20: imencode(“.jpg”,src,buff,param);
    の部分で
    Visual Studioのデバッガが停止し、「利用可能なソースがありません」タブにて
     呼び出し履歴の場所:
      highgui210.dll!1001ee22()
    を表示して停止してしまいます。

    VC++の環境依存の設定で単純なミスをしているような気がするのですが、上手く問題点を見つけられません。

    解決のヒントでも教えていただれると助かります。
    よろしくお願いします。

コメントをどうぞ