Python вопросы

Список разделов Blender 3D Плагины

Описание: Обсуждение расширений для Blender
Модератор: exooman

  • 1

Сообщение #21 Bibo » 20.09.2016, 15:33

Korchy писал(а):Насколько я вообще понимаю, layout это какая-то структура, в которой расписаны пункты этого самого меню
Так оно и есть. Функции layout-а используются для размещения элементов на твоей панели. Какие-то функции отвечают за способ размещения элементов (построчно, в колонку, задать промежуток между элементами и пр.). Какие-то непосредственно добавляют элемент управления (кнопки, списки, свитки и пр.).
Bibo
Аватара
Сообщения: 469

Сообщение #22 Korchy » 20.09.2016, 16:15

Да, так я и понял. Получается это набор операторов и функции вызова их функций draw в определенном порядке.
Эх, пользование документацией Блендера тот еще квест ((
Korchy M
Аватара
Сообщения: 2222



Сообщение #23 Pavel » 19.10.2016, 08:34

Вопрос по питону:
Как получить значения вращения (Euler XYZ или Quaternion WXYZ) зная следующее:
Имеется четыре координаты. Первые две координаты задают точку на плоскости XY, через которую и начало координат проводится плоскость, параллельная оси Z. На этой плоскости оставшимися двумя координатами задаётся окончательная точка поворота.
Pavel M
Сообщения: 624

Сообщение #24 Korchy » 07.11.2016, 10:47

Можно ли проверять значения входных параметоров оператора в poll ?
Простейший пример - хочу проверить, чтобы не было деления на 0.
Код: Выделить всё
import bpy

class div(bpy.types.Operator):
    bl_idname = "math.division"
    bl_label = "Division"

    v1 = bpy.props.IntProperty(
        name = "V1"
    )
    v2 = bpy.props.IntProperty(
        name = "V2"
    )

    @classmethod
    def poll(self, context):
        print(self.v2)
        if self.v2 == 0:
            return False
        return True

    def execute(self, context):
        print(self.v1 / self.v2)
        return {'FINISHED'}

def register():
    bpy.utils.register_class(div)

def unregister():
    bpy.utils.unregister_class(div)

if __name__ == "__main__":
    register()

результат вызова оператора:
Код: Выделить всё
>>> bpy.ops.math.division(v1 = 100, v2 = 2)
(<built-in function IntProperty>, {'name': 'V2', 'attr': 'v2'})
(<built-in function IntProperty>, {'name': 'V2', 'attr': 'v2'})
50.0
{'FINISHED'}

т.е. получается в poll еще нет доступа к self-свойствам? Можно его как-то получить? Или я вообще не правильно делаю и poll не для этого и нужно просто возвращать {'CANCELLED'} из execute?
Korchy M
Аватара
Сообщения: 2222



  • 1

Сообщение #25 Bibo » 07.11.2016, 11:53

Метод poll используется для проверки возможности вызова оператора в контексте, без проверки данных. Более того
Код: Выделить всё
    @classmethod
    def poll(self, context):
        print(self.v2)
        if self.v2 == 0:
            return False
        return True
Первый аргумент правильней будет именовать не self, а cls, так как классовые методы принимают в качестве первого аргумента не экземпляр класса, а сам класс.
К тому же, надо учесть, что питон используется для взаимодействия с Си инструментами редактора куда сложнее и запутаннее, чем это делается, допустим, в BGE. Потому справедливо произвести проверку:
Код: Выделить всё
    @classmethod
    def poll(cls, context):
        print(type(cls))
        return True
Результат
Код: Выделить всё
>>> bpy.ops.math.division(v1 = 100, v2 = 2)
<class 'bpy_types.OrderedMeta'>
То есть, мы получаем первым аргументом даже не наш класс div.

Проще говоря, обработку и проверку данных нужно производить в методе execute
Korchy писал(а):poll не для этого и нужно просто возвращать {'CANCELLED'} из execute
Bibo
Аватара
Сообщения: 469

Сообщение #26 Korchy » 07.11.2016, 12:16

Ну, чисто логически я тоже к этому выводу пришел, но думал - вдруг ))
А вообще странно, раз уж выделили определенный механизм для проверок возможности выполнения оператора, просто по логике все абсолютные проверки можно было бы делать там.
Korchy M
Аватара
Сообщения: 2222



Сообщение #27 try_out » 07.11.2016, 16:51

Уважаемые светлые головы. А можно ли рассчитывать на подсказку по Python применительно к GIMP?
У меня есть один аддон, который предназначен для GIMP версии примерно 2.6. В последней версии он работает неправильно, у меня установлена тестовая 2.9.
ASUS N53Jg /Linux Mint 18.2 Mate 64-bit
try_out M
Аватара
Откуда: РФ, Башкирия
Сообщения: 2337

Сообщение #28 Korchy » 07.11.2016, 19:10

Я не в теме совершенно.
Korchy M
Аватара
Сообщения: 2222



Сообщение #29 grayich » 07.11.2016, 21:55

try_out, вероятнее всего там половину плагина придётся переписывать, т.к. в 2.9 довольно много переделали
x64 Linux
grayich M
Аватара
Откуда: Харьков
Сообщения: 4507

Сообщение #30 try_out » 08.11.2016, 11:09

grayich писал(а):в 2.9 довольно много переделали
Похоже, что не только в 2.9…
Установил GIMP 2.8. Плагин заработал, но не вполне корректно.

Вот сам плагин. Хотя бы узнать по шагам, какие действия он выполняет. Он совсем небольшой.
Спойлер
Код: Выделить всё
#!/usr/bin/env python
#
# ================================================================================
#                   ColourNegativeInverter.py - (c) Berthold Hinz 2009
# ================================================================================
#
# COLOUR NEGATIVE INVERTER FOR THE GIMP V.3
#
# ================================================================================
#
# INSTRUCTIONS:
#
# Script needs the colour of unexposed film as reference. Either the scanned image
# includes a small border or edge of unexpoded film or you have to set the Gimp
# foereground colour to the colour of unexposed film.
#
# Change step-value to influence processing speed and performance. Step value of
# 1 means that each single pixel is taken into account. The higher the step-value
# the more pixels will be skiped. The script will work faster, but the probability
# to ignore important parts of the image will gain too.
#
# ================================================================================
#
# This program is free software; you can redistribute it and/or modify it.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# ================================================================================
from gimpfu import *
import pygtk
pygtk.require("2.0")
import gtk

def CNI(img, drw, incl, contr_enh, step, merge_layers):
    pdb.gimp_image_undo_group_start(img)
    if incl==True:
        LightMax=0
        pdb.gimp_message("Processing ..." +"\n")
        for i in range (1, img.width-2,step):
            for j in range (1, img.height-2,step):
                val = getpixel(drw, i, j)
                lightness = getV(val[0],val[1],val[2])
                if lightness > LightMax:
                    LightMax=lightness
                    LMax_R=val[0]
                    LMax_G=val[1]
                    LMax_B=val[2]
        gimp.set_foreground(LMax_R,LMax_G,LMax_B)
    else:
        fgc = gimp.get_foreground()
        LMax_R=fgc[0]
        LMax_G=fgc[1]
        LMax_B=fgc[2]
    layer_copy = drw.copy(True)
    layer_copy.mode = NORMAL_MODE
    layer_copy.name = "bias removed"
    img.add_layer(layer_copy,0)
    drw=img.layers[0]
    pdb.gimp_curves_spline(drw, 1, 4,[0, 0, LMax_R, 255])
    pdb.gimp_curves_spline(drw, 2, 4,[0, 0, LMax_G, 255])
    pdb.gimp_curves_spline(drw, 3, 4,[0, 0, LMax_B, 255])
    if contr_enh==True:
        pdb.gimp_levels_stretch(drw)
    pdb.gimp_message("Done!")
    layer_copy = drw.copy(True)
    layer_copy.mode = NORMAL_MODE
    layer_copy.name = "inverted"
    img.add_layer(layer_copy,0)
    drw=img.layers[0]
    pdb.gimp_invert(drw)
    if merge_layers==True:
        pdb.gimp_image_merge_down(img,drw,0)
        pdb.gimp_image_merge_down(img,img.layers[0],0)
    pdb.gimp_image_undo_group_end(img)

def getpixel(drw, x, y):
    """ gets colour values """
    tile = drw.get_tile2(False, x, y)
    x_offset = x % 64
    y_offset = y % 64
    pixel = tile[x_offset, y_offset]
    values = []
    for i in range(len(pixel)):
        values.append(ord(pixel[i]))
    return values

def getV(R, G, B):
    mi=min(R,G,B)*1.0
    ma=max(R,G,B)*1.0
    v = round(ma/255*100)
    return v

register(
    "colour_negative_inverter",
    "Removes orange bias from scanned colour negatives and inverts result"+"\n\n"+"(if you don't use auto-mode, set Gimp foreground colour to colour"+"\n" +"of unexposed film before you run the script)",
    "",
    "Berthold Hinz>",
    "GPL",
    "2009",
    "<Image>/Filters/Photo/Colour Negative Inverter",
    "RGB*",
    [(PF_TOGGLE,"incl","Auto-mode (best if image includes parts of unexposed film):", True),
    (PF_TOGGLE,"contr_enh","Contrast enhancement:", True),
    (PF_SLIDER,"step","Speed (only relevant for auto-mode):",10,(1,100,10)),
    (PF_TOGGLE,"merge_layers","Merge layers:", False),
    ],
    [],
    CNI)
main()
Там описание и блок регистрации больше занимает, чем сам плагин.
ASUS N53Jg /Linux Mint 18.2 Mate 64-bit
try_out M
Аватара
Откуда: РФ, Башкирия
Сообщения: 2337

  • 1

я тоже не в теме совершенно, но посмотреть могу )))

Сообщение #31 denis8424 » 09.11.2016, 00:41

длиннотекст

Код: Выделить всё
    def CNI(img, drw, incl, contr_enh, step, merge_layers):
    # img - то изображение, с которым работаем
    # drw - похоже на буфер изображения с методами работы c ним
    # incl - без понятия, как и  contr_enh
    # step - шаг по-русски, ниже понятно зачем
    # merge_layers - объединять ли слои???
   
    # я так понял, pdb - это база данных изображения с которым работаем, а может и всех открытых
    # вроде bpy.data в блендере
   
    pdb.gimp_image_undo_group_start(img)
    # не знаю, что такое incl
    if incl==True:
        # минимальная яркость
        LightMax=0
       # это просто выводит пользователю сообщение
        pdb.gimp_message("Processing ..." +"\n")
        # перебор с помощью двух циклов пикселей в изображении, но не подряд, а в соответствии с заданным шагом step, например, каждый третий пиксель.
        for i in range (1, img.width-2,step):
            for j in range (1, img.height-2,step):
                # ниже определение функции, возвращает в общем цвет
                val = getpixel(drw, i, j)
                # тоже определение этой функции ниже, но возвращает ярчайший цвет
                lightness = getV(val[0],val[1],val[2])
               # если яркость пикселя больше чем минимальная яркость определенная выше
                if lightness > LightMax:
                    # минимальная яркость теперь равна яркости пикселя и это значение перейдет на следующие итерации, пока не кончатся пиксели или не попадется более яркий
                    LightMax=lightness
                    # присваиваем временным цветам полученные значения цвета из функции
                    LMax_R=val[0]
                    LMax_G=val[1]
                    LMax_B=val[2]
        # присваиваем цвету переднего плана (цвет кисти) значения временного цвета
        gimp.set_foreground(LMax_R,LMax_G,LMax_B)
   # ложная ветвь условия, если incl ложно
    else:
        # тогда временные цвета равны значению текущего цвета переднего плана
        fgc = gimp.get_foreground()
        LMax_R=fgc[0]
        LMax_G=fgc[1]
        LMax_B=fgc[2]
    # создаем копию слоя
    layer_copy = drw.copy(True)
    # задаем режим наложения?
    layer_copy.mode = NORMAL_MODE
    # задаем ему наме
    layer_copy.name = "bias removed"
    # и добавляем эту копию к изображению
    img.add_layer(layer_copy,0)
    # похоже, что берем этот слой
    drw=img.layers[0]
    # и рисуем в нем кривые
    pdb.gimp_curves_spline(drw, 1, 4,[0, 0, LMax_R, 255])
    pdb.gimp_curves_spline(drw, 2, 4,[0, 0, LMax_G, 255])
    pdb.gimp_curves_spline(drw, 3, 4,[0, 0, LMax_B, 255])
    # это условие полностью ускользает от меня
    if contr_enh==True:
        # изменить уровень чего-то(интенсивности цвета???) в доступной области
        pdb.gimp_levels_stretch(drw)
    # сообщить пользователю, что все хорошо
    pdb.gimp_message("Done!")
    # опять добавляем слой
    layer_copy = drw.copy(True)
    layer_copy.mode = NORMAL_MODE
    layer_copy.name = "inverted"
    img.add_layer(layer_copy,0)
    drw=img.layers[0]
    # инвертируем слой
    pdb.gimp_invert(drw)
   
    if merge_layers==True:
        # объединяем слои, текущий и первый ниже
        pdb.gimp_image_merge_down(img,drw,0)
        # аналогично, шеф
        pdb.gimp_image_merge_down(img,img.layers[0],0)
    # это касается группы отменяемых действий, закрывается группа, наверное, чтобы полностью отменить работу за один клик
    pdb.gimp_image_undo_group_end(img)

# ну там написано, что возвращает цвет пикселя
def getpixel(drw, x, y):
    """ gets colour values """
   # получить "плитку", которая содержит пиксель
    tile = drw.get_tile2(False, x, y)
    # смещение по икс, остаток от деления позиции текущего пикселя почему-то на 64
    x_offset = x % 64
    # то же, но для Y -позиции пикселя
    y_offset = y % 64
    # теперь из плитки получаем по позиции пиксель, нафига оно так надо?
    pixel = tile[x_offset, y_offset]
    # создаем пустой список значений цвета
    values = []
    # циклом достаем значения из пикселя и записываем в список
    for i in range(len(pixel)):
        values.append(ord(pixel[i]))
    # отдаем список в точку вызова, гораздо выше
    return values

# получить максимальное значение цвета
def getV(R, G, B):
    # следующая строчка считается, но результат НЕ ИСПОЛЬЗУЕТСЯ  :facepalm:
    mi=min(R,G,B)*1.0
    # а вот здесь из списка значений цвета извлекается тот цветовой компонент, величина которого максимальная, например R
    ma=max(R,G,B)*1.0
    #  значение компонента делится и умножается, а потом округляется
    v = round(ma/255*100)
    # и отправляется в точку вызова
    return v
denis8424
Аватара
Сообщения: 705

  • 1

Сообщение #32 girafenok » 09.11.2016, 03:20

try_out,
Пару ошибок исправил, код почистил (не удержался).
В gimp 2.8.4 под ubuntu работает.

http://storage.gabbler.ru/cni.py
girafenok
рендер-фермер
рендер-фермер
Сообщения: 653

Сообщение #33 try_out » 09.11.2016, 07:38

О, спасибо, люди добрые. Сейчас буду изучать.

Добавлено спустя 2 часа 33 минуты:
denis8424, спасибо, но из пояснений всё же не понятно, что происходит с изображением. Pdb — база данных процедур GIMP.

Добавлено спустя 1 минуту 29 секунд:
girafenok писал(а):В gimp 2.8.4 под ubuntu работает
Спасибо. Ставлю GIMP 2.8 в виртуалку, потому что постоянно работаю в 2.9, ещё раз переустанавливать не хочется.
ASUS N53Jg /Linux Mint 18.2 Mate 64-bit
try_out M
Аватара
Откуда: РФ, Башкирия
Сообщения: 2337

Сообщение #34 girafenok » 09.11.2016, 10:56

try_out писал(а):из пояснений всё же не понятно, что происходит с изображением.
Собственно в скрипте никаких откровений нет, все можно сделать ручками.
Сначала происходит обработка изображения кривыми и уровнями для коррекции цветового диапазона и конраста от возможных искажений, потом собственно инвертирование.
girafenok
рендер-фермер
рендер-фермер
Сообщения: 653

Сообщение #35 try_out » 09.11.2016, 11:38

girafenok писал(а):все можно сделать ручками
Да, это понятно. Только хотелось бы знать, что именно делать. Какие параметры кривых и уровней.
ASUS N53Jg /Linux Mint 18.2 Mate 64-bit
try_out M
Аватара
Откуда: РФ, Башкирия
Сообщения: 2337

  • 1

Сообщение #36 girafenok » 09.11.2016, 11:53

try_out,
Если стоит галочка Auto mode, то сначала определяется максимально яркая точка, иначе за максимально яркую точку берется цвет переднего плана. Потом применяются кривые для каждого из RGB каналов. Кривая определяется двумя точками, первая всегда (0,0), вторая - (значение соответствующего канала максимально яркой точки, 255).
Если стоит галочка contrast enhancemnet, то применяются уровни в автоматическом режиме.
Затем инвертиуется изображение станадртной функцией.
Потом слои сливаются, если указан merge layers.
girafenok
рендер-фермер
рендер-фермер
Сообщения: 653

Сообщение #37 try_out » 09.11.2016, 12:06

Там не просто применяются уровни с кривыми и инвертируется. После работы фильтра цвет переднего плана становится оранжевым, по цвету маски негатива. По всей видимости, накладывается дополнительный слой для компенсации оранжевой маски. К тому же непонятно тогда, за что отвечает параметр Speed и что за порядок пикселов им устанавливается.

Добавлено спустя 3 минуты 5 секунд:
Если установить Speed в маленькое значение, например в единицу, фильтр работает очень долго. А кривые, уровни и инверсия работают очень быстро, как известно. Что делает программа всё это время?
ASUS N53Jg /Linux Mint 18.2 Mate 64-bit
try_out M
Аватара
Откуда: РФ, Башкирия
Сообщения: 2337

Сообщение #38 girafenok » 09.11.2016, 12:25

try_out писал(а):Что делает программа всё это время?
Определяет самую яркую точку изображения. С учетом большого количества пикселей и медленности встроенной математики Питона это занимает большое колиечство времени. В приницпе этот кусок можно переписать на модуле numpy и все будет существенно быстрее, но мне лениво.
Параметр speed отвечает за выборку пикселей, на основе которых определяется самая яркая точка. Если стои 1, то максимум ищется из всех пикселей, если стоит 10, то максимум ищется по каждой десятой точке изображения. В Первом случае просматриваются все пиксели, во втором в 10 раз меньше, отсюда и существенная разница в скорости.

Добавлено спустя 11 минут 58 секунд:
Я вообще не сильно подкован в обработке изображений, но что-то мне подсказывает, что самописная часть скрипта делает примерно тоже, что и функция баланс белого.
girafenok
рендер-фермер
рендер-фермер
Сообщения: 653

Сообщение #39 denis8424 » 09.11.2016, 16:59

try_out, извини, если бы у меня был был быстрый интернет, то я нашел бы вменяемый python API gimp, а так я сам не понял что оно делает и зачем оно надо. Что понял, то и написал, я никогда не программировал под gimp, у меня и с программировпнием под блендер проблемы то и дело вылезают.
denis8424
Аватара
Сообщения: 705

Сообщение #40 Korchy » 04.12.2016, 23:05

Такой вот непонятный момент выплыл. Мне нужно несколько раз в цикле запустить рендер. Вешаю обработчик на завершение, запускаю и в обработчике (по завершении) запускаю снова. Примерно так:
Код: Выделить всё
class loopRender(bpy.types.Operator):
    bl_idname = "render.loop_render"
    def execute(self, context):
        bpy.app.handlers.render_complete.append(onRenderFinish)
        a = bpy.ops.render.render('INVOKE_DEFAULT')
        print(a)
def onRenderFinish(scene):
    bpy.app.handlers.render_complete.remove(onRenderFinish)
    bpy.ops.render.loop_render()
Вроде бы все работает, но периодически bpy.ops.render.render('INVOKE_DEFAULT') возвращает {'CANCELLED'}, рендер не выполняется и соотв-но весь цикл останавливается.
Как вообще можно понять, по какой причине bpy.ops.render.render возвращает cancelled ?
Korchy M
Аватара
Сообщения: 2222



Пред.След.

Вернуться в Плагины

Кто сейчас на форуме (по активности за 5 минут)

Сейчас этот раздел просматривают: 1 гость

cron