use anyhow::{Context, Result};
use serde::Deserialize;
use std::path::Path;
pub const LN_EPS: f32 = 1e-5;
pub const IMAGE_MEAN: [f32; 3] = [0.485, 0.456, 0.406];
pub const IMAGE_STD: [f32; 3] = [0.229, 0.224, 0.225];
#[derive(Debug, Clone)]
pub struct Florence2VisionConfig {
pub dim_embed: Vec<usize>,
pub depths: Vec<usize>,
pub num_heads: Vec<usize>,
pub num_groups: Vec<usize>,
pub patch_size: Vec<usize>,
pub patch_stride: Vec<usize>,
pub patch_padding: Vec<usize>,
pub patch_prenorm: Vec<bool>,
pub window_size: usize,
pub projection_dim: usize,
pub mlp_ratio: f64,
pub image_pos_embed_max: usize,
pub visual_temporal_max: usize,
pub image_feature_source: Vec<String>,
}
impl Florence2VisionConfig {
pub fn num_stages(&self) -> usize {
self.dim_embed.len()
}
pub fn dim_out(&self) -> usize {
*self.dim_embed.last().unwrap()
}
}
#[derive(Debug, Clone)]
pub struct Florence2TextConfig {
pub vocab_size: usize,
pub d_model: usize,
pub encoder_layers: usize,
pub decoder_layers: usize,
pub encoder_attention_heads: usize,
pub decoder_attention_heads: usize,
pub encoder_ffn_dim: usize,
pub decoder_ffn_dim: usize,
pub max_position_embeddings: usize,
pub scale_embedding: bool,
pub bos_token_id: u32,
pub eos_token_id: u32,
pub pad_token_id: u32,
pub decoder_start_token_id: u32,
pub forced_bos_token_id: u32,
pub forced_eos_token_id: u32,
pub num_beams: usize,
pub no_repeat_ngram_size: usize,
}
impl Florence2TextConfig {
pub const POS_OFFSET: usize = 2;
pub fn enc_head_dim(&self) -> usize {
self.d_model / self.encoder_attention_heads
}
pub fn dec_head_dim(&self) -> usize {
self.d_model / self.decoder_attention_heads
}
}
#[derive(Debug, Clone)]
pub struct Florence2Config {
pub vision: Florence2VisionConfig,
pub text: Florence2TextConfig,
pub vocab_size: usize,
pub projection_dim: usize,
}
impl Florence2Config {
pub fn large() -> Self {
Self {
vision: Florence2VisionConfig {
dim_embed: vec![256, 512, 1024, 2048],
depths: vec![1, 1, 9, 1],
num_heads: vec![8, 16, 32, 64],
num_groups: vec![8, 16, 32, 64],
patch_size: vec![7, 3, 3, 3],
patch_stride: vec![4, 2, 2, 2],
patch_padding: vec![3, 1, 1, 1],
patch_prenorm: vec![false, true, true, true],
window_size: 12,
projection_dim: 1024,
mlp_ratio: 4.0,
image_pos_embed_max: 50,
visual_temporal_max: 100,
image_feature_source: vec!["spatial_avg_pool".into(), "temporal_avg_pool".into()],
},
text: Florence2TextConfig {
vocab_size: 51289,
d_model: 1024,
encoder_layers: 12,
decoder_layers: 12,
encoder_attention_heads: 16,
decoder_attention_heads: 16,
encoder_ffn_dim: 4096,
decoder_ffn_dim: 4096,
max_position_embeddings: 4096,
scale_embedding: false,
bos_token_id: 0,
eos_token_id: 2,
pad_token_id: 1,
decoder_start_token_id: 2,
forced_bos_token_id: 0,
forced_eos_token_id: 2,
num_beams: 3,
no_repeat_ngram_size: 3,
},
vocab_size: 51289,
projection_dim: 1024,
}
}
pub fn from_hf_config_json(path: &Path) -> Result<Self> {
let raw = std::fs::read_to_string(path)
.with_context(|| format!("read florence2 config {}", path.display()))?;
let hf: HfConfig = serde_json::from_str(&raw)
.with_context(|| format!("parse florence2 config {}", path.display()))?;
Ok(hf.into_config())
}
pub fn embed_scale(&self) -> f32 {
if self.text.scale_embedding {
(self.text.d_model as f32).sqrt()
} else {
1.0
}
}
}
#[derive(Debug, Deserialize)]
struct HfConfig {
vocab_size: usize,
projection_dim: usize,
text_config: HfTextConfig,
vision_config: HfVisionConfig,
}
#[derive(Debug, Deserialize)]
struct HfTextConfig {
vocab_size: usize,
d_model: usize,
encoder_layers: usize,
decoder_layers: usize,
encoder_attention_heads: usize,
decoder_attention_heads: usize,
encoder_ffn_dim: usize,
decoder_ffn_dim: usize,
max_position_embeddings: usize,
#[serde(default)]
scale_embedding: bool,
bos_token_id: u32,
eos_token_id: u32,
pad_token_id: u32,
decoder_start_token_id: u32,
forced_bos_token_id: u32,
forced_eos_token_id: u32,
#[serde(default = "default_beams")]
num_beams: usize,
#[serde(default = "default_ngram")]
no_repeat_ngram_size: usize,
}
fn default_beams() -> usize {
3
}
fn default_ngram() -> usize {
3
}
#[derive(Debug, Deserialize)]
struct HfVisionConfig {
dim_embed: Vec<usize>,
depths: Vec<usize>,
num_heads: Vec<usize>,
num_groups: Vec<usize>,
patch_size: Vec<usize>,
patch_stride: Vec<usize>,
patch_padding: Vec<usize>,
patch_prenorm: Vec<bool>,
window_size: usize,
projection_dim: usize,
image_pos_embed: HfPosEmbed,
visual_temporal_embedding: HfTemporalEmbed,
image_feature_source: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct HfPosEmbed {
max_pos_embeddings: usize,
}
#[derive(Debug, Deserialize)]
struct HfTemporalEmbed {
max_temporal_embeddings: usize,
}
impl HfConfig {
fn into_config(self) -> Florence2Config {
let v = self.vision_config;
let t = self.text_config;
Florence2Config {
vision: Florence2VisionConfig {
dim_embed: v.dim_embed,
depths: v.depths,
num_heads: v.num_heads,
num_groups: v.num_groups,
patch_size: v.patch_size,
patch_stride: v.patch_stride,
patch_padding: v.patch_padding,
patch_prenorm: v.patch_prenorm,
window_size: v.window_size,
projection_dim: v.projection_dim,
mlp_ratio: 4.0,
image_pos_embed_max: v.image_pos_embed.max_pos_embeddings,
visual_temporal_max: v.visual_temporal_embedding.max_temporal_embeddings,
image_feature_source: v.image_feature_source,
},
text: Florence2TextConfig {
vocab_size: t.vocab_size,
d_model: t.d_model,
encoder_layers: t.encoder_layers,
decoder_layers: t.decoder_layers,
encoder_attention_heads: t.encoder_attention_heads,
decoder_attention_heads: t.decoder_attention_heads,
encoder_ffn_dim: t.encoder_ffn_dim,
decoder_ffn_dim: t.decoder_ffn_dim,
max_position_embeddings: t.max_position_embeddings,
scale_embedding: t.scale_embedding,
bos_token_id: t.bos_token_id,
eos_token_id: t.eos_token_id,
pad_token_id: t.pad_token_id,
decoder_start_token_id: t.decoder_start_token_id,
forced_bos_token_id: t.forced_bos_token_id,
forced_eos_token_id: t.forced_eos_token_id,
num_beams: t.num_beams,
no_repeat_ngram_size: t.no_repeat_ngram_size,
},
vocab_size: self.vocab_size,
projection_dim: self.projection_dim,
}
}
}
pub fn task_post_processing_type(task: &str) -> &'static str {
match task {
"<OCR>" => "pure_text",
"<OCR_WITH_REGION>" => "ocr",
"<CAPTION>" => "pure_text",
"<DETAILED_CAPTION>" => "pure_text",
"<MORE_DETAILED_CAPTION>" => "pure_text",
"<OD>" => "description_with_bboxes",
"<DENSE_REGION_CAPTION>" => "description_with_bboxes",
"<CAPTION_TO_PHRASE_GROUNDING>" => "phrase_grounding",
"<REFERRING_EXPRESSION_SEGMENTATION>" => "polygons",
"<REGION_TO_SEGMENTATION>" => "polygons",
"<OPEN_VOCABULARY_DETECTION>" => "description_with_bboxes_or_polygons",
"<REGION_TO_CATEGORY>" => "pure_text",
"<REGION_TO_DESCRIPTION>" => "pure_text",
"<REGION_TO_OCR>" => "pure_text",
"<REGION_PROPOSAL>" => "bboxes",
_ => "pure_text",
}
}
pub fn construct_prompt(text: &str) -> String {
for (token, prompt) in TASK_PROMPTS_WITHOUT_INPUTS {
if text.contains(token) {
return prompt.to_string();
}
}
for (token, prompt) in TASK_PROMPTS_WITH_INPUT {
if text.contains(token) {
let input = text.replace(token, "");
return prompt.replace("{input}", &input);
}
}
text.to_string()
}
const TASK_PROMPTS_WITHOUT_INPUTS: &[(&str, &str)] = &[
("<OCR>", "What is the text in the image?"),
(
"<OCR_WITH_REGION>",
"What is the text in the image, with regions?",
),
("<CAPTION>", "What does the image describe?"),
(
"<DETAILED_CAPTION>",
"Describe in detail what is shown in the image.",
),
(
"<MORE_DETAILED_CAPTION>",
"Describe with a paragraph what is shown in the image.",
),
(
"<OD>",
"Locate the objects with category name in the image.",
),
(
"<DENSE_REGION_CAPTION>",
"Locate the objects in the image, with their descriptions.",
),
(
"<REGION_PROPOSAL>",
"Locate the region proposals in the image.",
),
];
const TASK_PROMPTS_WITH_INPUT: &[(&str, &str)] = &[
(
"<CAPTION_TO_PHRASE_GROUNDING>",
"Locate the phrases in the caption: {input}",
),
(
"<REFERRING_EXPRESSION_SEGMENTATION>",
"Locate {input} in the image with mask",
),
(
"<REGION_TO_SEGMENTATION>",
"What is the polygon mask of region {input}",
),
(
"<OPEN_VOCABULARY_DETECTION>",
"Locate {input} in the image.",
),
("<REGION_TO_CATEGORY>", "What is the region {input}?"),
(
"<REGION_TO_DESCRIPTION>",
"What does the region {input} describe?",
),
("<REGION_TO_OCR>", "What text is in the region {input}?"),
];