Задать вопрос
  • Почему модель обнаружения объектов YOLO работает медленно?

    Vindicar
    @Vindicar
    RTFM!
    Первый момент тебе уже подсказали - потоки в питоне не помогут.
    Второй момент - я фз зачем тебе supervision, попробуй сначала голый yolo, и в частности, попробуй запустить его на видяхе.
    Третий момент - yolo бывают разного размера. Чем больше модель, тем точнее, но и тем медленнее.

    Вот пример, с которым я работал

    """
    Принимаем видео с вебкамеры, и пытаемся сегментировать его с помощью YOLOv8.
    Выводим в окне маски объектов.
    Потребуется установить пакеты следующей командой:
    pip install ultralytics
    """
    from sys import argv
    from pathlib import Path
    
    import numpy
    import cv2
    import torch
    
    COLORS = [
        (64, 128, 128),
        (128, 64, 64),
        (64, 128, 64),
        (64, 64, 128),
        (128, 64, 128),
    ]
    
    VIDEO_SOURCE = 0  # 0 - вебкамера. Строка - имя файла или URL видео потока.
    script_dir = Path(argv[0]).parent.resolve()
    # определяем, на каком устройстве будет выполняться модель
    device = (
        'cuda' if torch.cuda.is_available() else
        'mps' if torch.backends.mps.is_available() else
        'cpu'
    )
    
    import ultralytics
    
    
    def text_at(  # рисует текст по центру
            img: numpy.ndarray,  # на чём
            text: str,  # что
            pos: tuple[int, int],  # где центр
            font=cv2.FONT_HERSHEY_COMPLEX,  # каким шрифтом
            font_scale: float = 1.0,  # в каком масштабе (не кегль!)
            font_thickness: int = 2,  # насколько толстые линии
            text_color=(255, 0, 0),  # каким цветом
            bg_color=None  # на каком фоне
            ):
        x, y = pos
        (text_w, text_h), _ = cv2.getTextSize(text, font, font_scale, font_thickness)
        if bg_color is not None:
            cv2.rectangle(img,
                          (x - text_w // 2, y - text_h // 2),
                          (x + text_w // 2, y + text_h // 2),
                          bg_color, -1)
        cv2.putText(img,
                    text,
                    (x - text_w // 2, y + text_h // 2 - 1),
                    font, font_scale, text_color, font_thickness)
    
    
    model = ultralytics.YOLO(
        script_dir / 'yolov8m-seg.pt'  # имя файла модели указывает на её тип
    )
    
    # цикл работы с видео
    video = cv2.VideoCapture(VIDEO_SOURCE)
    if not video.isOpened():
        raise SystemExit('Не удалось открыть источник видео')
    overlay_image: numpy.ndarray = numpy.zeros(  # изображение-оверлей для отображения разметки кадра
        (
            int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)),
            int(video.get(cv2.CAP_PROP_FRAME_WIDTH)),
            3
         ),
        numpy.uint8
    )
    
    try:
        while True:
            success, frame = video.read()  # читаем кадр
            if not success:
                raise SystemExit('Видео закончилось')
            # для YOLO предобработка не требуется
            outputs = model.predict(  # получаем отклик сети
                source=frame,
                conf=0.15,  # минимальная степень уверенности в объекте
                device=device,  # устройство
                verbose=False  # чтобы не спамило логом в консоль
            )
            output = outputs[0]  # модель всегда возвращает список результатов, даже для одного изображения
            if output.masks is not None:  # если были получены маски
                masks = output.masks.cpu().xy  # список масок как координат вершин многоугольников (контуров)
                boxes = output.boxes.cpu()  # ограничивающие прямоугольники
                image_output = []  # список аннотаций, которые нужно будет вывести
                for box, mask in zip(boxes, masks):  # формируем список аннотаций
                    class_id = int(box.cls.item())  # номер класса, соответствующего очередной области
                    class_name = model.names[class_id]  # имя класса, соответствующее области
                    pts = numpy.array(mask).astype(numpy.int32)  # массив координат точек контура Nx2
                    image_output.append((class_id, class_name, pts))
                overlay_image.fill(0)  # очищаем изображение-оверлей
                for (class_id, class_name, pts) in image_output:  # закрашиваем каждую маску цветом
                    cv2.fillPoly(
                        img=overlay_image,
                        pts=pts[numpy.newaxis, ...],  # для работы fillPoly() массив должен быть вида 1xNx2
                        color=COLORS[class_id % len(COLORS)],  # цвет массива определяем по номеру класса для стабильности
                    )
                for (class_id, class_name, pts) in image_output:  # выводим надписи отдельным циклом, чтобы их не закрасило
                    M = cv2.moments(pts)  # вычисляем моменты контура
                    # используем их для расчёта координат центроида контура
                    cX = int(M["m10"] / M["m00"])
                    cY = int(M["m01"] / M["m00"])
                    # выводим имя класса
                    text_at(overlay_image, class_name,
                            (cX, cY),
                            text_color=(255, 255, 255),
                            bg_color=(1, 1, 1))
                # накладываем оверлей с разметкой на кадр
                alpha = 0.8  # вес оверлея, чем меньше - тем он прозрачнее.
                apply = overlay_image > 0  # изменяем только те части кадра, где есть оверлей
                frame[apply] = alpha * overlay_image[apply] + (1 - alpha) * frame[apply]  # обожаю numpy
            cv2.imshow('Press Esc to exit', frame)  # показываем результат
            if cv2.waitKey(10) == 27:  # если пользователь нажал Esc, выходим
                break
    finally:
        video.release()  # не забываем отключиться от источника видео в итоге

    Ответ написан
    Комментировать
  • Никакой vpn не работает ни amnezia vpn wg, tor, vless + reality?

    Vindicar
    @Vindicar
    RTFM!
    Имей ввиду пару вещей.
    1. Пинг -1 в клиенте-обёртке типа V2RAY - ни о чём. У меня такой пинг уже года полтора, а туннель работает.
    2. Мог засветить IP адрес если ходил через туннель на российские/беларусские/иже с ними ресурсы. Моему серверу некоторое время назад хорошо так резали скорость из-за неосторожности. Сменил IP на VPS, где туннель поднят, теперь осторожничаю. Настроил роутинг в клиенте.
    3. Есть разница, как настроен REALITY. Насколько я понимаю, self-steal (когда маскируешься под собственный сайт, развёрнутый на том же домене) работает более надёжно.
    Ответ написан
  • Как сделать нажатие кнопки при парсинге в python?

    Vindicar
    @Vindicar
    RTFM!
    Да, можно.
    Используй консоль разработчика в браузере, чтобы понять, какой именно запрос отправляется на сервер. Обычно нужно выяснить метод (GET/POST), URL, параметры, тело. Либа requests и аналоги позволяют всё это задать, просто почитай доки по конкретной теме.
    Ответ написан
    Комментировать
  • Как запустить отдельный скрипт в субпроцессах, получив интерфейс типа concurrent.futures.Executor?

    Vindicar
    @Vindicar Автор вопроса
    RTFM!
    Плохая новость - готового решения сформулированной в вопросе задачи, похоже, нет. Нужно использовать брокеры или что-то подобное.

    Хорошая новость - я снова попался на проблему XY. Всё нормально, если спрятать тяжёлые импорты в теле основной программы вот так:
    # ------------------- main.py -------------------
    async def main():
        import urllib3  # как бы тяжелый модуль, нужный только главному скрипту
        import importlib
        ...  # получаем исходные данные
        # динамически подтаскиваем нужный модуль
        mod = importlib.import_module('modules.submodule')  
        m = mod.Module(x=-1)  # используем класс из него
        # этот метод под капотом использует мультипроцессинг
        results = await m.run_tasks(some_data)
        ...  # что-то делаем с результатами
    
    if __name__ == '__main__':
        import asyncio
        asyncio.run(main())
    
    # ------------------- modules/submodule.py -------------------
    import asyncio
    import concurrent.futures
    import time
    import sys
    import os
    
    class Module:
            def __init__(self, x):
            self.x = x
    
        def __repr__(self) -> str:
            return f'{self.__class__.__name__}(x={self.x!r}) {hex(id(self)).upper()} @ {os.getpid()}'
    
        async def run_tasks(self, data):
            self.x = len(items)
            print(f'Using instance {self!r}')
            loop = asyncio.get_running_loop()
            pool = concurrent.futures.ProcessPoolExecutor(4)  # где будем исполнять таск
            # готовим таски к исполнению
            futures = [loop.run_in_executor(pool, self.worker_func, item) for item in data]
            # ждём их завершения
            return await asyncio.gather(*futures, return_exceptions=True)
    
        def worker_func(self, item):
            # инстансы разные, разумеется, но их состояние, похоже, клонируется в новые процессы...
            print(f'Using instance {self!r}. urllib3', 'is' if 'urllib3' in sys.modules else 'is not', 'present', flush=True)
            time.sleep(0.1)  # имитируем напряжённую работу
            return repr(item)
    Ответ написан
    Комментировать
  • Как правильно спроектировать эту функцию?

    Vindicar
    @Vindicar
    RTFM!
    Ты неверно понимаешь, как работает overload. Это тупо подсказка для IDE - "эту функцию можно вызывать вот так, и она вернёт вот это, или же вот этак, и она вернёт вон то". Тело функции, завёрнутой в overload, игнорируется. А последнее объявление функции не должно иметь overload, и оно как раз и будет реализовывать все варианты. Так что итоговая реализация всё равно будет иметь if внутри. Да и в твоём случае overload никчему, так как другие-то параметры не отличаются.

    Ты мог бы, конечно, упороться, и сделать functools.singledispatch - при условии, что ты message переставишь первым параметром. Но, имхо, в этом нет практической необходимости, всё это делается в разы проще.

    async def show_cpanel(
        state: FSMContext,
        message_source: typing.Union[Message, CallbackQuery]  # значение одного из двух типов, но обязательное!
    ) -> None:
        '''
        Send control panel to current user
        '''
        this_user = message_source.from_user.id  # и Message, и CallbackQuery имеют from_user
        message = message if isinstance(message_source, Message) else message_source.message
    
        profile_is_visible = await req.check_profile_visible(this_user)
        message_id = (await message.answer(
            '<b>' + _('Панель управления') + '</b>',
            parse_mode='HTML',
            reply_markup=kb.control_panel(profile_visible=profile_is_visible)
        )).message_id
        await state.update_data(cpanel_message_id=message_id)
        logger.info(f'Admin #{this_user} opened control panel')

    Вот и всё. Твой вариант
    callback_query: Optional[CallbackQuery] = None,
        message: Optional[Message] = None,

    плох тем, что из него совершенно не очевидно, что хотя бы один из параметров не должен быть None.

    EDIT: Хотя я не вполне понял идею насчёт callback.message. Если я верно помню, этот атрибут хранит ссылку на сообщение от бота, содержащее кнопку, для которой был вызван callback. Поэтому его from_user по идее будет заведомо указывать на бота. Так что да, лучше последуй совету Everything_is_bad и переделай функцию так, чтобы она принимала пользователя и сообщение отдельными параметрами. А их значения определяй там, где ты функцию вызываешь.
    Ответ написан
    5 комментариев
  • SSH +reverse https proxy, при чём тут openSSL(VPN)?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала, OpenSSL - это (если на пальцах) библиотека+утилита для шифрования трафика. Ею или её аналогами пользуется примерно всё, что хочет установить шифрованный канал. Помимо использования её как библиотеки, ты можешь использовать её как утилиту, в духе "слушай адрес+порт A.B.C.D:X, все входящие подключения расшифровывай(для сервера)/шифруй(для клиента), после чего перенаправляй на адрес+порт E.F.G.H:Y". Тогда таким образом можно создать шифрованный туннель, через который пускать трафик какого-то другого приложения.

    Но постановка задачи вызывает вопросы. Что значит "только по HTTPS"? Каким именно образом это правило контролируется? Не зная ответа на этот вопрос, решения не найдёшь.

    Если "нужно увидеть TLS-хэндшейк", то да, подход на базе OpenSSL может сработать, но его нужно будет настроить и на сервере, и на клиенте. На сервере - OpenSSL должна слушать порт и перенаправлять расшифрованные соединения на порт SSH сервера. На клиенте - OpenSSL должна слушать порт, и пробрасывать зашифрованные соединения на адрес+порт, который случает OpenSSL на сервере.

    Но имей ввиду, что SSH сервера вроде OpenSSH (но вроде не Dropbear) и так используют OpenSSL для защиты своего трафика. Хотя при этом всё равно есть различия между тем, как устанавливается соединение при HTTP+TLS и при SSH, так что может в таком туннеле и будет смысл.

    Но соответствие HTTPS может контролироваться более жёстко. Нужно использовать заданный хостером HTTP-прокси/реверс-прокси для работы с сетью? Или, скажем, слишком долго висящие соединения принудительно рвутся? Или TLS-хэндшейк проверяется на соответствие популярным браузерам? Во всех этих случаях простым туннелем ты не обойдёшься.
    Ответ написан
    6 комментариев
  • Как в командной строке сохранить полный путь к файлу для вставки его в другом месте при смене директории?

    Vindicar
    @Vindicar
    RTFM!
    Если задача всплывает часто, освой Midnight Commander или иной двухпанельный файловый менеджер. Он запускается в терминальном режиме и прекрасно подходит для таких ситуаций.
    Ответ написан
    Комментировать
  • Можно ли применить условное форматирование к графику/диаграмме?

    Vindicar
    @Vindicar
    RTFM!
    Нет, нельзя. Построить два графика (один только с растущими интервалами, один только с убывающими) тоже тяжело, так как число точек в них не будет совпадать с числом точек в исходных данных.
    Если ты переделаешь исходные данные в такой вид:
    X Y
    1 10
    1 10
    2 15
    2 15
    3 12
    3 12
    ...
    То тогда можно будет провернуть трюк с двумя сериями в одних осях. Но это тоже тот ещё гемморой.
    Ответ написан
  • Не копятся ли строки в памяти при работе с python?

    Vindicar
    @Vindicar
    RTFM!
    Попробовал накидать скрипт для проверки:
    import tracemalloc
    
    tracemalloc.start()
    
    for i in range(4):
        snapshotA = tracemalloc.take_snapshot()
        s = f'Строка №{i+1}'
        snapshotB = tracemalloc.take_snapshot()
        s = s.replace('Строка', 'Другая строка')
        snapshotC = tracemalloc.take_snapshot()
        print('>', s)
        statab = snapshotB.compare_to(snapshotA, 'lineno')
        print("[A->B]")
        for stat in statab[:10]:
            if stat.traceback[0].filename == __file__:
                print(stat)
        statbc = snapshotC.compare_to(snapshotB, 'lineno')
        print("[B->C]")
        for stat in statbc[:10]:
            if stat.traceback[0].filename == __file__:
                print(stat)

    Если я верно интерпретирую вывод, неиспользуемые строки "умирают" на каждой итерации.
    Но ситуация усложняется, если на них есть ссылки в других местах, особенно если имеют место циклические ссылки.
    Ответ написан
  • Как вывести время в шаблоне Django в соответствии с часовым поясом?

    Vindicar
    @Vindicar
    RTFM!
    Не работал с джангой, но...
    time = datetime.time(11, 00, 0, tzinfo=timezone(timedelta(hours=0)))

    Это время в UTC.
    А вот что используется при выводе... может, как раз твоё TIME_ZONE = 'Europe/Moscow'?
    Ответ написан
  • Извлечение таблиц со спецификациями из PDF чертежей металлоконструкций - решаемо? Или я встрял?

    Vindicar
    @Vindicar
    RTFM!
    Имхо без комбинации подходов ловить вообще нечего. Найди инструмент (и набор предобработок), который найдёт тебе разметку таблиц, выдерни содержимое отдельных ячеек, и подбирай предобработки+инструмент, который будет распознавать ячейки.
    Ответ написан
    Комментировать
  • Как ловить exceptions в библиотеке которую я не использую напрямую?

    Vindicar
    @Vindicar
    RTFM!
    где я его должен ловить учитывая что саму urllib3 я через import не подключаю

    Ну так подключай её в свой код и лови в своём коде. from urllib3.exceptions import LocationParseError
    Раз requests её уже использует, затраты на import в твоём скрипте будут околонулевые.
    requests поступает вполне логично, не изобретает велосипед (т.е. своё исключение с тем же смыслом), а задействует тип исключения из используемой стандартной библиотеки языка.
    Ответ написан
    Комментировать
  • Какую библиотеку лучше использовать для discord бота на python?

    Vindicar
    @Vindicar
    RTFM!
    discord.py уже давно начал деятельность обратно. На момент написания этого коммента на гитхабе последний коммит 3 месяца назад, issue закрыт считанные дни назад.
    Ответ написан
    Комментировать
  • Почему возникают ошибки несовместимости версий Python и как решить проблему с установкой PySimpleGUI?

    Vindicar
    @Vindicar
    RTFM!
    Разобраться, какие версии библиотек всё-таки нужны. Ты не написал, для каких пакетов возникает ошибка.
    Возможно, стоит смягчить требования - например, не требовать определённую версию пакета, а любую, или ограничится только первой цифрой. Скажем, вместо somepackage==1.2.3 указать somepackage==1.* или вообще somepackage без версии.
    Ответ написан
    Комментировать
  • Можно ли улучшить приложенный AutoEnum (см. код ниже) в python?

    Vindicar
    @Vindicar
    RTFM!
    Очевидно, что декоратор должен находить и заменять объявленные константы сам, так как всё остальное уже отработало.
    Если хочешь полагаться на метакласс, ему можно передавать аргументы при наследовании. ЕМНИП:
    class MyMeta(type):
        def __new__(metacls, name, bases, namespace, **kwargs):
            print(f"__new__(): {name}({kwargs})")
            return super().__new__(metacls, name, bases, namespace)
    
    class MyBase(metaclass=MyMeta):  # __new__(): MyBase({})
        pass
    
    class MyClass(MyBase, foo='bar'):  # __new__(): MyClass({'foo': 'bar'})
        pass

    А вообще я не вижу выигрыша в твоём классе. Статическая типизация, к слову, барахлить не начнёт? Среда разработки будет правильно опознавать TestAutoEnum.FIRST как экземпляр TestAutoEnum?
    Ответ написан
    Комментировать
  • Как осознано подменять url?

    Vindicar
    @Vindicar
    RTFM!
    Я это делаю с помощью Greasmonkey (для хрома - Tampermonkey). Попробуй адаптировать под свою задачу.
    Ключевой момент - run-at. Но сразу скажу, этот скрипт у меня барахлил.
    // ==UserScript==
    // @name     Force Learn.MS to English
    // @version  1
    // @grant    none
    // @run-at   document-start
    // @include  https://learn.microsoft.com/ru-ru/*
    // ==/UserScript==
    
    (function() {
      'use strict';
      const new_url = window.location.toString().replace('://learn.microsoft.com/ru-ru/', '://learn.microsoft.com/en-us/');
      window.location.replace(new_url);
    })();
    Ответ написан
    2 комментария
  • Почему api золотого яблока блокирует все запросы?

    Vindicar
    @Vindicar
    RTFM!
    Вполне может быть, что банят за запросы не из диапазонов домашних и мобильных провайдеров.
    Ответ написан
    Комментировать
  • Как можно ускорить выполнение этого кода?

    Vindicar
    @Vindicar
    RTFM!
    Я верно понимаю, что нужно найти слова не короче заданной длины, которые можно составить из букв данного слова?
    Во-первых, может иметь смысл сразу отбросить слова, содержащие буквы не из данного слова, чтобы в дальнейшем анализировать только сравнительно допустимые слова. Также может иметь смысл использовать для подсчёта количества букв collections.Counter.
    Например, так
    from collections import Counter
    
    main_word = 'АКСИЛИРОВАНИЕ' + '\n'  # основное слово + перенос строки, чтобы не вызывать str.rstrip()
    main_set = frozenset(main_word)  # множество букв слова без учёта повторов
    main_len = len(main_word)
    min_length = 8
    with open('D:\\Program Files\\Text\\слова\\1.txt', 'r') as f:
        candidates = [  # слова-кандидаты, состоящие из тех же букв и подходящие по длине
            (word, Counter(word))  # само слово и его состав по буквам
            for word in f  # для всех слов в файле
            # проверяем длину слова и соответствие набора букв без учёта их количества
            if min_length<=len(word)<=main_len and main_set.issuperset(word)  
        ]
    main_counter = Counter(main_word)  # подсчёт числа букв в основном слове
    results = [  # итоговый результат
        word  # те слова
        for word, counter in candidates  # из числа слов-кандидатов
        if all(counter[key] <= main_counter[key] for key in counter)  # у которых нет превышения ни по одной букве
    ]


    Во-вторых, важна структура файла. Например, если строки в файле расположены по возрастанию длины, мы можем попробовать быстренько пропустить короткие слова в начале файла, обработать то, что идёт после, и остановиться, когда дойдём до слов длиннее заданного.
    В-третьих, если вместо текстового файла использовать, скажем, pickle-файл, и хранить в нём предрассчитанные количества букв для каждого слова, то это потенциально может ускорить процесс.
    Но чтобы проверить скорость работы кода, нужна ссылка на пример файла со словами. И уточни, как обрабатывается буква Ё.
    Ответ написан
    Комментировать
  • Как создать task в __init__ asyncio?

    Vindicar
    @Vindicar
    RTFM!
    Тебе довольно прямо сказано: "AttributeError: loop attribute cannot be accessed in non-async contexts."
    Иными словами, атрибут Bot.loop (ссылку на рабочий цикл asyncio) можно читать, только если ты находишься внутри async def функции, прямо или косвенно.
    Причина простая - asyncio.run() или эквивалентная функция как раз создаёт и запускает рабочий цикл asyncio (обычно называемый event loop или просто loop). Если рабочий цикл ещё не создан, то что должен вернуть атрибут Bot.loop? На этот вопрос просто нет правильного ответа. Поэтому доступ к атрибуту блокируется.

    И насчёт решения тоже подсказано: "Consider using either an asynchronous main function and passing it to asyncio.run or using asynchronous initialisation hooks such as Client.setup_hook". Иными словами, тебе нужно сделать так, чтобы твой код, обращающийся к Bot.loop, выполнялся в асинхронной (async def) функции. Это можно сделать двумя способами.

    Первый: вынести твой код из конструктора (который не может быть асинхронным) в отдельный метод. Например, Bot (и его предок Client) позволяют вызвать асинхронный метод on_ready() при запуске бота. Тут есть много оговорок - в частности, on_ready() может быть вызван неоднократно, если есть проблемы с соединением. Это нужно иметь ввиду.

    Второй: завернуть вызов всего твоего конструктора в асинхронный метод. Упрощённо, вместо
    import discord
    from discord.ext import commands
    
    intents = discord.Intents.default()
    intents.members = True
    intents.message_content = True
    
    bot = commands.Bot(command_prefix='!', intents=intents)
    # ...
    bot.run('token')

    можешь попробовать что-то вроде
    import discord
    from discord.ext import commands
    
    
    async def main():
        # асинхронная функция может быть выполнена ТОЛЬКО внутри рабочего цикла
        # значит, рабочий цикл уже точно существует и выполняется
        intents = discord.Intents.default()
        intents.members = True
        intents.message_content = True
        # конструктор сам по себе не асинхронный, но он выполняется в асинхронном контексте
        bot = commands.Bot(command_prefix='!', intents=intents)  
        # ...
        # мы уже в асинхронной функции, поэтому используем await start() вместо run()
        await bot.start('token')  # main() не завершит работу, пока бот не завершит работу
    
    
    if __name__ == '__main__':
        asyncio.run(main())  # создаём рабочий цикл. он будет работать, пока main() не завершит работу


    Но я соглашусь с Everything_is_bad - сначала разберись, как работает asyncio. Строить сложные конструкции с несколькими долгоживущими корутинами методом проб и ошибок - это слишком муторно.

    Ну и очень большой вопрос от меня: ты, я вижу, мастеришь систему плагинов. Чем тебя не устроили коги?
    Ответ написан
    3 комментария