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