1
0
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:
Virgil Dupras
2010-02-04 13:13:08 +01:00
parent 0b9d936317
commit 352a21acaa
11 changed files with 325 additions and 250 deletions

View File

@@ -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
View 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);
}

View File

@@ -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
View 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
View 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, ...);

View File

@@ -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,
)