1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "tflite/tools/bitmap_helpers.h"
17 
18 #include <fstream>
19 #include <iostream>
20 
21 #include "tflite/tools/log.h"
22 
23 namespace tflite {
24 namespace label_classify {
DecodeBmp(const uint8_t * input,int32_t rowSize,ImageInfo imageInfo,bool topDown,std::vector<uint8_t> & output)25 void DecodeBmp(const uint8_t* input, int32_t rowSize, ImageInfo imageInfo, bool topDown, std::vector<uint8_t>& output)
26 {
27     ColorChannelOffset colorChannelOffset = { BLUE_OFFSET, GREEN_OFFSET, ALPHA_OFFSET };
28     for (int32_t i = 0; i < imageInfo.height; ++i) {
29         int32_t srcPos;
30         int32_t dstPos;
31 
32         for (int32_t j = 0; j < imageInfo.width; j++) {
33             if (!topDown) {
34                 srcPos = ((imageInfo.height - 1 - i) * rowSize) + j * imageInfo.channels;
35             } else {
36                 srcPos = i * rowSize + j * imageInfo.channels;
37             }
38 
39             dstPos = (i * imageInfo.width + j) * imageInfo.channels;
40 
41             switch (imageInfo.channels) {
42                 case GRAYSCALE_DIM:
43                     output[dstPos] = input[srcPos];
44                     break;
45                 case BGR_DIM:
46                     // BGR -> RGB
47                     output[dstPos] = input[srcPos + colorChannelOffset.blueOffset];
48                     output[dstPos + colorChannelOffset.greenOffset] = input[srcPos + colorChannelOffset.greenOffset];
49                     output[dstPos + colorChannelOffset.blueOffset] = input[srcPos];
50                     break;
51                 case BGRA_DIM:
52                     // BGRA -> RGBA
53                     output[dstPos] = input[srcPos + colorChannelOffset.blueOffset];
54                     output[dstPos + colorChannelOffset.greenOffset] = input[srcPos + colorChannelOffset.greenOffset];
55                     output[dstPos + colorChannelOffset.blueOffset] = input[srcPos];
56                     output[dstPos + colorChannelOffset.alphaOffset] = input[srcPos + colorChannelOffset.alphaOffset];
57                     break;
58                 default:
59                     LOG(FATAL) << "Unexpected number of channels: " << imageInfo.channels;
60                     break;
61             }
62         }
63     }
64     return;
65 }
66 
ReadBmp(const std::string & inputBmpName,ImageInfo & imageInfo,Settings * s,std::vector<uint8_t> & inputImage)67 void ReadBmp(const std::string& inputBmpName, ImageInfo& imageInfo, Settings* s, std::vector<uint8_t>& inputImage)
68 {
69     int32_t begin, end;
70     std::ifstream file(inputBmpName, std::ios::in | std::ios::binary);
71     if (!file) {
72         LOG(FATAL) << "input file " << inputBmpName << " not found";
73         return;
74     }
75 
76     begin = file.tellg();
77     file.seekg(0, std::ios::end);
78     end = file.tellg();
79     size_t len = end - begin;
80     if (s->verbose) {
81         LOG(INFO) << "len: " << len;
82     }
83 
84     std::vector<uint8_t> img_bytes(len);
85     BmpAddressOffset bmpAddressOffset = { HEADER_ADDRESS_OFFSET, WIDTH_ADDRESS_OFFSET,
86         HEIGHT_ADDRESS_OFFSET, BBP_ADDRESS_OFFSET };
87     file.seekg(0, std::ios::beg);
88     file.read(reinterpret_cast<char*>(img_bytes.data()), len);
89     const int32_t headerSize =
90         *(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.headerAddressOffset));
91     imageInfo.width = *(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.widthAddressOffset));
92     imageInfo.height =
93         abs(*(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.heightAddressOffset)));
94     const int32_t bpp = *(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.bbpAddressOffset));
95     imageInfo.channels = bpp / BIT_TO_BYTE;
96     inputImage.resize(imageInfo.height * imageInfo.width * imageInfo.channels);
97 
98     if (s->verbose) {
99         LOG(INFO) << "width, height, channels: " << imageInfo.width << ", " << imageInfo.height << ", "
100             << imageInfo.channels;
101     }
102 
103     // there may be padding bytes when the width is not a multiple of 4 bytes.
104     // 8 * channels == bits per pixel
105     const int32_t rowSize = ((8 * imageInfo.channels * imageInfo.width + 31) >> 5) << 2;
106 
107     // if height is negative, data layout is top down. otherwise, it's bottom up.
108     bool topDown = (imageInfo.height < 0);
109 
110     // Decode image, allocating tensor once the image size is known.
111     const uint8_t* bmpPixels = &img_bytes[headerSize];
112     DecodeBmp(bmpPixels, rowSize, imageInfo, topDown, inputImage);
113     return;
114 }
115 } // namespace label_classify
116 } // namespace tflite
117