CoolFace
Modelpublic

neuralabs/deepseek_ocr_de

sourceHugging Faceapache-2.0updated 10mo agoView on Hugging Face
5likes26downloads
modeling_deepseekocr.py1043 linesDownload Raw Back to root
1# MIT License modified from prithivMLmods/DeepSeek-OCR-Latest-BF16.I642import os3import math4import re5from tqdm import tqdm6from abc import ABC7from typing import List, Optional, Tuple, Union8 9from addict import Dict10from PIL import Image, ImageOps, ImageDraw, ImageFont11import numpy as np12 13import torch14import torch.nn as nn15from torch.nn import CrossEntropyLoss16from torchvision import transforms17 18from transformers.cache_utils import Cache19from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast20from transformers import DeepseekV2Model, DeepseekV2ForCausalLM21from transformers import DeepseekV2Config22from transformers.models.deepseek_v2.modeling_deepseek_v2 import (23    DeepseekV2Attention, DeepseekV2MLP, DeepseekV2MoE, DeepseekV2RMSNorm, DeepseekV2DecoderLayer)24from transformers.models.llama.modeling_llama import LlamaAttention, LlamaRotaryEmbedding25from transformers import TextStreamer26from .deepencoder import build_sam_vit_b, build_clip_l, MlpProjector27from .conversation import get_conv_template28 29torch_dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float1630 31def load_image(image_path):32 33    try:34        image = Image.open(image_path)35        36        corrected_image = ImageOps.exif_transpose(image)37        38        return corrected_image39        40    except Exception as e:41        print(f"error: {e}")42        try:43            return Image.open(image_path)44        except:45            return None46 47 48def re_match(text):49    pattern = r'(<\|ref\|>(.*?)<\|/ref\|><\|det\|>(.*?)<\|/det\|>)'50    matches = re.findall(pattern, text, re.DOTALL)51 52    # pattern1 = r'<\|ref\|>.*?<\|/ref\|>\n'53    # new_text1 = re.sub(pattern1, '', text, flags=re.DOTALL)54 55    mathes_image = []56    mathes_other = []57    for a_match in matches:58        if '<|ref|>image<|/ref|>' in a_match[0]:59            mathes_image.append(a_match[0])60        else:61            mathes_other.append(a_match[0])62    return matches, mathes_image, mathes_other63 64 65def extract_coordinates_and_label(ref_text, image_width, image_height):66 67    try:68        label_type = ref_text[1]69        cor_list = eval(ref_text[2])70    except Exception as e:71        print(e)72        return None73 74    return (label_type, cor_list)75 76 77def draw_bounding_boxes(image, refs, ouput_path):78 79    image_width, image_height = image.size80    81    img_draw = image.copy()82    draw = ImageDraw.Draw(img_draw)83 84    overlay = Image.new('RGBA', img_draw.size, (0, 0, 0, 0))85    draw2 = ImageDraw.Draw(overlay)86    87    # try:88    # except IOError:89    #     try:90    #         font = ImageFont.truetype("DejaVuSans.ttf", 20) 91    #     except IOError:92    font = ImageFont.load_default()93 94    img_idx = 095    96    for i, ref in enumerate(refs):97        try:98            result = extract_coordinates_and_label(ref, image_width, image_height)99            if result:100                label_type, points_list = result101                102                color = (np.random.randint(0, 200), np.random.randint(0, 200), np.random.randint(0, 255))103 104                color_a = color + (20, )105                for points in points_list:106                    x1, y1, x2, y2 = points107 108                    x1 = int(x1 / 999 * image_width)109                    y1 = int(y1 / 999 * image_height)110 111                    x2 = int(x2 / 999 * image_width)112                    y2 = int(y2 / 999 * image_height)113 114                    if label_type == 'image':115                        try:116                            cropped = image.crop((x1, y1, x2, y2))117                            cropped.save(f"{ouput_path}/images/{img_idx}.jpg")118                        except Exception as e:119                            print(e)120                            pass121                        img_idx += 1122                        123                    try:124                        if label_type == 'title':125                            draw.rectangle([x1, y1, x2, y2], outline=color, width=4)126                            draw2.rectangle([x1, y1, x2, y2], fill=color_a, outline=(0, 0, 0, 0), width=1)127                        else:128                            draw.rectangle([x1, y1, x2, y2], outline=color, width=2)129                            draw2.rectangle([x1, y1, x2, y2], fill=color_a, outline=(0, 0, 0, 0), width=1)130                        text_x = x1131                        text_y = max(0, y1 - 15)132                            133                        134                        text_bbox = draw.textbbox((0, 0), label_type, font=font)135                        text_width = text_bbox[2] - text_bbox[0]136                        text_height = text_bbox[3] - text_bbox[1]137                        draw.rectangle([text_x, text_y, text_x + text_width, text_y + text_height], 138                                    fill=(255, 255, 255, 30))139                        140                        draw.text((text_x, text_y), label_type, font=font, fill=color)141                    except:142                        pass143        except:144            continue145    img_draw.paste(overlay, (0, 0), overlay)146    return img_draw147 148 149def process_image_with_refs(image, ref_texts, output_path):150 151    result_image = draw_bounding_boxes(image, ref_texts, output_path)152    153    return result_image154 155 156 157 158 159def find_closest_aspect_ratio(aspect_ratio, target_ratios, width, height, image_size):160    best_ratio_diff = float('inf')161    best_ratio = (1, 1)162    area = width * height163    for ratio in target_ratios:164        target_aspect_ratio = ratio[0] / ratio[1]165        ratio_diff = abs(aspect_ratio - target_aspect_ratio)166        if ratio_diff < best_ratio_diff:167            best_ratio_diff = ratio_diff168            best_ratio = ratio169        elif ratio_diff == best_ratio_diff:170            if area > 0.5 * image_size * image_size * ratio[0] * ratio[1]:171                best_ratio = ratio172    # print(f'width: {width}, height: {height}, best_ratio: {best_ratio}')173    return best_ratio174 175 176def dynamic_preprocess(image, min_num=2, max_num=9, image_size=640, use_thumbnail=False):177    orig_width, orig_height = image.size178    aspect_ratio = orig_width / orig_height179 180    # calculate the existing image aspect ratio181    target_ratios = set(182        (i, j) for n in range(min_num, max_num + 1) for i in range(1, n + 1) for j in range(1, n + 1) if183        i * j <= max_num and i * j >= min_num)184    # print(target_ratios)185    target_ratios = sorted(target_ratios, key=lambda x: x[0] * x[1])186 187    # find the closest aspect ratio to the target188    target_aspect_ratio = find_closest_aspect_ratio(189        aspect_ratio, target_ratios, orig_width, orig_height, image_size)190 191    # print(target_aspect_ratio)192    # calculate the target width and height193    target_width = image_size * target_aspect_ratio[0]194    target_height = image_size * target_aspect_ratio[1]195    blocks = target_aspect_ratio[0] * target_aspect_ratio[1]196 197    # resize the image198    resized_img = image.resize((target_width, target_height))199    processed_images = []200    for i in range(blocks):201        box = (202            (i % (target_width // image_size)) * image_size,203            (i // (target_width // image_size)) * image_size,204            ((i % (target_width // image_size)) + 1) * image_size,205            ((i // (target_width // image_size)) + 1) * image_size206        )207        # split the image208        split_img = resized_img.crop(box)209        processed_images.append(split_img)210    assert len(processed_images) == blocks211    if use_thumbnail and len(processed_images) != 1:212        thumbnail_img = image.resize((image_size, image_size))213        processed_images.append(thumbnail_img)214    return processed_images, target_aspect_ratio215 216 217 218def normalize_transform(mean, std):219    if mean is None and std is None:220        transform = None221    elif mean is None and std is not None:222        mean = [0.] * len(std)223        transform = transforms.Normalize(mean=mean, std=std)224    elif mean is not None and std is None:225        std = [1.] * len(mean)226        transform = transforms.Normalize(mean=mean, std=std)227    else:228        transform = transforms.Normalize(mean=mean, std=std)229 230    return transform231 232 233 234def format_messages(235        conversations: List[Dict[str, str]],236        sft_format: str = "deepseek",237        system_prompt: str = "",238):239    """240    Applies the SFT template to conversation.241 242    Args:243        conversations (List[Dict]): A List of messages.244        sft_format (str, optional): The format of the SFT template to use. Defaults to "deepseek".245        system_prompt (str, optional): The system prompt to use in the SFT template. Defaults to "".246 247    Returns:248        sft_prompt (str): The formatted text.249    """250 251    conv = get_conv_template(sft_format)252    conv.set_system_message(system_prompt)253    for message in conversations:254        conv.append_message(message["role"], message["content"].strip())255    sft_prompt = conv.get_prompt().strip()256 257    return sft_prompt258 259 260def text_encode(tokenizer, text: str, bos: bool = True, eos: bool = False):261    t = tokenizer.encode(text, add_special_tokens=False)262    bos_id = 0263    eos_id = 1264    if bos:265        t = [bos_id] + t266    if eos:267        t = t + [eos_id]268 269    return t270 271def load_pil_images(conversations: List[Dict[str, str]]) -> List[Image.Image]:272    """273 274    Args:275        conversations (List[Dict[str, str]]): the conversations with a list of messages. An example is :276            [277                {278                    "role": "User",279                    "content": "<image_placeholder>\nExtract all information from this image and convert them into markdown format.",280                    "images": ["./examples/table_datasets.png"]281                },282                {"role": "Assistant", "content": ""},283            ]284 285    Returns:286        pil_images (List[PIL.Image.Image]): the list of PIL images.287 288    """289 290    pil_images = []291 292    for message in conversations:293        if "images" not in message:294            continue295 296        for image_path in message["images"]:297            # print('----------------')298            # print(image_path)299            # print('----------------')300            # exit()301            302            # pil_img = Image.open(image_path)303            pil_img = load_image(image_path)304            pil_img = pil_img.convert("RGB")305            pil_images.append(pil_img)306 307    return pil_images308 309 310class BaseTransform(ABC):311 312    def set_rng(self, *args, **kwargs):313        pass314 315    def __call__(self, *args, **kwargs) -> torch.Tensor:316        pass317 318    @property319    def default_shape(self):320        raise NotImplementedError321 322 323class BasicImageTransform(BaseTransform):324    def __init__(325        self, 326        mean: Optional[Tuple[float, float, float]] = (0.5, 0.5, 0.5),327        std: Optional[Tuple[float, float, float]] = (0.5, 0.5, 0.5),328        normalize: bool = True329    ):330        self.mean = mean331        self.std = std332    333        transform_pipelines = [334            transforms.ToTensor()335        ]336 337        normalize = normalize_transform(mean, std) if normalize else nn.Identity()338        if normalize is not None:339            transform_pipelines.append(normalize)340 341        self.transform = transforms.Compose(transform_pipelines)342    343    def __call__(self, x):344        x = self.transform(x)345        return x346 347class NoEOSTextStreamer(TextStreamer):348    def on_finalized_text(self, text: str, stream_end: bool = False):349 350        eos_text = self.tokenizer.decode([self.tokenizer.eos_token_id], skip_special_tokens=False)351        text = text.replace(eos_text, "\n")352        print(text, flush=True, end="")353 354 355def decoder_layer_init(self, config: DeepseekV2Config, layer_idx: int):356    nn.Module.__init__(self)357    self.hidden_size = config.hidden_size358 359    if config.use_mla:360        self.self_attn = DeepseekV2Attention(config=config, layer_idx=layer_idx)361    else:362        config.head_dim = config.hidden_size // config.num_attention_heads363        self.self_attn = LlamaAttention(config, layer_idx)364    self.mlp = DeepseekV2MoE(config) if layer_idx >= config.first_k_dense_replace else DeepseekV2MLP(config)365 366    self.input_layernorm = DeepseekV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)367    self.post_attention_layernorm = DeepseekV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)368 369 370DeepseekV2DecoderLayer.__init__ = decoder_layer_init371 372class DeepseekOCRConfig(DeepseekV2Config):373    model_type = "DeepseekOCR"374 375class DeepseekOCRModel(DeepseekV2Model):376    config_class = DeepseekOCRConfig377 378    def __init__(self, config: DeepseekV2Config):379        super(DeepseekOCRModel, self).__init__(config)380 381        self.sam_model = build_sam_vit_b()382        self.vision_model = build_clip_l()383        # self.conv_2 = nn.Conv2d(in_channels=1024, out_channels=2048, kernel_size=2, stride=2)384        n_embed = 1280385        self.projector =  MlpProjector(Dict(projector_type="linear", input_dim=2048, n_embed=n_embed))386        embed_std = 1 / torch.sqrt(torch.tensor(n_embed, dtype=torch.float32))387        self.image_newline = nn.Parameter(torch.randn(n_embed) * embed_std)388        self.view_seperator = nn.Parameter(torch.randn(n_embed) * embed_std)389 390        self.rotary_emb = LlamaRotaryEmbedding(config=config)391    392    def forward(393        self,394        input_ids: torch.LongTensor = None,395        attention_mask: Optional[torch.Tensor] = None,396        position_ids: Optional[torch.LongTensor] = None,397        past_key_values: Optional[List[torch.FloatTensor]] = None,398        inputs_embeds: Optional[torch.FloatTensor] = None,399        use_cache: Optional[bool] = None,400        output_attentions: Optional[bool] = None,401        output_hidden_states: Optional[bool] = None,402        images: Optional[torch.FloatTensor] = None,403        images_seq_mask: Optional[torch.FloatTensor] = None,404        images_spatial_crop: Optional[torch.FloatTensor] = None,405        return_dict: Optional[bool] = None,406    ) -> Union[Tuple, BaseModelOutputWithPast]:407 408 409 410        if inputs_embeds is None:411            # inputs_embeds = self.embed_tokens(input_ids)412            inputs_embeds = self.get_input_embeddings()(input_ids)413 414        inputs_embeds = inputs_embeds.clone()415 416        sam_model = getattr(self, 'sam_model', None)417        # sam_model = self.sam_model418        vision_model = getattr(self, 'vision_model', None)419 420 421 422        if sam_model is not None and (input_ids.shape[1] != 1 or self.training) and torch.sum(images[0][1]).item() != 0:423 424            idx = 0425            426            # sam_model = torch.jit.script(sam_model)427            428            # start_time = time.time()429            for image, crop_shape in zip(images, images_spatial_crop):430                images_in_this_batch = []431 432                patches = image[0]433                image_ori = image[1]434 435                with torch.no_grad():436                # with torch.inference_mode(): 437                    438                    if torch.sum(patches).item() != 0:439                        # P, C, H, W = patches.shape440                        crop_flag = 1441                        local_features_1 = sam_model(patches)442 443                        local_features_2 = vision_model(patches, local_features_1)  444                        # vit_time = time.time()445                        local_features = torch.cat((local_features_2[:, 1:], local_features_1.flatten(2).permute(0, 2, 1)), dim=-1) 446                        local_features = self.projector(local_features)447 448 449                        global_features_1 = sam_model(image_ori)450                        global_features_2 = vision_model(image_ori, global_features_1) 451                        global_features = torch.cat((global_features_2[:, 1:], global_features_1.flatten(2).permute(0, 2, 1)), dim=-1) 452                        global_features = self.projector(global_features)453 454                        print('=====================')455                        print('BASE: ', global_features.shape)456                        print('PATCHES: ', local_features.shape)457                        print('=====================')458 459                        _, hw, n_dim = global_features.shape460                        h = w = int(hw ** 0.5)461 462                        _2, hw2, n_dim2 = local_features.shape463                        h2 = w2 = int(hw2 ** 0.5)464 465                        width_crop_num, height_crop_num = crop_shape[0], crop_shape[1]466 467                        global_features = global_features.view(h, w, n_dim)468 469                        global_features = torch.cat(470                            [global_features, self.image_newline[None, None, :].expand(h, 1, n_dim)], dim=1471                        )472 473                        global_features = global_features.view(-1, n_dim)474 475 476                        local_features = local_features.view(height_crop_num, width_crop_num, h2, w2, n_dim2).permute(0, 2, 1, 3, 4).reshape(height_crop_num*h2, width_crop_num*w2, n_dim2)477                        local_features = torch.cat(478                            [local_features, self.image_newline[None, None, :].expand(height_crop_num * h2, 1, n_dim2)], dim=1479                        )480                        local_features = local_features.view(-1, n_dim2)481 482                        global_local_features = torch.cat([local_features, global_features, self.view_seperator[None, :]], dim=0)483 484                        # end_time = time.time()485 486                        # print('sam: ', sam_time - start_time)487                        # print('vit: ', vit_time - sam_time)488                        # print('all: ', end_time - start_time)489 490                        # exit()491                   492                    else:493                        global_features_1 = sam_model(image_ori)494                        global_features_2 = vision_model(image_ori, global_features_1) 495                        global_features = torch.cat((global_features_2[:, 1:], global_features_1.flatten(2).permute(0, 2, 1)), dim=-1) 496                        global_features = self.projector(global_features)497                        _, hw, n_dim = global_features.shape498                        h = w = int(hw ** 0.5)499 500 501                        global_features = global_features.view(h, w, n_dim)502 503                        global_features = torch.cat(504                            [global_features, self.image_newline[None, None, :].expand(h, 1, n_dim)], dim=1505                        )506 507                        global_features = global_features.view(-1, n_dim)508 509                        global_local_features = torch.cat([global_features, self.view_seperator[None, :]], dim=0)510 511                    images_in_this_batch.append(global_local_features)512                513 514                if images_in_this_batch:515                    images_in_this_batch = torch.cat(images_in_this_batch, dim=0)516                    images_in_this_batch = images_in_this_batch.to(517                        device=inputs_embeds.device, dtype=inputs_embeds.dtype518                    )519                    mask = images_seq_mask[idx].unsqueeze(-1).to(inputs_embeds.device)   # bool [T, 1]520                    updated_row = inputs_embeds[idx].masked_scatter(mask, images_in_this_batch)521                    inputs_embeds[idx] = updated_row522 523                idx += 1524            525        return super(DeepseekOCRModel, self).forward(526            input_ids=None, attention_mask=attention_mask, past_key_values=past_key_values,527            inputs_embeds=inputs_embeds, use_cache=use_cache, position_ids = position_ids,528            output_attentions=output_attentions, output_hidden_states=output_hidden_states,529            return_dict=return_dict530        )531    532 533class DeepseekOCRForCausalLM(DeepseekV2ForCausalLM):534 535    config_class = DeepseekOCRConfig536    # supports_gradient_checkpointing = True537 538    def __init__(self, config):539        super(DeepseekV2ForCausalLM, self).__init__(config)540        self.model = DeepseekOCRModel(config)541 542        self.vocab_size = config.vocab_size543 544        self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)545 546        # Initialize weights and apply final processing547        self.post_init()548 549    def get_model(self):550        return self.model551 552 553    def forward(554        self,555        input_ids: torch.LongTensor = None,556        attention_mask: Optional[torch.Tensor] = None,557        position_ids: Optional[torch.LongTensor] = None,558        past_key_values: Optional[List[torch.FloatTensor]] = None,559        inputs_embeds: Optional[torch.FloatTensor] = None,560        labels: Optional[torch.LongTensor] = None,561        use_cache: Optional[bool] = None,562        output_attentions: Optional[bool] = None,563        output_hidden_states: Optional[bool] = None,564        images: Optional[torch.FloatTensor] = None,565        images_seq_mask: Optional[torch.FloatTensor] = None,566        images_spatial_crop: Optional[torch.FloatTensor] = None,567        return_dict: Optional[bool] = None,568        569    ) -> Union[Tuple, CausalLMOutputWithPast]:570        output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions571        output_hidden_states = (572            output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states573        )574        return_dict = return_dict if return_dict is not None else self.config.use_return_dict575 576 577 578        outputs  = self.model(579            input_ids=input_ids,580            past_key_values=past_key_values,581            attention_mask=attention_mask,582            position_ids=position_ids,583            inputs_embeds=inputs_embeds,584            use_cache=use_cache,585            output_attentions=output_attentions,586            output_hidden_states=output_hidden_states,587            images=images,588            images_seq_mask = images_seq_mask,589            images_spatial_crop = images_spatial_crop,590            return_dict=return_dict591            592        )593 594        hidden_states = outputs[0]595        logits = self.lm_head(hidden_states)596        logits = logits.float()597 598        # logits599 600        loss = None601        if labels is not None:602            # Shift so that tokens < n predict n603            shift_logits = logits[..., :-1, :].contiguous()604            shift_labels = labels[..., 1:].contiguous()605            # Flatten the tokens606            loss_fct = CrossEntropyLoss()607            shift_logits = shift_logits.view(-1, self.config.vocab_size)608            shift_labels = shift_labels.view(-1)609            # Enable model parallelism610            shift_labels = shift_labels.to(shift_logits.device)611            loss = loss_fct(shift_logits, shift_labels)612 613        if not return_dict:614            output = (logits,) + outputs[1:]615            return (loss,) + output if loss is not None else output616 617        return CausalLMOutputWithPast(618            loss=loss,619            logits=logits,620            past_key_values=outputs.past_key_values,621            hidden_states=outputs.hidden_states,622            attentions=outputs.attentions,623        )624 625 626    def prepare_inputs_for_generation(627        self, input_ids, past_key_values=None, attention_mask=None, inputs_embeds=None, **kwargs628    ):629        # Omit tokens covered by past_key_values630        past_length = 0631        if past_key_values is not None:632            if isinstance(past_key_values, Cache):633                cache_length = past_key_values.get_seq_length()634                past_length = past_key_values.get_seq_length()635                max_cache_length = None636            else:637                cache_length = past_length = past_key_values[0][0].shape[2]638                max_cache_length = None639 640            # Keep only the unprocessed tokens:641            # 1 - If the length of the attention_mask exceeds the length of input_ids, then we are in a setting where642            # some of the inputs are exclusively passed as part of the cache (e.g. when passing input_embeds as643            # input)644            if attention_mask is not None and attention_mask.shape[1] > input_ids.shape[1]:645                input_ids = input_ids[:, -(attention_mask.shape[1] - past_length) :]646            # 2 - If the past_length is smaller than input_ids', then input_ids holds all input tokens. We can discard647            # input_ids based on the past_length.648            elif past_length < input_ids.shape[1]:649                input_ids = input_ids[:, past_length:]650            # 3 - Otherwise (past_length >= input_ids.shape[1]), let's assume input_ids only has unprocessed tokens.651 652            # If we are about to go beyond the maximum cache length, we need to crop the input attention mask.653            if (654                max_cache_length is not None655                and attention_mask is not None656                and cache_length + input_ids.shape[1] > max_cache_length657            ):658                attention_mask = attention_mask[:, -max_cache_length:]659 660        position_ids = kwargs.get("position_ids", None)661        if attention_mask is not None and position_ids is None:662            # create position_ids on the fly for batch generation663            position_ids = attention_mask.long().cumsum(-1) - 1664            position_ids.masked_fill_(attention_mask == 0, 1)665            if past_key_values:666                position_ids = position_ids[:, -input_ids.shape[1] :]667 668        # if self.generation_config.cache_implementation == "static":669        #     # generation with static cache670        #     cache_position = kwargs.get("cache_position", None)671        #     if cache_position is None:672        #         past_length = 0673        #     else:674        #         past_length = cache_position[-1] + 1675        #     input_ids = input_ids[:, past_length:]676        #     position_ids = position_ids[:, past_length:]677 678        # TODO @gante we should only keep a `cache_position` in generate, and do +=1.679        # same goes for position ids. Could also help with continued generation.680        cache_position = torch.arange(past_length, past_length + position_ids.shape[-1], device=position_ids.device)681 682        # if `inputs_embeds` are passed, we only want to use them in the 1st generation step683        if inputs_embeds is not None and past_key_values is None:684            model_inputs = {"inputs_embeds": inputs_embeds}685        else:686            model_inputs = {"input_ids": input_ids}687 688        model_inputs.update(689            {690                "position_ids": position_ids,691                "past_key_values": past_key_values,692                "use_cache": kwargs.get("use_cache"),693                "attention_mask": attention_mask,694                "images": kwargs.get("images", None),695                "images_seq_mask": kwargs.get("images_seq_mask", None),696                "images_spatial_crop": kwargs.get("images_spatial_crop", None),697            }698        )699        return model_inputs700    701 702    def disable_torch_init(self):703        """704        Disable the redundant torch default initialization to accelerate model creation.705        """706        import torch707        setattr(torch.nn.Linear, "reset_parameters", lambda self: None)708        setattr(torch.nn.LayerNorm, "reset_parameters", lambda self: None)709 710 711 712    def infer(self, tokenizer, prompt='', image_file='', output_path = '', base_size=1024, image_size=640, crop_mode=True, test_compress=False, save_results=False, eval_mode=False):713        self.disable_torch_init()714 715        os.makedirs(output_path, exist_ok=True)716        os.makedirs(f'{output_path}/images', exist_ok=True)717 718        if prompt and image_file:719            conversation = [720                {721                    "role": "<|User|>",722                    # "content": "<image>\n<|grounding|>Given the layout of the image. ",723                    "content": f'{prompt}',724                    # "content": "君不见黄河之水天上来的下一句是什么?",725                    # "content": "<image>\nFree OCR. ",726                    # "content": "<image>\nParse the figure. ",727                    # "content": "<image>\nExtract the text in the image. ",728                    "images": [f'{image_file}'],729                },730                {"role": "<|Assistant|>", "content": ""},731            ]732        733        elif prompt:734            conversation = [735                {736                    "role": "<|User|>",737                    # "content": "<image>\n<|grounding|>Given the layout of the image. ",738                    "content": f'{prompt}',739                    # "content": "君不见黄河之水天上来的下一句是什么?",740                    # "content": "<image>\nFree OCR. ",741                    # "content": "<image>\nParse the figure. ",742                    # "content": "<image>\nExtract the text in the image. ",743                    # "images": [f'{image_file}'],744                },745                {"role": "<|Assistant|>", "content": ""},746            ]747        else:748            assert False, f'prompt is none!'749        750        prompt = format_messages(conversations=conversation, sft_format='plain', system_prompt='')751 752        patch_size = 16753        downsample_ratio = 4754        images = load_pil_images(conversation)755 756        valid_img_tokens = 0757        ratio = 1758 759        image_draw = images[0].copy()760 761        w,h = image_draw.size762        # print(w, h)763        ratio = 1 - ((max(w, h) - min(w, h)) / (max(w, h)))764    765 766        image_transform=BasicImageTransform(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5), normalize=True)767        images_seq_mask = []768 769        image_token = '<image>'770        image_token_id = 128815771        text_splits = prompt.split(image_token)772 773        images_list, images_crop_list, images_seq_mask = [], [], []774        tokenized_str = []775        images_spatial_crop = []776        for text_sep, image in zip(text_splits, images):777 778            tokenized_sep = text_encode(tokenizer, text_sep, bos=False, eos=False)779            tokenized_str += tokenized_sep780            images_seq_mask += [False] * len(tokenized_sep)781 782            if crop_mode:783 784                if image.size[0] <= 640 and image.size[1] <= 640:785                    crop_ratio = [1, 1]786 787                else:788                    if crop_mode:789                        # best_width, best_height = select_best_resolution(image.size, self.candidate_resolutions)790                        images_crop_raw, crop_ratio = dynamic_preprocess(image)791                    else:792                        # best_width, best_height = self.image_size, self.image_size793                        crop_ratio = [1, 1]794                795                """process the global view"""796                # image = image.resize((base_size, base_size))797                global_view = ImageOps.pad(image, (base_size, base_size),798                                        color=tuple(int(x * 255) for x in image_transform.mean))799                800                if base_size == 1024:801                    valid_img_tokens += int(256 * ratio)802                elif base_size == 1280:803                    valid_img_tokens += int(400 * ratio)804                # elif base_size == 640:805                #     valid_img_tokens += int(100 * ratio)806                807 808 809 810                811                images_list.append(image_transform(global_view).to(torch_dtype))812 813                # global_view_tensor = image_transform(global_view).to(torch_dtype)814 815                width_crop_num, height_crop_num = crop_ratio816 817                images_spatial_crop.append([width_crop_num, height_crop_num])818                819                820                if width_crop_num > 1 or height_crop_num > 1:821                    """process the local views"""822                    823                    for i in range(len(images_crop_raw)):824                        images_crop_list.append(image_transform(images_crop_raw[i]).to(torch_dtype))825                826                if image_size == 640:827                    valid_img_tokens += len(images_crop_list) * 100828 829                num_queries = math.ceil((image_size // patch_size) / downsample_ratio)830                num_queries_base = math.ceil((base_size // patch_size) / downsample_ratio)831 832 833 834                """add image tokens"""835 836                837 838                tokenized_image = ([image_token_id] * num_queries_base + [image_token_id]) * num_queries_base839                tokenized_image += [image_token_id]840                if width_crop_num > 1 or height_crop_num > 1:841                    tokenized_image += ([image_token_id] * (num_queries * width_crop_num) + [image_token_id]) * (842                                num_queries * height_crop_num)843                tokenized_str += tokenized_image844                images_seq_mask += [True] * len(tokenized_image)845                # num_image_tokens.append(len(tokenized_image))846 847            else:848                # best_width, best_height = self.image_size, self.image_size849                # print(image.size, (best_width, best_height)) # check the select_best_resolutions func850 851                """process the global view"""852                if image_size <= 640:853                    print('directly resize')854                    image = image.resize((image_size, image_size))855                # else:856                global_view = ImageOps.pad(image, (image_size, image_size),857                                        color=tuple(int(x * 255) for x in image_transform.mean))858                images_list.append(image_transform(global_view).to(torch_dtype))859 860                if base_size == 1024:861                    valid_img_tokens += int(256 * ratio)862                elif base_size == 1280:863                    valid_img_tokens += int(400 * ratio)864                elif base_size == 640:865                    valid_img_tokens += int(100 * 1)866                elif base_size == 512:867                    valid_img_tokens += int(64 * 1)868 869                width_crop_num, height_crop_num = 1, 1870 871                images_spatial_crop.append([width_crop_num, height_crop_num])872 873 874                """add image tokens"""875                num_queries = math.ceil((image_size // patch_size) / downsample_ratio)876 877                tokenized_image = ([image_token_id] * num_queries + [image_token_id]) * num_queries878                tokenized_image += [image_token_id]879                # tokenized_image += ([self.image_token_id] * (num_queries * width_crop_num) + [self.image_token_id]) * (880                #             num_queries * height_crop_num)881                tokenized_str += tokenized_image882                images_seq_mask += [True] * len(tokenized_image)883                # num_image_tokens.append(len(tokenized_image))884        885 886        """process the last text split"""887        tokenized_sep = text_encode(tokenizer, text_splits[-1], bos=False, eos=False)888        tokenized_str += tokenized_sep889        images_seq_mask += [False] * len(tokenized_sep)890 891        """add the bos tokens"""892        bos_id = 0893        tokenized_str = [bos_id] + tokenized_str 894        images_seq_mask = [False] + images_seq_mask895 896 897 898        input_ids = torch.LongTensor(tokenized_str)899 900        images_seq_mask = torch.tensor(images_seq_mask, dtype=torch.bool)901 902 903        if len(images_list) == 0:904            images_ori = torch.zeros((1, 3, image_size, image_size))905            images_spatial_crop = torch.zeros((1, 2), dtype=torch.long)906            images_crop = torch.zeros((1, 3, base_size, base_size))907 908        else:909            images_ori = torch.stack(images_list, dim=0)910            images_spatial_crop = torch.tensor(images_spatial_crop, dtype=torch.long)911            if images_crop_list:912                images_crop = torch.stack(images_crop_list, dim=0)913            else:914                images_crop = torch.zeros((1, 3, base_size, base_size))915 916 917 918        if not eval_mode:919            streamer = NoEOSTextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=False)920            with torch.autocast("cuda", dtype=torch_dtype):921                with torch.no_grad():922                    output_ids = self.generate(923                        input_ids.unsqueeze(0).cuda(),924                        images=[(images_crop.cuda(), images_ori.cuda())],925                        images_seq_mask = images_seq_mask.unsqueeze(0).cuda(),926                        images_spatial_crop = images_spatial_crop,927                        # do_sample=False,928                        # num_beams = 1,929                        temperature=0.0,930                        eos_token_id=tokenizer.eos_token_id,931                        streamer=streamer,932                        max_new_tokens=8192,933                        no_repeat_ngram_size = 20,934                        use_cache = True935                        )936 937        else:938            with torch.autocast("cuda", dtype=torch_dtype):939                with torch.no_grad():940                    output_ids = self.generate(941                        input_ids.unsqueeze(0).cuda(),942                        images=[(images_crop.cuda(), images_ori.cuda())],943                        images_seq_mask = images_seq_mask.unsqueeze(0).cuda(),944                        images_spatial_crop = images_spatial_crop,945                        # do_sample=False,946                        # num_beams = 1,947                        temperature=0.0,948                        eos_token_id=tokenizer.eos_token_id,949                        max_new_tokens=8192,950                        no_repeat_ngram_size = 35,951                        use_cache = True952                        )953                954 955        if '<image>' in conversation[0]['content'] and eval_mode:956                outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).cuda().shape[1]:])957                stop_str = '<|end▁of▁sentence|>'958                if outputs.endswith(stop_str):959                    outputs = outputs[:-len(stop_str)]960                # re_match961                outputs = outputs.strip()962 963                return outputs964        965        if '<image>' in conversation[0]['content'] and test_compress:966            outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).cuda().shape[1]:])967            pure_texts_outputs_token_length = len(text_encode(tokenizer, outputs, bos=False, eos=False))968            print('='*50)969            print('image size: ', (w, h))970            print('valid image tokens: ', int(valid_img_tokens))971            print('output texts tokens (valid): ', pure_texts_outputs_token_length)972            print('compression ratio: ', round(pure_texts_outputs_token_length/valid_img_tokens, 2))973            print('='*50)974 975 976        if '<image>' in conversation[0]['content'] and save_results:977            outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).cuda().shape[1]:])978            stop_str = '<|end▁of▁sentence|>'979 980            print('='*15 + 'save results:' + '='*15)981            982            # # # # conv.messages[-1][-1] = outputs983            if outputs.endswith(stop_str):984                outputs = outputs[:-len(stop_str)]985            outputs = outputs.strip()986 987            matches_ref, matches_images, mathes_other = re_match(outputs)988            # print(matches_ref)989            result = process_image_with_refs(image_draw, matches_ref, output_path)990 991 992            for idx, a_match_image in enumerate(tqdm(matches_images, desc="image")):993                outputs = outputs.replace(a_match_image, '![](images/' + str(idx) + '.jpg)\n')994            995            for idx, a_match_other in enumerate(tqdm(mathes_other, desc="other")):996                outputs = outputs.replace(a_match_other, '').replace('\\coloneqq', ':=').replace('\\eqqcolon', '=:')997 998 999            # if 'structural formula' in conversation[0]['content']:1000            #     outputs = '<smiles>' + outputs + '</smiles>'1001            with open(f'{output_path}/result.mmd', 'w', encoding = 'utf-8') as afile:1002                afile.write(outputs)1003 1004            if 'line_type' in outputs:1005                import matplotlib.pyplot as plt1006                lines = eval(outputs)['Line']['line']1007 1008                line_type = eval(outputs)['Line']['line_type']1009                # print(lines)1010 1011                endpoints = eval(outputs)['Line']['line_endpoint']1012 1013                fig, ax = plt.subplots(figsize=(3,3), dpi=200)1014                ax.set_xlim(-15, 15)1015                ax.set_ylim(-15, 15)1016 1017                for idx, line in enumerate(lines):1018                    try:1019                        p0 = eval(line.split(' -- ')[0])1020                        p1 = eval(line.split(' -- ')[-1])1021 1022                        if line_type[idx] == '--':1023                            ax.plot([p0[0], p1[0]], [p0[1], p1[1]], linewidth=0.8, color='k')1024                        else:1025                            ax.plot([p0[0], p1[0]], [p0[1], p1[1]], linewidth = 0.8, color = 'k')1026 1027                        ax.scatter(p0[0], p0[1], s=5, color = 'k')1028                        ax.scatter(p1[0], p1[1], s=5, color = 'k')1029                    except:1030                        pass1031 1032                for endpoint in endpoints:1033 1034                    label = endpoint.split(': ')[0]1035                    (x, y) = eval(endpoint.split(': ')[1])1036                    ax.annotate(label, (x, y), xytext=(1, 1), textcoords='offset points', 1037                                fontsize=5, fontweight='light')1038                1039 1040                plt.savefig(f'{output_path}/geo.jpg')1041                plt.close()1042 1043            result.save(f"{output_path}/result_with_boxes.jpg")