Как я преобразовываю какое-либо изображение в изображение paletted с 4 цветами, пользующееся Библиотекой Обработки изображений Python?

Очевидно, что можно создавать классы во время выполнения или даже новые анонимные типы, но они чрезвычайно ограничены в том, как вы можете использовать их в своем коде.

Если вы предпочитаете работать в современной универсальной среде Queryable и избегать создания классов во время выполнения, которые имеют ограниченный доступ во время компиляции, вы можете свернуть свой собственный анализатор выражений и построить деревья Expression. Хитрость заключается в том, чтобы использовать тип Array в качестве возврата из Select, чтобы сделать члены доступными. Это означает, что все выражения должны возвращать один и тот же тип, но эта реализация при необходимости преобразует все выражения в один тип.

Вот пример реализации:

public static class IQueryableExt {
    public static Expression<Func<TRec, TVal?[]>> SelectExpr<TRec, TVal>(this IEnumerable<string> strExprs) where TVal : struct {
        var p = Expression.Parameter(typeof(TRec), "p");
        var exprs = strExprs.Select(se => {
            var e = se.ParseExpression(p);
            return e.Type.IsNullableType() && e.Type.GetGenericArguments()[0] == typeof(TVal) ? e : Expression.Convert(e, typeof(TVal?));
        }).ToArray();

        return Expression.Lambda<Func<TRec, TVal?[]>>(Expression.NewArrayInit(typeof(TVal?), exprs), p);
    }

    static char[] operators = { '+', '-', '*', '/' };
    static Regex tokenRE = new Regex($@"(?=[-+*/()])|(?<=[-+*/()])", RegexOptions.Compiled);
    static HashSet<char> hsOperators = operators.ToHashSet();
    static Dictionary<char, ExpressionType> opType = new Dictionary<char, ExpressionType>() {
        { '*', ExpressionType.Multiply },
        { '/', ExpressionType.Divide },
        { '+', ExpressionType.Add },
        { '-', ExpressionType.Subtract }
    };

    static int opPriority(char op) => hsOperators.Contains(op) ? Array.IndexOf(operators, op) >> 1 : (op == ')' ? -1 : -2);

    public static Expression ParseExpression(this string expr, ParameterExpression dbParam) {
        var opStack = new Stack<char>();
        opStack.Push('(');
        var operandStack = new Stack<Expression>();

        foreach (var t in tokenRE.Split(expr).Where(t => !String.IsNullOrEmpty(t)).Append(")")) {
            if (t.Length > 1) // process column name
                operandStack.Push(Expression.PropertyOrField(dbParam, t));
            else {
                while (t[0] != '(' && opPriority(opStack.Peek()) >= opPriority(t[0])) {
                    var curOp = opStack.Pop();
                    var right = operandStack.Pop();
                    var left = operandStack.Pop();
                    if (right.Type != left.Type) {
                        if (right.Type.IsNullableType())
                            left = Expression.Convert(left, right.Type);
                        else if (left.Type.IsNullableType())
                            right = Expression.Convert(right, left.Type);
                        else
                            throw new Exception($"Incompatible types for operator{curOp}: {left.Type.Name}, {right.Type.Name}");
                    }
                    operandStack.Push(Expression.MakeBinary(opType[curOp], left, right));
                }
                if (t[0] != ')')
                    opStack.Push(t[0]);
                else
                    opStack.Pop(); // pop (
            }
        }
        return operandStack.Pop();
    }

    public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
        nullableType.IsGenericType &&
        !nullableType.IsGenericTypeDefinition &&
        Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
}

К сожалению, вывод типа не может легко получить тип ответа, поэтому вы должны вручную передать тип записи и тип ответа. Обратите внимание, что в синтаксическом анализаторе есть специальный код для обработки преобразования в (распространенные в SQL) типы, допускающие значения NULL, при смешивании значений Nullable и Nullable.

Учитывая columnsToSelect, который вы предоставили в качестве примера:

List<string> columnsToSelect = new List<string>();
columnsToSelect.Add("col1 + col2");
columnsToSelect.Add("col2");
columnsToSelect.Add("col3 + col4");

Вы можете запросить базу данных следующим образом:

var queryResult= Context.RawData.Select(columnsToSelect.SelextExpr<TRawData, int>());

И queryResult будет иметь тип [118 ] или IQueryable<int?[]> в зависимости от типов столбцов SQL.

11
задан Thomas Vander Stichele 25 October 2008 в 17:36
поделиться

2 ответа

Во-первых: Ваши четыре цветовых палитры (черный, зеленый, красный, желтый) не имеют никакого синего компонента. Так, необходимо признать, что выходное изображение едва приблизит входное изображение, если не будет никакого синего компонента для запуска с.

Попробуйте этот код:

import Image

def estimate_color(c, bit, c_error):
    c_new= c -  c_error
    if c_new > 127:
        c_bit= bit
        c_error= 255 - c_new
    else:
        c_bit= 0
        c_error= -c_new
    return c_bit, c_error

def image2cga(im):
    "Produce a sequence of CGA pixels from image im"
    im_width= im.size[0]
    for index, (r, g, b) in enumerate(im.getdata()):
        if index % im_width == 0: # start of a line
            r_error= g_error= 0
        r_bit, r_error= estimate_color(r, 1, r_error)
        g_bit, g_error= estimate_color(g, 2, g_error)
        yield r_bit|g_bit

def cvt2cga(imgfn):
    "Convert an RGB image to (K, R, G, Y) CGA image"
    inp_im= Image.open(imgfn) # assume it's RGB
    out_im= Image.new("P", inp_im.size, None)
    out_im.putpalette( (
        0, 0, 0,
        255, 0, 0,
        0, 255, 0,
        255, 255, 0,
    ) )
    out_im.putdata(list(image2cga(inp_im)))
    return out_im

if __name__ == "__main__":
    import sys, os

    for imgfn in sys.argv[1:]:
        im= cvt2cga(imgfn)
        dirname, filename= os.path.split(imgfn)
        name, ext= os.path.splitext(filename)
        newpathname= os.path.join(dirname, "cga-%s.png" % name)
        im.save(newpathname)

Это создает изображение палитры PNG только с первыми четырьмя наборами записей палитры к Вашим цветам. Это демонстрационное изображение:

становится

Это тривиально для взятия вывода image2cga (приводит к последовательности 0-3 значений), и упакуйте каждые четыре значения к байту.

Если Вы нуждаетесь в помощи о том, что делает код, спросите, и я объясню.

EDIT1: не изобретать велосипед

Конечно, оказывается, что я был слишком восторжен и — как обнаруженный Thomas — метод Image.quantize может взять изображение палитры в качестве аргумента и сделать квантование с намного лучшими результатами, чем мой специальный метод выше:

def cga_quantize(image):
    pal_image= Image.new("P", (1,1))
    pal_image.putpalette( (0,0,0, 0,255,0, 255,0,0, 255,255,0) + (0,0,0)*252)
    return image.convert("RGB").quantize(palette=pal_image)

EDIT1, продолжение следует: Упакуйте пиксели в байты

Для "добавленной стоимости", здесь следует коду для создания упакованной строки (4 пикселя за байт):

import itertools as it

# setup: create a map with tuples [(0,0,0,0)‥(3,3,3,3)] as keys
# and values [chr(0)‥chr(255)], because PIL does not yet support
# 4 colour palette images

TUPLE2CHAR= {}

# Assume (b7, b6) are pixel0, (b5, b4) are pixel1…
# Call it "big endian"

KEY_BUILDER= [
    (0, 64, 128, 192), # pixel0 value used as index
    (0, 16, 32, 48), # pixel1
    (0, 4, 8, 12), # pixel2
    (0, 1, 2, 3), # pixel3
]
# For "little endian", uncomment the following line
## KEY_BUILDER.reverse()

# python2.6 has itertools.product, but for compatibility purposes
# let's do it verbosely:
for ix0, px0 in enumerate(KEY_BUILDER[0]):
    for ix1, px1 in enumerate(KEY_BUILDER[1]):
        for ix2, px2 in enumerate(KEY_BUILDER[2]):
            for ix3, px3 in enumerate(KEY_BUILDER[3]):
                TUPLE2CHAR[ix0,ix1,ix2,ix3]= chr(px0+px1+px2+px3)

# Another helper function, copied almost verbatim from itertools docs
def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return it.izip(*[it.chain(iterable, it.repeat(padvalue, n-1))]*n)

# now the functions
def seq2str(seq):
    """Takes a sequence of [0..3] values and packs them into bytes
    using two bits per value"""
    return ''.join(
        TUPLE2CHAR[four_pixel]
        for four_pixel in grouper(4, seq, 0))

# and the image related function
# Note that the following function is correct,
# but is not useful for Windows 16 colour bitmaps,
# which start at the *bottom* row…
def image2str(img):
    return seq2str(img.getdata())
17
ответ дан 3 December 2019 в 04:54
поделиться

John, я нашел, что сначала связываются также, но это непосредственно не помогло мне с проблемой. Это действительно заставляло меня выглядеть глубже, квантуют все же.

Я придумал это вчера перед тем, чтобы ложиться спать:

import sys

import PIL
import Image

PALETTE = [
    0,   0,   0,  # black,  00
    0,   255, 0,  # green,  01
    255, 0,   0,  # red,    10
    255, 255, 0,  # yellow, 11
] + [0, ] * 252 * 3

# a palette image to use for quant
pimage = Image.new("P", (1, 1), 0)
pimage.putpalette(PALETTE)

# open the source image
image = Image.open(sys.argv[1])
image = image.convert("RGB")

# quantize it using our palette image
imagep = image.quantize(palette=pimage)

# save
imagep.save('/tmp/cga.png')

TZ.TZIOY, Ваше решение, кажется, работает вдоль тех же принципов. Престижность, я должен был прекратить работать над ним и ожидать Вашего ответа. Мой немного более прост, хотя определенно не более логичный, чем Ваш. PIL является громоздким для использования. Ваш объясняет, что продолжает делать это.

6
ответ дан 3 December 2019 в 04:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: