Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docker镜像中的configs文件挂载问题 #860

Open
dahaox3 opened this issue Feb 28, 2025 · 5 comments
Open

docker镜像中的configs文件挂载问题 #860

dahaox3 opened this issue Feb 28, 2025 · 5 comments

Comments

@dahaox3
Copy link

dahaox3 commented Feb 28, 2025

根据comicread插件文档部署manga-image-translator的docker镜像后,翻译漫画时出现白底问题。

Image

查阅manga-image-translator文档与issue后发现是inpainter未启用的原因,需要挂载configs文件。请问我要如何做才能在docker上挂载configs文件呢?我在yml中填写--config-file参数,控制台报错manga_image_translator_gpu | main.py: error: unrecognized arguments: --config-file

@popcion
Copy link
Contributor

popcion commented Feb 28, 2025

所以我不用docker了😂
希望有人修吧,帮顶
你也可以用映射,先把docker文件显示出来,在compose文件内修改:

    volumes:
      - ./result:/app/result
      - ./manga_translator:/app/manga_translator

把docker里面config.py文件内的
inpainter: Inpainter = Inpainter.none
改成
inpainter: Inpainter = Inpainter.lama_large
然后重新运行

@dahaox3
Copy link
Author

dahaox3 commented Mar 1, 2025

所以我不用docker了😂 希望有人修吧,帮顶 你也可以用映射,先把docker文件显示出来,在compose文件内修改:

    volumes:
      - ./result:/app/result
      - ./manga_translator:/app/manga_translator

把docker里面config.py文件内的 inpainter: Inpainter = Inpainter.none 改成 inpainter: Inpainter = Inpainter.lama_large 然后重新运行

非常感谢你,但我今天部署了python版的,发现也挂载不了config文件。
我的操作命令是python ./server/main.py --use-gpu --config-file D:\manga-image-translator\config.json 他报错不识别--config-file参数
请问大佬有什么头绪吗,我用local模式能识别--config-file参数

@popcion
Copy link
Contributor

popcion commented Mar 1, 2025

很可惜现在web mode没有--config-file参数,也没有--font-path pre-dict post-dict参数,这应该是最影响使用的,导致现在的web mode效果很差基本没法使用。但是一直没人修,我也不太会修,对新版不了解基本没用过,等其他大佬帮忙吧。你也可以再开一个相关的issue说明情况,看看有没有使用web模式的用户把这4个参数重新加进去。最好用英文说明,英文用户应该更多。
#822

你可以暂时去用旧版
https://github.com/sugarshop/manga-image-translator
然后把旧版中chatgpt.py里的_translate函数和后两个_request_translation函数替换成下面的,然后按照旧版的文档使用web mode:

ENG:
"It's a pity that the current web mode does not have the --config-file parameter, nor the --font-path, pre-dict, and post-dict parameters. This should be the most impactful issue, resulting in poor performance of the web mode, making it basically unusable. However, no one has fixed it yet, and I don't know how to fix it myself, so I'll wait for other experts to help.

You can temporarily use the old version at this link and replace the _translate function and the last two _request_translation functions in chatgpt.py from the old version with the ones below, then use it according to the documentation of the old version.:"

    async def _translate(self, from_lang: str, to_lang: str, queries: List[str]) -> List[str]:  
        translations = [''] * len(queries)  
        self.logger.debug(f'Temperature: {self.temperature}, TopP: {self.top_p}')  
        MAX_SPLIT_ATTEMPTS = 3  # Default max split attempts  
        RETRY_ATTEMPTS = self._RETRY_ATTEMPTS  

        async def translate_batch(prompt_queries, prompt_query_indices, split_level=0):  
            nonlocal MAX_SPLIT_ATTEMPTS
            split_prefix = ' (split)' if split_level > 0 else ''  

            # Assemble prompt for the current batch  
            prompt, query_size = self._assemble_prompts(from_lang, to_lang, prompt_queries).__next__()  
            self.logger.debug(f'-- GPT Prompt{split_prefix} --\n' + self._format_prompt_log(to_lang, prompt))  

            for attempt in range(RETRY_ATTEMPTS):  
                try:  
                    # Start the translation request with timeout handling
                    request_task = asyncio.create_task(self._request_translation(to_lang, prompt))
                    started = time.time()
                    timeout_attempt = 0
                    ratelimit_attempt = 0
                    server_error_attempt = 0
                    while not request_task.done():
                        await asyncio.sleep(0.1)
                        if time.time() - started > self._TIMEOUT + (timeout_attempt * self._TIMEOUT / 2):
                            # Server takes too long to respond
                            if timeout_attempt >= self._TIMEOUT_RETRY_ATTEMPTS:
                                raise Exception('Openai servers did not respond quickly enough.')
                            timeout_attempt += 1
                            self.logger.warn(f'Restarting request due to timeout. Attempt: {timeout_attempt}')
                            request_task.cancel()
                            request_task = asyncio.create_task(self._request_translation(to_lang, prompt))
                            started = time.time()

                    # Get the response
                    response = await request_task  
                    self.logger.debug(f'-- GPT Response{split_prefix} --\n' + response)  

                    # Split response into translations  
                    new_translations = re.split(r'<\|\d+\|>', response)  
                    if not new_translations[0].strip():  
                        new_translations = new_translations[1:]  

                    if len(prompt_queries) == 1 and len(new_translations) == 1 and not re.match(r'^\s*<\|\d+\|>', response):  
                        self.logger.warn(f'Single query response does not contain prefix, retrying...(Attempt {attempt + 1})')  
                        continue  

                    # Check for error messages in translations  
                    ERROR_KEYWORDS = [  
                        # ENG_KEYWORDS
                        r"I must decline",
                        r'(i(\'m| am)?\s+)?sorry(.|\n)*?(can(\'t|not)|unable to|cannot)\s+(assist|help)',
                        # CHINESE_KEYWORDS (using regex patterns)
                        r"(抱歉,|对不起,)?我(无法[将把]|不[能会便](提供|处理)?)", 
                        r"我无法(满足|回答|处理|提供)",  
                        r"这超出了我的范围",   
                        r"我需要婉拒", 
                        r"翻译或生成", #deepseek高频
                        r"[的个]内容(吧)?", #claude高频
                        # JAPANESE_KEYWORDS
                        r"申し訳ありませんが",    
                    ]  
                    if any(keyword in t for t in new_translations for keyword in ERROR_KEYWORDS):  
                        remaining_attempts = RETRY_ATTEMPTS - attempt - 1  
                        self.logger.warn(f'Error message detected in response, remaining {remaining_attempts} time(s) before splitting the translation.')  
                        continue  

                    if len(new_translations) < query_size:  
                        # Try splitting by newlines instead  
                        new_translations = re.split(r'\n', response)  

                    if len(new_translations) < query_size:  
                        remaining_attempts = RETRY_ATTEMPTS - attempt - 1  
                        self.logger.warn(f'Incomplete response, remaining {remaining_attempts} time(s) before splitting the translation.')  
                        continue  

                    # Trim excess translations and pad if necessary  
                    new_translations = new_translations[:query_size] + [''] * (query_size - len(new_translations))  
                    # Clean translations by keeping only the content before the first newline  
                    new_translations = [t.split('\n')[0].strip() for t in new_translations]  
                    # Remove any potential prefix markers  
                    new_translations = [re.sub(r'^\s*<\|\d+\|>\s*', '', t) for t in new_translations]  

                    # for i, translation in enumerate(new_translations):
                        # if not is_valuable_text(translation):
                            # self.logger.info(f'Filtered out: {translation}')  
                            # self.logger.info('Reason: Text is not considered valuable.')  
                            # new_translations[i] = ''  

                    # Check if any translations are empty  
                    if any(not t.strip() for t in new_translations):  
                        self.logger.warn(f'Empty translations detected. Resplitting the batch.') 
                        break  # Exit retry loop and trigger split logic below 

                    # Store the translations in the correct indices  
                    for idx, translation in zip(prompt_query_indices, new_translations):  
                        translations[idx] = translation  

                    # Log progress  
                    self.logger.info(f'Batch translated: {len([t for t in translations if t])}/{len(queries)} completed.')  
                    #self.logger.debug(f'Completed translations: {[t if t else queries[i] for i, t in enumerate(translations)]}')        
                    return True  # Successfully translated this batch  

                except openai.RateLimitError:  # Server returned ratelimit response
                    ratelimit_attempt += 1
                    if ratelimit_attempt >= self._RATELIMIT_RETRY_ATTEMPTS:
                        raise
                    self.logger.warn(
                        f'Restarting request due to ratelimiting by openai servers. Attempt: {ratelimit_attempt}')
                    await asyncio.sleep(2)
                except openai.APIError:  
                    server_error_attempt += 1
                    if server_error_attempt >= self._RETRY_ATTEMPTS:
                        self.logger.error(
                            'Openai encountered a server error, possibly due to high server load. Use a different translator or try again later.')
                        raise
                    self.logger.warn(f'Restarting request due to a server error. Attempt: {server_error_attempt}')
                    await asyncio.sleep(1)
                except Exception as e:  
                    self.logger.error(f'Error during translation attempt: {e}')  
                    if attempt == RETRY_ATTEMPTS - 1:  
                        raise  
                    await asyncio.sleep(1)  

            # If retries exhausted and still not successful, proceed to split if allowed  
            if split_level < MAX_SPLIT_ATTEMPTS:  
                if split_level == 0:  
                    self.logger.warn('Retry limit reached. Starting to split the translation batch.')  
                else:  
                    self.logger.warn('Further splitting the translation batch due to persistent errors.')  
                mid_index = len(prompt_queries) // 2  
                futures = []  
                # Split the batch into two halves  
                for sub_queries, sub_indices in [   
                    (prompt_queries[:mid_index], prompt_query_indices[:mid_index]),  
                    (prompt_queries[mid_index:], prompt_query_indices[mid_index:]),  
                ]:  
                    if sub_queries:  
                        futures.append(translate_batch(sub_queries, sub_indices, split_level + 1))  
                results = await asyncio.gather(*futures)  
                return all(results)  
            else:  
                self.logger.error('Maximum split attempts reached. Unable to translate the following queries:')  
                for idx in prompt_query_indices:  
                    self.logger.error(f'Query: {queries[idx]}')  
                return False  # Indicate failure for this batch   

        # Begin translation process  
        prompt_queries = queries  
        prompt_query_indices = list(range(len(queries)))  
        await translate_batch(prompt_queries, prompt_query_indices)  

        self.logger.debug(translations)  
        if self.token_count_last:  
            self.logger.info(f'Used {self.token_count_last} tokens (Total: {self.token_count})')  
        return translations
    async def _request_translation(self, to_lang: str, prompt: str) -> str:
        messages = [
            {'role': 'system', 'content': self.chat_system_template.format(to_lang=to_lang)},
            {'role': 'user', 'content': self.chat_sample[0]},
            {'role': 'assistant', 'content': self.chat_sample[1]},
            {'role': 'user', 'content': prompt},
        ]
        
        
        try:
            response = await self.client.chat.completions.create(
                model=chatgpt-4o-latest, #在此修改模型
                messages=messages,
                max_tokens=self._MAX_TOKENS // 2,
                temperature=self.temperature,
                top_p=self.top_p,
            )
            
            # 获取响应文本
            raw_response = response.choices[0].message.content
            
            # 去除 <think></think> 标签及其内容
            think_pattern = re.compile(r'<think>.*?</think>', re.DOTALL)
            cleaned_response = re.sub(think_pattern, '', raw_response)
            
            # 删除多余的空行
            cleaned_response = re.sub(r'\n\s*\n', '\n', cleaned_response).strip()
            
            # 添加错误处理和日志
            if not hasattr(response, 'usage') or not hasattr(response.usage, 'total_tokens'):
                self.logger.warning("Response does not contain usage information")
                self.token_count_last = 0
            else:
                self.token_count += response.usage.total_tokens
                self.token_count_last = response.usage.total_tokens
                
            return cleaned_response
        
        except Exception as e:
            self.logger.error(f"Error in _request_translation: {str(e)}")
            raise

以下是把对web mode无用的参数去掉后的配置文件,如果后续有人在web mode增加了--config-file参数后可以直接用:

ENG:
"This is the configuration file with the parameters that are useless for web mode removed. If someone adds the --config-file parameter to web mode in the future, it can be used directly:"

{
  "filter_text": null,
  "render": {
    "renderer": "default",
    "alignment": "auto",
    "disable_font_border": false,
    "font_size_offset": 0,
    "font_size_minimum": -1,
    "uppercase": false,
    "lowercase": false,
    "gimp_font": "Sans-serif",
    "no_hyphenation": false,
    "font_color": null,
    "line_spacing": null,
    "font_size": null
  },
  "upscale": {
    "upscaler": "esrgan",
    "revert_upscaling": false,
    "upscale_ratio": null
  },
  "translator": {
    "no_text_lang_skip": false,
    "skip_lang": "",
    "gpt_config": null,
    "translator_chain": null
  },
  "detector": {
    "text_threshold": 0.5,
    "det_rotate": false,
    "det_auto_rotate": false,
    "det_invert": false,
    "det_gamma_correct": false,
    "box_threshold": 0.7,
    "unclip_ratio": 2.3
  },
  "colorizer": {
    "colorization_size": 576,
    "denoise_sigma": 30,
    "colorizer": "none"
  },
  "inpainter": {
    "inpainter": "lama_large",
    "inpainting_size": 2048,
    "inpainting_precision": "bf16"
  },
  "ocr": {
    "use_mocr_merge": false,
    "ocr": "48px",
    "min_text_length": 0,
    "ignore_bubble": 0
  },
  "kernel_size": 3,
  "mask_dilation_offset": 30
}

@www3199
Copy link

www3199 commented Mar 3, 2025

所以我不用docker了😂 希望有人修吧,帮顶 你也可以用映射,先把docker文件显示出来,在compose文件内修改:

    volumes:
      - ./result:/app/result
      - ./manga_translator:/app/manga_translator

把docker里面config.py文件内的 inpainter: Inpainter = Inpainter.none 改成 inpainter: Inpainter = Inpainter.lama_large 然后重新运行

非常感谢你,但我今天部署了python版的,发现也挂载不了config文件。 我的操作命令是python ./server/main.py --use-gpu --config-file D:\manga-image-translator\config.json 他报错不识别--config-file参数 请问大佬有什么头绪吗,我用local模式能识别--config-file参数

Web Mode 就修改默认设置文件吧,在\manga_translator\config.py 。WebUI的修改在 \server\index.html ,默认的WebUI没有Sakura等翻译器,需要你手动添加。

@popcion
Copy link
Contributor

popcion commented Mar 3, 2025

Web Mode 就修改默认设置文件吧

只能这样了,现在web不能指定config文件,所以我把几个影响较大的参数直接加到了ui里。还有个没解决的问题是web无法指定字体

默认的WebUI没有Sakura等翻译器

上一个pr已添加了sakura,旧版的webui是有sakura和离线模型的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants