[ImageMagick 学习] Fred's ImageMagick Scripts 转 C++ 的统一解决方案

曾经遇到这样一些问题:

  1. Fred's ImageMagick Scripts 提供的脚本,很好很强大,但是不能直接被C++程序调用,并且有license 问题,如果能转译成C++ 版就好了。怎么转译?
  2. Fred's ImageMagick Scripts 提供的脚本很丰富,如果在做“Shell 脚本-> C++代码”转译过程中,对这些脚本没有一个统一的解决方案,而是把每个脚本都当成一个特例来对待,那工作量是巨大的。怎样将转译的工作量降至最低而又不损失代码执行效率?

根据 ImageMagick 命令的中间操作结果 的介绍,答案是:

不要按照Fred's ImageMagick Scripts 脚本中的方法,多次调用convert 命令(即在C++代码中多次调用ConvertImageCommand() 函数),而是参考Fred's ImageMagick Scripts 每个特效的页面的最后一段文字中介绍的等价命令行,写出对应的命令行字符串,作为参数传给下面代码中的 IM_Convert() 函数。就这么简单!

例如,要消除mf-small.jpg 图片中的背景噪声,保存成mf-small-out.jpg,我们临摹 TEXTCLEANER 脚本等价命令行

convert \( $infile -colorspace gray -type grayscale -contrast-stretch 0 \) \
\( -clone 0 -colorspace gray -negate -lat ${filtersize}x${filtersize}+${offset}% -contrast-stretch 0 \) \
-compose copy_opacity -composite -fill "$bgcolor" -opaque none +matte \
-deskew 40% -sharpen 0x1 $outfile
即传给IM_Convert() 函数的字符串是:
convert ( mf-small.jpg -colorspace gray -type grayscale -contrast-stretch 0 ) ( -clone 0 -colorspace gray -negate -lat 25x25+10% -contrast-stretch 0 ) -compose copy_opacity -composite -fill \"white\" -opaque none +matte -deskew 40% -sharpen 0x1 mf-small-out.jpg
示例代码:

#include <wand/MagickWand.h>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;

void AllocArg(const char* cmd, int &argc, char** &argv) {
	istringstream issCmd(cmd);
	string sPara;
	vector<string> vParas;

	argc = 0;
	while(issCmd >> sPara) {
		vParas.push_back(sPara);
		++argc;
	}

	argv = (char**)malloc(argc * sizeof(char*));
	for(int i = 0; i < argc; ++i) {
		argv[i] = (char*)malloc(vParas[i].size() + 1);
		strcpy(argv[i], vParas[i].c_str());
	}
}

void FreeArg(int &argc, char** &argv) {
	if(argv != NULL) {
		for(int i = 0; i < argc; ++i) {
			free(argv[i]);
		}
		free(argv);
		argv = NULL;
	}
}

MagickBooleanType IM_Convert(const char* cmd) {
	char** argv = NULL;
	int argc = 0;
	AllocArg(cmd, argc, argv);

	ExceptionInfo *exception = NULL;
	ImageInfo *image_info = NULL;
 	MagickBooleanType status = MagickTrue;

	MagickCoreGenesis(*argv, MagickTrue);
	exception = AcquireExceptionInfo();
	image_info = AcquireImageInfo();

	status = MagickCommandGenesis(image_info, ConvertImageCommand, argc, argv, (char **) NULL, exception);

	image_info = DestroyImageInfo(image_info);
	exception = DestroyExceptionInfo(exception);
	MagickCoreTerminus();

	FreeArg(argc, argv);

	return(status);
}

int main(int argc, char* argv[])
{
	IM_Convert("convert ( mf-small.jpg -colorspace gray -type grayscale -contrast-stretch 0 ) "
		"( -clone 0 -colorspace gray -negate -lat 25x25+10% -contrast-stretch 0 ) "
		"-compose copy_opacity -composite -fill white -opaque none +matte "
		"-deskew 40% -sharpen 0x1 mf-small-out.jpg");
}

Makefile:

all:
        g++ -o im -g `pkg-config --cflags --libs MagickWand` main.cpp

clean:
        rm -f im

注意:

  1. 传给IM_Convert() 的参数字符串有可能也是动态生成的,比如里面的某些参数的取舍、参数值的计算等。对于这些动态因素,可以在IM_Convert() 的外面再封一层函数,用于取舍参数、计算参数值等,最终动态生成命令行字符串,传给IM_Convert()。喜欢玩设计模式的同学,可以在这里发挥特长了。
  2. 上面的动态生成命令行字符串,用stl 的stringstream 就好了。
  3. 其实这样做还是有些多余计算量的——动态生成命令行字符串、解析字符串、销毁解析出来的参数,不过这些消耗相比ConvertImageCommand() 函数本身的计算量,可以忽略不计了。
  4. 既然ConvertImageCommand() 函数提供了临时变量处理、出栈入栈等玩法,我们就没有必要自己重新再做一套类似的东西了。不要想着再从底层做起,一点点地抠代码,定制一个“紧凑小巧高性能” 的自定义图像处理特效函数了,那样做,会得不偿失的……
  5. 命令行中的"\(" 和 "\)" 前后都要有空格,否则ImageMagick 命令不认!这个硬性规定,同样也适用于IM_Convert(),即该函数的输入字符串中的"\(" 和 "\)" 前后都要有空格的!
  6. 这里说的“统一解决方案”,说白了就是,convert 命令调用 ConvertImageCommand(ImageInfo *image_info, int argc,char **argv,char **metadata,ExceptionInfo *exception) 函数,我们的程序也调用它;不同的是前者是给脚本用的,后者给C 程序调用,并且只需要传入一个字符串(命令行)参数。
  7. 这里有对Android 版的ImageMagick 的一些介绍,但貌似每一种特效,都要自己写一堆Java 代码
  8. 对于下面的命令行,在终端直接运行OK,放到IM_Convert() 运行就有下面的错误提示。后来将传给IM_Convert() 的字符串中的 \"white\" 改成 white 就OK了。事实上,命令行中的 "white" 前后带或者不带双引号都是可以的,但传给IM_Convert() 的字符串中不要带,否则ConvertImageCommand() 函数要解析的参数还带着前后的双引号,会让ConvertImageCommand() 找不到匹配项的,从而发生错误。
传给IM_Convert() 的命令行:

convert ( mf-small.jpg -colorspace gray -type grayscale -contrast-stretch 0 ) ( -clone 0 -colorspace gray -negate -lat 25x25+10% -contrast-stretch 0 ) -compose copy_opacity -composite -fill \"white\" -opaque none +matte -deskew 40% -sharpen 0x1 mf-small-out.jpg
错误提示:

im: unable to open image `"white"': No such file or directory @ error/blob.c/OpenBlob/2641.
im: no decode delegate for this image format `"white"' @ error/constitute.c/ReadImage/550.


你可能感兴趣的:([ImageMagick 学习] Fred's ImageMagick Scripts 转 C++ 的统一解决方案)