mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-22 14:41:39 +00:00
Converted PictureBlocks to a Python extension and created a "common" C unit for common code among extensions.
This commit is contained in:
@@ -11,7 +11,7 @@ import logging
|
||||
import plistlib
|
||||
import re
|
||||
|
||||
from Foundation import NSBundle, NSUserDefaults, NSURL
|
||||
from Foundation import NSUserDefaults, NSURL
|
||||
from appscript import app, k, CommandError
|
||||
|
||||
from hsutil import io
|
||||
@@ -21,14 +21,9 @@ from hsutil.cocoa import as_fetch
|
||||
|
||||
from core import fs
|
||||
from core import app_cocoa, directories
|
||||
from . import data
|
||||
from .cache import string_to_colors
|
||||
from . import data, _block_osx
|
||||
from .scanner import ScannerPE
|
||||
|
||||
mainBundle = NSBundle.mainBundle()
|
||||
PictureBlocks = mainBundle.classNamed_('PictureBlocks')
|
||||
assert PictureBlocks is not None
|
||||
|
||||
class Photo(fs.File):
|
||||
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
||||
INITIAL_INFO.update({
|
||||
@@ -43,17 +38,16 @@ class Photo(fs.File):
|
||||
def _read_info(self, field):
|
||||
fs.File._read_info(self, field)
|
||||
if field == 'dimensions':
|
||||
size = PictureBlocks.getImageSize_(unicode(self.path))
|
||||
self.dimensions = (size.width, size.height)
|
||||
self.dimensions = _block_osx.get_image_size(unicode(self.path))
|
||||
|
||||
def get_blocks(self, block_count_per_side):
|
||||
try:
|
||||
blocks = PictureBlocks.getBlocksFromImagePath_blockCount_(unicode(self.path), block_count_per_side)
|
||||
blocks = _block_osx.getblocks(unicode(self.path), block_count_per_side)
|
||||
except Exception as e:
|
||||
raise IOError('The reading of "%s" failed with "%s"' % (unicode(self.path), unicode(e)))
|
||||
if not blocks:
|
||||
raise IOError('The picture %s could not be read' % unicode(self.path))
|
||||
return string_to_colors(blocks)
|
||||
return blocks
|
||||
|
||||
|
||||
class IPhoto(Photo):
|
||||
|
||||
@@ -23,5 +23,6 @@ os.system('python setup.py build_ext --inplace')
|
||||
os.chdir('..')
|
||||
move(op.join('modules', '_block.so'), '_block.so')
|
||||
move(op.join('modules', '_block.pyd'), '_block.pyd')
|
||||
move(op.join('modules', '_block_osx.so'), '_block_osx.so')
|
||||
move(op.join('modules', '_cache.so'), '_cache.so')
|
||||
move(op.join('modules', '_cache.pyd'), '_cache.pyd')
|
||||
|
||||
@@ -7,59 +7,17 @@
|
||||
* http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
#include "common.h"
|
||||
|
||||
/* avgdiff/maxdiff has been called with empty lists */
|
||||
static PyObject *NoBlocksError;
|
||||
/* avgdiff/maxdiff has been called with 2 block lists of different size. */
|
||||
static PyObject *DifferentBlockCountError;
|
||||
|
||||
/* It seems like MS VC defines min/max already */
|
||||
#ifndef _MSC_VER
|
||||
static int
|
||||
max(int a, int b)
|
||||
{
|
||||
return b > a ? b : a;
|
||||
}
|
||||
|
||||
static int
|
||||
min(int a, int b)
|
||||
{
|
||||
return b < a ? b : a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create a tuple out of an array of integers. */
|
||||
static PyObject*
|
||||
inttuple(int n, ...)
|
||||
{
|
||||
int i;
|
||||
PyObject *pnumber;
|
||||
PyObject *result;
|
||||
va_list numbers;
|
||||
|
||||
va_start(numbers, n);
|
||||
result = PyTuple_New(n);
|
||||
|
||||
for (i=0; i<n; i++) {
|
||||
pnumber = PyInt_FromLong(va_arg(numbers, int));
|
||||
if (pnumber == NULL) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(result, i, pnumber);
|
||||
}
|
||||
|
||||
va_end(numbers);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Returns a 3 sized tuple containing the mean color of 'image'.
|
||||
* image: a PIL image or crop.
|
||||
*/
|
||||
static PyObject*
|
||||
getblock(PyObject *image)
|
||||
static PyObject* getblock(PyObject *image)
|
||||
{
|
||||
int i, totr, totg, totb;
|
||||
Py_ssize_t pixel_count;
|
||||
@@ -107,8 +65,7 @@ getblock(PyObject *image)
|
||||
/* Returns the difference between the first block and the second.
|
||||
* It returns an absolute sum of the 3 differences (RGB).
|
||||
*/
|
||||
static int
|
||||
diff(PyObject *first, PyObject *second)
|
||||
static int diff(PyObject *first, PyObject *second)
|
||||
{
|
||||
Py_ssize_t r1, g1, b1, r2, b2, g2;
|
||||
PyObject *pr, *pg, *pb;
|
||||
@@ -144,8 +101,7 @@ If it is 10, for example, 100 blocks will be returns (10 width, 10 height). The
|
||||
necessarely cover square areas. The area covered by each block will be proportional to the image\n\
|
||||
itself.\n");
|
||||
|
||||
static PyObject*
|
||||
block_getblocks2(PyObject *self, PyObject *args)
|
||||
static PyObject* block_getblocks2(PyObject *self, PyObject *args)
|
||||
{
|
||||
int block_count_per_side, width, height, block_width, block_height, ih;
|
||||
PyObject *image;
|
||||
@@ -218,8 +174,7 @@ PyDoc_STRVAR(block_avgdiff_doc,
|
||||
If the result surpasses limit, limit + 1 is returned, except if less than min_iterations\n\
|
||||
iterations have been made in the blocks.\n");
|
||||
|
||||
static PyObject*
|
||||
block_avgdiff(PyObject *self, PyObject *args)
|
||||
static PyObject* block_avgdiff(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *first, *second;
|
||||
int limit, min_iterations;
|
||||
|
||||
229
core_pe/modules/block_osx.m
Normal file
229
core_pe/modules/block_osx.m
Normal file
@@ -0,0 +1,229 @@
|
||||
/* Created By: Virgil Dupras
|
||||
* Created On: 2010-02-04
|
||||
* Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||
*
|
||||
* This software is licensed under the "HS" License as described in the "LICENSE" file,
|
||||
* which should be included with this package. The terms are also available at
|
||||
* http://www.hardcoded.net/licenses/hs_license
|
||||
**/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static CFStringRef
|
||||
pystring2cfstring(PyObject *pystring)
|
||||
{
|
||||
PyObject *encoded;
|
||||
UInt8 *s;
|
||||
CFIndex size;
|
||||
CFStringRef result;
|
||||
|
||||
if (PyUnicode_Check(pystring)) {
|
||||
encoded = PyUnicode_AsUTF8String(pystring);
|
||||
if (encoded == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
encoded = pystring;
|
||||
Py_INCREF(encoded);
|
||||
}
|
||||
|
||||
s = (UInt8*)PyString_AS_STRING(encoded);
|
||||
size = PyString_GET_SIZE(encoded);
|
||||
result = CFStringCreateWithBytes(NULL, s, size, kCFStringEncodingUTF8, FALSE);
|
||||
Py_DECREF(encoded);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject* block_osx_get_image_size(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *path;
|
||||
CFStringRef image_path;
|
||||
CFURLRef image_url;
|
||||
CGImageSourceRef source;
|
||||
CGImageRef image;
|
||||
size_t width, height;
|
||||
PyObject *pwidth, *pheight;
|
||||
PyObject *result;
|
||||
|
||||
width = 0;
|
||||
height = 0;
|
||||
if (!PyArg_ParseTuple(args, "O", &path)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_path = pystring2cfstring(path);
|
||||
if (image_path == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE);
|
||||
CFRelease(image_path);
|
||||
|
||||
source = CGImageSourceCreateWithURL(image_url, NULL);
|
||||
CFRelease(image_url);
|
||||
if (source != NULL) {
|
||||
image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||
if (image != NULL) {
|
||||
width = CGImageGetWidth(image);
|
||||
height = CGImageGetHeight(image);
|
||||
CGImageRelease(image);
|
||||
}
|
||||
CFRelease(source);
|
||||
}
|
||||
|
||||
pwidth = PyInt_FromSsize_t(width);
|
||||
if (pwidth == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
pheight = PyInt_FromSsize_t(height);
|
||||
if (pheight == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
result = PyTuple_Pack(2, pwidth, pheight);
|
||||
Py_DECREF(pwidth);
|
||||
Py_DECREF(pheight);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CGContextRef
|
||||
MyCreateBitmapContext(int width, int height)
|
||||
{
|
||||
CGContextRef context = NULL;
|
||||
CGColorSpaceRef colorSpace;
|
||||
void *bitmapData;
|
||||
int bitmapByteCount;
|
||||
int bitmapBytesPerRow;
|
||||
|
||||
bitmapBytesPerRow = (width * 4);
|
||||
bitmapByteCount = (bitmapBytesPerRow * height);
|
||||
|
||||
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
||||
|
||||
// calloc() must be used to allocate bitmapData here because the buffer has to be zeroed.
|
||||
// If it's not zeroes, when images with transparency are drawn in the context, this buffer
|
||||
// will stay with undefined pixels, which means that two pictures with the same pixels will
|
||||
// most likely have different blocks (which is not supposed to happen).
|
||||
bitmapData = calloc(bitmapByteCount, 1);
|
||||
if (bitmapData == NULL) {
|
||||
fprintf(stderr, "Memory not allocated!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context = CGBitmapContextCreate(bitmapData, width, height, 8, bitmapBytesPerRow, colorSpace,
|
||||
kCGImageAlphaNoneSkipLast);
|
||||
if (context== NULL) {
|
||||
free(bitmapData);
|
||||
fprintf(stderr, "Context not created!");
|
||||
return NULL;
|
||||
}
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
return context;
|
||||
}
|
||||
|
||||
static PyObject* getblock(unsigned char *imageData, int imageWidth, int imageHeight, int boxX, int boxY, int boxW, int boxH)
|
||||
{
|
||||
int i,j, totalR, totalG, totalB;
|
||||
|
||||
totalR = totalG = totalB = 0;
|
||||
for(i=boxY; i<boxY+boxH; i++) {
|
||||
for(j=boxX; j<boxX+boxW; j++) {
|
||||
int offset = (i * imageWidth * 4) + (j * 4);
|
||||
totalR += *(imageData + offset);
|
||||
totalG += *(imageData + offset + 1);
|
||||
totalB += *(imageData + offset + 2);
|
||||
}
|
||||
}
|
||||
int pixelCount = boxH * boxW;
|
||||
totalR /= pixelCount;
|
||||
totalG /= pixelCount;
|
||||
totalB /= pixelCount;
|
||||
|
||||
return inttuple(3, totalR, totalG, totalB);
|
||||
}
|
||||
|
||||
static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *path, *result;
|
||||
CFStringRef image_path;
|
||||
CFURLRef image_url;
|
||||
CGImageSourceRef source;
|
||||
CGImageRef image;
|
||||
size_t width, height;
|
||||
int block_count, block_width, block_height, block_xcount, block_ycount, i;
|
||||
|
||||
width = 0;
|
||||
height = 0;
|
||||
if (!PyArg_ParseTuple(args, "Oi", &path, &block_count)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_path = pystring2cfstring(path);
|
||||
if (image_path == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE);
|
||||
CFRelease(image_path);
|
||||
|
||||
source = CGImageSourceCreateWithURL(image_url, NULL);
|
||||
CFRelease(image_url);
|
||||
if (source == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||
if (image == NULL) {
|
||||
CFRelease(source);
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
width = CGImageGetWidth(image);
|
||||
height = CGImageGetHeight(image);
|
||||
CGContextRef myContext = MyCreateBitmapContext(width, height);
|
||||
CGRect myBoundingBox = CGRectMake(0, 0, width, height);
|
||||
CGContextDrawImage(myContext, myBoundingBox, image);
|
||||
unsigned char *bitmapData = CGBitmapContextGetData(myContext);
|
||||
CGContextRelease(myContext);
|
||||
CGImageRelease(image);
|
||||
CFRelease(source);
|
||||
if (bitmapData == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
block_width = max(width/block_count, 1);
|
||||
block_height = max(height/block_count, 1);
|
||||
/* block_count might have changed */
|
||||
block_xcount = (width / block_width);
|
||||
block_ycount = (height / block_height);
|
||||
|
||||
result = PyList_New(block_xcount * block_ycount);
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(i=0; i<block_ycount; i++)
|
||||
{
|
||||
int j;
|
||||
for(j=0; j<block_xcount; j++)
|
||||
{
|
||||
PyObject *block = getblock(bitmapData, width, height, j*block_width, i*block_height,
|
||||
block_width, block_height);
|
||||
PyList_SET_ITEM(result, i*block_ycount+j, block);
|
||||
}
|
||||
}
|
||||
|
||||
free(bitmapData);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef BlockOsxMethods[] = {
|
||||
{"get_image_size", block_osx_get_image_size, METH_VARARGS, ""},
|
||||
{"getblocks", block_osx_getblocks, METH_VARARGS, ""},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
init_block_osx(void)
|
||||
{
|
||||
Py_InitModule("_block_osx", BlockOsxMethods);
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
* which should be included with this package. The terms are also available at
|
||||
* http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* I know that there strtol out there, but it requires a pointer to
|
||||
* a char, which would in turn require me to buffer my chars around,
|
||||
@@ -49,24 +49,13 @@ cache_string_to_colors(PyObject *self, PyObject *args)
|
||||
long r, g, b;
|
||||
Py_ssize_t ci;
|
||||
PyObject *color_tuple;
|
||||
PyObject *pr, *pg, *pb;
|
||||
|
||||
ci = i * 6;
|
||||
r = (xchar_to_long(s[ci]) << 4) + xchar_to_long(s[ci+1]);
|
||||
g = (xchar_to_long(s[ci+2]) << 4) + xchar_to_long(s[ci+3]);
|
||||
b = (xchar_to_long(s[ci+4]) << 4) + xchar_to_long(s[ci+5]);
|
||||
|
||||
pr = PyInt_FromLong(r);
|
||||
pg = PyInt_FromLong(g);
|
||||
pb = PyInt_FromLong(b);
|
||||
if (pb == NULL) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
color_tuple = PyTuple_Pack(3, pr, pg, pb);
|
||||
Py_DECREF(pr);
|
||||
Py_DECREF(pg);
|
||||
Py_DECREF(pb);
|
||||
color_tuple = inttuple(3, r, g, b);
|
||||
if (color_tuple == NULL) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
|
||||
45
core_pe/modules/common.c
Normal file
45
core_pe/modules/common.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Created By: Virgil Dupras
|
||||
* Created On: 2010-02-04
|
||||
* Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||
*
|
||||
* This software is licensed under the "HS" License as described in the "LICENSE" file,
|
||||
* which should be included with this package. The terms are also available at
|
||||
* http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
int max(int a, int b)
|
||||
{
|
||||
return b > a ? b : a;
|
||||
}
|
||||
|
||||
int min(int a, int b)
|
||||
{
|
||||
return b < a ? b : a;
|
||||
}
|
||||
#endif
|
||||
|
||||
PyObject* inttuple(int n, ...)
|
||||
{
|
||||
int i;
|
||||
PyObject *pnumber;
|
||||
PyObject *result;
|
||||
va_list numbers;
|
||||
|
||||
va_start(numbers, n);
|
||||
result = PyTuple_New(n);
|
||||
|
||||
for (i=0; i<n; i++) {
|
||||
pnumber = PyInt_FromLong(va_arg(numbers, int));
|
||||
if (pnumber == NULL) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(result, i, pnumber);
|
||||
}
|
||||
|
||||
va_end(numbers);
|
||||
return result;
|
||||
}
|
||||
20
core_pe/modules/common.h
Normal file
20
core_pe/modules/common.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* Created By: Virgil Dupras
|
||||
* Created On: 2010-02-04
|
||||
* Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||
*
|
||||
* This software is licensed under the "HS" License as described in the "LICENSE" file,
|
||||
* which should be included with this package. The terms are also available at
|
||||
* http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
|
||||
/* It seems like MS VC defines min/max already */
|
||||
#ifndef _MSC_VER
|
||||
int max(int a, int b);
|
||||
int min(int a, int b);
|
||||
#endif
|
||||
|
||||
/* Create a tuple out of an array of integers. */
|
||||
PyObject* inttuple(int n, ...);
|
||||
@@ -6,12 +6,25 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
import sys
|
||||
|
||||
from distutils.core import setup
|
||||
from distutils.extension import Extension
|
||||
|
||||
exts = []
|
||||
|
||||
exts.append(Extension("_block", ["block.c", "common.c"]))
|
||||
exts.append(Extension("_cache", ["cache.c", "common.c"]))
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
exts.append(Extension(
|
||||
"_block_osx", ["block_osx.m", "common.c"],
|
||||
extra_link_args=[
|
||||
"-framework", "CoreFoundation",
|
||||
"-framework", "Foundation",
|
||||
"-framework", "ApplicationServices",
|
||||
]))
|
||||
|
||||
setup(
|
||||
ext_modules = [
|
||||
Extension("_block", ["block.c"]),
|
||||
Extension("_cache", ["cache.c"]),
|
||||
]
|
||||
ext_modules = exts,
|
||||
)
|
||||
Reference in New Issue
Block a user