PuzzleD

git clone git://xatko.vsos.ethz.ch/PuzzleD.git
Log | Files | Refs | README

puzzle.d (9328B)


      1 /**
      2  * A D-Interface to $(LINK2 https://pureftpd.org/project/libpuzzle, Libpuzzle)
      3  * 
      4  * License: $(LINK2 https://www.gnu.org/licenses/lgpl.html, LGPLV3)
      5  * Authors: Dominik Schmidt
      6  */
      7 module puzzle;
      8 
      9 version(unittest){
     10 	static immutable string testimage="iVBORw0KGgoAAAANSUhEUgAAAE4AAAASAQMAAADPKHrSAAAABlBMVEX///8AAABVwtN+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wgbCSsvSqRkugAAAGxJREFUCNdjYEAAGyYDMH2AgUGCSQDMdAAyGTCZDQwSbLL9yRL//jkrANVKHDPk6OlxEwBpcxHkkJCAMwUE3H9AmQYGbkATlEDMhAQwE6htxqFDTgIM9s+3tz549+/f5wcg01kQLkJi8jHgAQA3KhrFG5DorwAAAABJRU5ErkJggg==";
     11 	ubyte[] testimagedat(){
     12 		import std.base64;
     13 		return cast(ubyte[])Base64.decode(testimage);
     14 	}
     15 }
     16 
     17 extern(C){
     18 	struct PuzzleContext {
     19 		uint puzzle_max_width;
     20 		uint puzzle_max_height;
     21 		uint puzzle_lambdas;
     22 		double puzzle_p_ratio;
     23 		double puzzle_noise_cutoff;
     24 		double puzzle_contrast_barrier_for_cropping;
     25 		double puzzle_max_cropping_ratio;
     26 		int puzzle_enable_autocrop;
     27 		ulong magic;
     28 	};
     29 	struct PuzzleDvec {
     30 		size_t sizeof_vec;
     31 		size_t sizeof_compressed_vec;
     32 		double *vec;
     33 	};
     34 	
     35 	struct PuzzleCvec {
     36 		size_t sizeof_vec;
     37 		char *vec;
     38 	};
     39 	struct PuzzleCompressedCvec {
     40     size_t sizeof_compressed_vec;
     41     ubyte *vec;
     42 };
     43 
     44 	     void
     45      puzzle_init_context(PuzzleContext *context);
     46 
     47      void
     48      puzzle_free_context(PuzzleContext *context);
     49 
     50      void
     51      puzzle_init_cvec(PuzzleContext *context, PuzzleCvec *cvec);
     52 
     53      void
     54      puzzle_init_dvec(PuzzleContext *context, PuzzleDvec *dvec);
     55 
     56      int
     57      puzzle_fill_dvec_from_file(PuzzleContext *context, PuzzleDvec * dvec, const char *file);
     58 
     59      int
     60      puzzle_fill_cvec_from_file(PuzzleContext *context, PuzzleCvec * cvec, const char *file);
     61 
     62      int
     63      puzzle_fill_dvec_from_mem(PuzzleContext *context, PuzzleDvec * dvec, const void *mem,
     64          size_t size);
     65 
     66      int
     67      puzzle_fill_cvec_from_mem(PuzzleContext *context, PuzzleCvec * cvec, const void *mem,
     68          size_t size);
     69 
     70      int
     71      puzzle_fill_cvec_from_dvec(PuzzleContext *context, PuzzleCvec * cvec,
     72          const PuzzleDvec *dvec);
     73 
     74      void
     75      puzzle_free_cvec(PuzzleContext *context, PuzzleCvec *cvec);
     76 
     77      void
     78      puzzle_free_dvec(PuzzleContext *context, PuzzleDvec *dvec);
     79 
     80      void
     81      puzzle_init_compressed_cvec(PuzzleContext *context,
     82          PuzzleCompressedCvec * compressed_cvec);
     83 
     84      void
     85      puzzle_free_compressed_cvec(PuzzleContext *context,
     86          PuzzleCompressedCvec * compressed_cvec);
     87 
     88      int
     89      puzzle_compress_cvec(PuzzleContext *context, PuzzleCompressedCvec * compressed_cvec,
     90          const PuzzleCvec * cvec);
     91 
     92      int
     93      puzzle_uncompress_cvec(PuzzleContext *context, PuzzleCompressedCvec * compressed_cvec,
     94          const(PuzzleCvec *) cvec);
     95 
     96      double
     97      puzzle_vector_normalized_distance(PuzzleContext *context, const PuzzleCvec * cvec1,
     98          const PuzzleCvec * cvec2, int fix_for_texts);
     99 
    100 }
    101 import std.string;
    102 
    103 static bool valid(in PuzzleContext ctx){
    104 	return (ctx.magic==0xdeadbeef);
    105 }
    106 static bool valid(in PuzzleContext *ctx){
    107 	return (ctx!=null && valid(*ctx));
    108 }
    109 
    110 class PuzzleException : Exception{
    111 	pure nothrow @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null){
    112 		super(msg,file,line,next);
    113 	}
    114 }
    115 
    116 /**
    117  * PuzzleContext wrapper
    118  * 
    119  * Has to be inititialized with `Puzzle.initialize();`
    120  * and frees the context on destruction.
    121  * 
    122  * Be careful, when you stack-allocate it, the object gets
    123  * rendered unusable once the scope has been left.
    124  * 
    125  */
    126 struct Puzzle{
    127 	PuzzleContext ctx;
    128 	/**
    129 	 * Initializes the Puzzle-context.
    130 	 * 
    131 	 */
    132 	void initialize()
    133 	out{
    134 		assert(ctx.valid());
    135 	}
    136 	body{
    137 		puzzle_init_context(&ctx);
    138 	}
    139 	
    140 	///Frees the PuzzleContext (If there is one)
    141 	~this(){
    142 		if(ctx.valid){
    143 			puzzle_free_context(&ctx);
    144 		}
    145 	}
    146 	/**
    147 	 * Returns a newly allocated DVec-Structure
    148 	 * 
    149 	 * $(DDOC_SEE_ALSO struct DVec)
    150 	 * 
    151 	 */
    152 	DVec dvec(){
    153 		return DVec(&ctx);
    154 	}
    155 	
    156 	/**
    157 	 * Returns a newly allocated CVec-Structure
    158 	 * 
    159 	 * $(DDOC_SEE_ALSO struct CVec)
    160 	 * 
    161 	 */
    162 	CVec cvec(){
    163 		return CVec(&ctx);
    164 	}
    165 }
    166 ///
    167 unittest{
    168 	Puzzle p;
    169 	assert(!p.ctx.valid);
    170 	p.initialize();
    171 	assert(p.ctx.valid);
    172 	
    173 	DVec d=p.dvec();
    174 	assert(d.ctx.valid);
    175 	
    176 	DVec c=p.dvec();
    177 	assert(c.ctx.valid);
    178 }
    179 
    180 /**
    181  * Wraps the PuzzleDVec-structure
    182  * 
    183  */
    184 struct DVec{
    185 	PuzzleDvec vec;
    186 	PuzzleContext *ctx;
    187 	@disable this();
    188 	
    189 	///Initializes the DVec
    190 	this(PuzzleContext *ctx)
    191 	in{
    192 		assert(ctx.valid(), "Context invalid");
    193 	}
    194 	body{
    195 		this.ctx=ctx;
    196 		puzzle_init_dvec(ctx,&vec);
    197 	}
    198 	
    199 	///Frees the DVec (If it has been allocated by the library);
    200 	~this(){
    201 		if(ctx!=null){
    202 			puzzle_free_dvec(ctx,&vec);
    203 		}
    204 	}
    205 	
    206 	/**
    207 	 * Generates the vector from a file.
    208 	 * Params:
    209 	 * 		f	=	File to read the data from.
    210 	 * Throws:
    211 	 * 	PuzzleException if the puzzle-function fails to load the image
    212 	 */
    213 	void load(string f){
    214 		if(puzzle_fill_dvec_from_file(ctx, &vec, toStringz(f))!=0){
    215 			throw new PuzzleException("Couldn't read from file");
    216 		}
    217 	}
    218 	
    219 	/**
    220 	 * Generates the vector from memory
    221 	 * Params:
    222 	 * 		mem	=	The memory of an Image
    223 	 * Throws:
    224 	 * 		PuzzleException if the memory couldn't be read.
    225 	 */
    226 	void load(void[] mem){
    227 		if(puzzle_fill_dvec_from_mem(ctx, &vec, cast(void*)mem, mem.length)!=0){
    228 			throw new PuzzleException("Couldn't read from memory");
    229 		}
    230 	}
    231 	/**
    232 	 * Get the CVec derived from this DVector.
    233 	 * Returns:
    234 	 * 	The corresponding(and comparable) CVector to this DVector
    235 	 */
    236 	CVec cvec(){
    237 		CVec cvec=CVec(ctx);
    238 		puzzle_fill_cvec_from_dvec(ctx,&cvec.vec,&vec);
    239 		return cvec;
    240 	}
    241 	
    242 	/**
    243 	 * Get the length of the vector
    244 	 * Returns:
    245 	 * 	The length of the vector
    246 	 */ 
    247 	@safe @nogc @property size_t length(){
    248 		return vec.sizeof_vec;
    249 	}
    250 }
    251 
    252 ///
    253 struct CVec{
    254 	PuzzleCvec vec;
    255 	PuzzleContext *ctx;
    256 	@disable this();
    257 	this(ref CVec_Compressed c){
    258 		this(c.ctx);
    259 		puzzle_uncompress_cvec(ctx, &c.vec, &vec);
    260 	}
    261 	/**
    262 	 * Generates a Vector by hand.
    263 	 * 
    264 	 * This can be used to save a Vector and reload it again.
    265 	 * 
    266 	 * Params:
    267 	 * 		a = The array of data to fill the vector with.
    268 	 */
    269 	@nogc pure nothrow this(byte[] a){
    270 		this(cast(char*)a.ptr, a.length);
    271 	}
    272 	///ditto
    273 	@nogc pure nothrow this(char *a, size_t length){
    274 		vec.vec=a;
    275 		vec.sizeof_vec=length;
    276 	}
    277 	
    278 	///
    279 	unittest{
    280 		Puzzle p;
    281 		p.initialize();
    282 		CVec c=p.cvec();
    283 		
    284 		c.load(testimagedat());
    285 		assert(c.length>0);
    286 		
    287 		CVec c2=CVec(c.vec.vec,c.length);
    288 		assert(std.math.approxEqual(c.compare(c2),0));
    289 	}
    290 	
    291 	///Initializes a Vector
    292 	this(PuzzleContext *ctx)
    293 	in{
    294 		assert(ctx.valid(), "Context invalid");
    295 	}
    296 	body{
    297 		this.ctx=ctx;
    298 		puzzle_init_cvec(ctx,&vec);
    299 	}
    300 	///Frees the vector(If it was allocated by the library)
    301 	~this(){
    302 		if(ctx!=null){
    303 			puzzle_free_cvec(ctx,&vec);
    304 		}
    305 	}
    306 	
    307 	/**
    308 	 * Generates the vector from a file.
    309 	 * Params:
    310 	 * 		f	=	File to read the data from.
    311 	 * Throws:
    312 	 * 	PuzzleException if the puzzle-function fails to load the image
    313 	 */
    314 	void load(string f){
    315 		if(puzzle_fill_cvec_from_file(ctx, &vec, toStringz(f))!=0){
    316 			throw new PuzzleException("Couldn't read from file");
    317 		}
    318 	}
    319 	
    320 	/**
    321 	 * Generates the vector from memory
    322 	 * Params:
    323 	 * 		mem	=	The memory of an Image
    324 	 * Throws:
    325 	 * 		PuzzleException if the memory couldn't be read.
    326 	 */
    327 	void load(void[] mem){
    328 		if(puzzle_fill_cvec_from_mem(ctx, &vec, cast(void*)mem, mem.length)!=0){
    329 			throw new PuzzleException("Couldn't read from memory");
    330 		}
    331 	}
    332 	
    333 	/**
    334 	 * Compare two CVectors.
    335 	 * Params:
    336 	 * 		b 		= The second vector to compare to
    337 	 * 		hasText	= Wether one of the images had any text in it.
    338 	 * Returns:
    339 	 * 	The distance of both vectors.
    340 	 */ 
    341 	double compare(in ref CVec b, bool hasText=false){
    342 		return puzzle_vector_normalized_distance(ctx,&vec,&b.vec,(hasText) ? 1 : 0);
    343 	}
    344 	
    345 	
    346 	/**
    347 	 * Compresses a CVector
    348 	 * Returns:
    349 	 * 	A compressed CVector
    350 	 */ 
    351 	CVec_Compressed compress(){
    352 		return CVec_Compressed(this);
    353 	}
    354 	
    355 	/**
    356 	 * Get the length of the vector
    357 	 * Returns:
    358 	 * 	The length of the vector
    359 	 */ 
    360 	@nogc @safe @property size_t length(){
    361 		return vec.sizeof_vec;
    362 	}
    363 }
    364 
    365 ///
    366 unittest{
    367 	Puzzle p;
    368 	p.initialize();
    369 	CVec c=p.cvec();
    370 	c.load(testimagedat());
    371 	
    372 	assert(std.math.approxEqual(c.compare(c),0));
    373 }
    374 
    375 ///A compressed CVector
    376 struct CVec_Compressed{
    377 	PuzzleCompressedCvec vec;
    378 	PuzzleContext *ctx;
    379 	@disable this();
    380 	
    381 	///Generates a compressed CVector corresponding to c 
    382 	this(ref CVec c){
    383 		this(c.ctx);
    384 		puzzle_compress_cvec(ctx, &vec, &c.vec);
    385 	}
    386 	
    387 	/**
    388 	 * Generates a Vector by hand.
    389 	 * 
    390 	 * Params:
    391 	 * 		a = The array of data to fill the vector with.
    392 	 */
    393 	this(ubyte[] a){
    394 		this(a.ptr,a.length);
    395 	}
    396 	
    397 	///ditto
    398 	this(ubyte* a, size_t length){
    399 		vec.vec=a;
    400 		vec.sizeof_compressed_vec=length;
    401 	}
    402 	
    403 	///Allocates a new Compressed CVector in the library
    404 	this(PuzzleContext *ctx)
    405 	in{
    406 		assert(ctx.valid(), "Context invalid");
    407 	}
    408 	body{
    409 		this.ctx=ctx;
    410 		puzzle_init_compressed_cvec(ctx,&vec);
    411 	}
    412 	
    413 	///Frees the Vector, if it was allocated by the library
    414 	~this(){
    415 		if(ctx!=null){
    416 			puzzle_free_compressed_cvec(ctx,&vec);
    417 		}
    418 	}
    419 	
    420 	///Returns the corresponding uncompressed CVector
    421 	CVec uncompress(){
    422 		return CVec(this);
    423 	}
    424 	
    425 	///
    426 	@safe @nogc @property size_t length(){
    427 		return vec.sizeof_compressed_vec;
    428 	}
    429 }
    430 
    431 ///
    432 unittest{
    433 	Puzzle p;
    434 	p.initialize();
    435 	CVec c=p.cvec();
    436 	c.load(testimagedat());
    437 	CVec_Compressed cc=c.compress();
    438 	CVec c2=cc.uncompress();
    439 	
    440 	assert(std.math.approxEqual(c.compare(c2),0));
    441 }