normalize: x = (x - mean) / std TODO: implement bicubic interpolation instead of linear.
| 724 | // normalize: x = (x - mean) / std |
| 725 | // TODO: implement bicubic interpolation instead of linear. |
| 726 | bool clip_image_preprocess(const clip_ctx * ctx, const clip_image_u8 * img, clip_image_f32 * res, const bool pad2square) { |
| 727 | if (!ctx->has_vision_encoder) { |
| 728 | printf("This gguf file seems to have no vision encoder\n"); |
| 729 | return false; |
| 730 | } |
| 731 | |
| 732 | // the logic below is to pad the shorter side to the longer side with a background color: rgb(122, 116, 104) |
| 733 | // see https://github.com/haotian-liu/LLaVA/blob/e854a2bf85118c504f6f16bf5c3c7c92f8fa8c6b/llava/conversation.py#L113-L156 |
| 734 | |
| 735 | clip_image_u8 * temp = make_clip_image_u8(); // we will keep the input image data here temporarily |
| 736 | if (pad2square && img->nx != img->ny) { |
| 737 | int longer_side = std::max(img->nx, img->ny); |
| 738 | temp->nx = longer_side; |
| 739 | temp->ny = longer_side; |
| 740 | temp->size = 3 * longer_side * longer_side; |
| 741 | temp->data = new uint8_t[temp->size](); |
| 742 | uint8_t bc[3] = {122, 116, 104}; // bakground color in RGB from LLaVA |
| 743 | |
| 744 | // fill with background color |
| 745 | for (size_t i = 0; i < temp->size; i++) { |
| 746 | temp->data[i] = bc[i % 3]; |
| 747 | } |
| 748 | |
| 749 | // copy from the input image |
| 750 | for (int y = 0; y < img->ny; y++) { |
| 751 | for (int x = 0; x < img->nx; x++) { |
| 752 | const int i = 3 * (y * img->nx + x); |
| 753 | const int j = 3 * (y * temp->nx + x); |
| 754 | temp->data[j] = img->data[i]; |
| 755 | temp->data[j+1] = img->data[i+1]; |
| 756 | temp->data[j+2] = img->data[i+2]; |
| 757 | } |
| 758 | } |
| 759 | } else { |
| 760 | temp->nx = img->nx; |
| 761 | temp->ny = img->ny; |
| 762 | temp->size = img->size; |
| 763 | temp->data = new uint8_t[temp->size](); |
| 764 | memcpy(&temp->data[0], &img->data[0], temp->size); // copy |
| 765 | } |
| 766 | |
| 767 | const int nx = temp->nx; |
| 768 | const int ny = temp->ny; |
| 769 | |
| 770 | const int nx2 = ctx->vision_model.hparams.image_size; |
| 771 | const int ny2 = ctx->vision_model.hparams.image_size; |
| 772 | |
| 773 | res->nx = nx2; |
| 774 | res->ny = ny2; |
| 775 | res->size = 3 * nx2 * ny2; |
| 776 | res->data = new float[res->size](); |
| 777 | |
| 778 | const float scale = std::max(nx, ny) / (float)ctx->vision_model.hparams.image_size; |
| 779 | |
| 780 | const int nx3 = int(nx / scale + 0.5f); |
| 781 | const int ny3 = int(ny / scale + 0.5f); |
| 782 | |
| 783 | const auto & m3 = ctx->image_mean; // {0.48145466f, 0.4578275f, 0.40821073f}; |
no test coverage detected