mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-08 04:26:58 +03:00
Initial Code v0.1.0
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,8 @@
|
|||||||
|
# Remove if certain files are committed on purpose
|
||||||
|
.pio/
|
||||||
|
.vscode/
|
||||||
|
.code-workspace
|
||||||
|
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
|
|||||||
5
code/.gitignore
vendored
Normal file
5
code/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
8
code/CMakeLists.txt
Normal file
8
code/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
|
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||||
|
|
||||||
|
set(PROJECT_VER "0.0.9.3")
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(esp32cam-server-only)
|
||||||
39
code/include/README
Normal file
39
code/include/README
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||||
46
code/lib/README
Normal file
46
code/lib/README
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||||
157
code/lib/connect_wlan/Helper.cpp
Normal file
157
code/lib/connect_wlan/Helper.cpp
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
//#pragma warning(disable : 4996)
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
//#define ISWINDOWS_TRUE
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
std::string FormatFileName(std::string input)
|
||||||
|
{
|
||||||
|
#ifdef ISWINDOWS_TRUE
|
||||||
|
input.erase(0, 1);
|
||||||
|
std::string os = "/";
|
||||||
|
std::string ns = "\\";
|
||||||
|
FindReplace(input, os, ns);
|
||||||
|
#endif
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void FindReplace(std::string& line, std::string& oldString, std::string& newString) {
|
||||||
|
const size_t oldSize = oldString.length();
|
||||||
|
|
||||||
|
// do nothing if line is shorter than the string to find
|
||||||
|
if (oldSize > line.length()) return;
|
||||||
|
|
||||||
|
const size_t newSize = newString.length();
|
||||||
|
for (size_t pos = 0; ; pos += newSize) {
|
||||||
|
// Locate the substring to replace
|
||||||
|
pos = line.find(oldString, pos);
|
||||||
|
if (pos == std::string::npos) return;
|
||||||
|
if (oldSize == newSize) {
|
||||||
|
// if they're same size, use std::string::replace
|
||||||
|
line.replace(pos, oldSize, newString);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if not same size, replace by erasing and inserting
|
||||||
|
line.erase(pos, oldSize);
|
||||||
|
line.insert(pos, newString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool ctype_space(const char c, string adddelimiter)
|
||||||
|
{
|
||||||
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 11)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (adddelimiter.find(c) != string::npos)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string trim(string istring, string adddelimiter)
|
||||||
|
{
|
||||||
|
bool trimmed = false;
|
||||||
|
|
||||||
|
if (ctype_space(istring[istring.length() - 1], adddelimiter))
|
||||||
|
{
|
||||||
|
istring.erase(istring.length() - 1);
|
||||||
|
trimmed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctype_space(istring[0], adddelimiter))
|
||||||
|
{
|
||||||
|
istring.erase(0, 1);
|
||||||
|
trimmed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((trimmed == false) || (istring.size() == 0))
|
||||||
|
{
|
||||||
|
return istring;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return trim(istring, adddelimiter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t findDelimiterPos(string input, string delimiter)
|
||||||
|
{
|
||||||
|
size_t pos = std::string::npos;
|
||||||
|
size_t zw;
|
||||||
|
string akt_del;
|
||||||
|
|
||||||
|
for (int anz = 0; anz < delimiter.length(); ++anz)
|
||||||
|
{
|
||||||
|
akt_del = delimiter[anz];
|
||||||
|
if ((zw = input.find(akt_del)) != std::string::npos)
|
||||||
|
{
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
if (zw < pos)
|
||||||
|
pos = zw;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pos = zw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CopyFile(string input, string output)
|
||||||
|
{
|
||||||
|
input = FormatFileName(input);
|
||||||
|
output = FormatFileName(output);
|
||||||
|
|
||||||
|
char cTemp;
|
||||||
|
FILE* fpSourceFile = fopen(input.c_str(), "rb");
|
||||||
|
FILE* fpTargetFile = fopen(output.c_str(), "wb");
|
||||||
|
|
||||||
|
// Code Section
|
||||||
|
|
||||||
|
// Read From The Source File - "Copy"
|
||||||
|
while (fread(&cTemp, 1, 1, fpSourceFile) == 1)
|
||||||
|
{
|
||||||
|
// Write To The Target File - "Paste"
|
||||||
|
fwrite(&cTemp, 1, 1, fpTargetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close The Files
|
||||||
|
fclose(fpSourceFile);
|
||||||
|
fclose(fpTargetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string getFileType(string filename)
|
||||||
|
{
|
||||||
|
int lastpos = filename.find(".", 0);
|
||||||
|
int neu_pos;
|
||||||
|
while ((neu_pos = filename.find(".", lastpos + 1)) > -1)
|
||||||
|
{
|
||||||
|
lastpos = neu_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
string zw = filename.substr(lastpos + 1, filename.size() - lastpos);
|
||||||
|
|
||||||
|
return zw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string toUpper(string in)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < in.length(); ++i)
|
||||||
|
in[i] = toupper(in[i]);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
22
code/lib/connect_wlan/Helper.h
Normal file
22
code/lib/connect_wlan/Helper.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
std::string FormatFileName(std::string input);
|
||||||
|
void FindReplace(std::string& line, std::string& oldString, std::string& newString);
|
||||||
|
|
||||||
|
void CopyFile(string input, string output);
|
||||||
|
|
||||||
|
size_t findDelimiterPos(string input, string delimiter);
|
||||||
|
//string trim(string istring);
|
||||||
|
string trim(string istring, string adddelimiter = "");
|
||||||
|
bool ctype_space(const char c, string adddelimiter);
|
||||||
|
|
||||||
|
string getFileType(string filename);
|
||||||
|
|
||||||
|
string toUpper(string in);
|
||||||
|
|
||||||
|
|
||||||
162
code/lib/connect_wlan/connect_wlan.cpp
Normal file
162
code/lib/connect_wlan/connect_wlan.cpp
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
#include "connect_wlan.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_wifi.h"
|
||||||
|
#include "esp_event_loop.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static const char *MAIN_TAG = "connect_wlan";
|
||||||
|
|
||||||
|
std::string ssid;
|
||||||
|
std::string passphrase;
|
||||||
|
|
||||||
|
static EventGroupHandle_t wifi_event_group;
|
||||||
|
|
||||||
|
|
||||||
|
#define BLINK_GPIO GPIO_NUM_33
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<string> ZerlegeZeile(std::string input)
|
||||||
|
{
|
||||||
|
std::vector<string> Output;
|
||||||
|
std::string delimiter = " =,";
|
||||||
|
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
size_t pos = findDelimiterPos(input, delimiter);
|
||||||
|
std::string token;
|
||||||
|
while (pos != std::string::npos) {
|
||||||
|
token = input.substr(0, pos);
|
||||||
|
token = trim(token, delimiter);
|
||||||
|
Output.push_back(token);
|
||||||
|
input.erase(0, pos + 1);
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
pos = findDelimiterPos(input, delimiter);
|
||||||
|
}
|
||||||
|
Output.push_back(input);
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void wifi_connect(){
|
||||||
|
wifi_config_t cfg = { };
|
||||||
|
strcpy((char*)cfg.sta.ssid, (const char*)ssid.c_str());
|
||||||
|
strcpy((char*)cfg.sta.password, (const char*)passphrase.c_str());
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK( esp_wifi_disconnect() );
|
||||||
|
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &cfg) );
|
||||||
|
ESP_ERROR_CHECK( esp_wifi_connect() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void blinkstatus(int dauer)
|
||||||
|
{
|
||||||
|
gpio_reset_pin(BLINK_GPIO);
|
||||||
|
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
gpio_set_level(BLINK_GPIO, 0);
|
||||||
|
vTaskDelay(dauer / portTICK_PERIOD_MS);
|
||||||
|
gpio_set_level(BLINK_GPIO, 1);
|
||||||
|
vTaskDelay(dauer / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t event_handler(void *ctx, system_event_t *event)
|
||||||
|
{
|
||||||
|
switch(event->event_id) {
|
||||||
|
case SYSTEM_EVENT_STA_START:
|
||||||
|
blinkstatus(200);
|
||||||
|
wifi_connect();
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_STA_GOT_IP:
|
||||||
|
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
||||||
|
blinkstatus(1000);
|
||||||
|
break;
|
||||||
|
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||||
|
blinkstatus(200);
|
||||||
|
esp_wifi_connect();
|
||||||
|
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialise_wifi(std::string _ssid, std::string _passphrase)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );
|
||||||
|
wifi_event_group = xEventGroupCreate();
|
||||||
|
ssid = _ssid;
|
||||||
|
passphrase = _passphrase;
|
||||||
|
esp_log_level_set("wifi", ESP_LOG_NONE); // disable wifi driver logging
|
||||||
|
tcpip_adapter_init();
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
|
||||||
|
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||||
|
ESP_ERROR_CHECK( esp_wifi_start() );
|
||||||
|
esp_err_t ret = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA ,"icircuit");
|
||||||
|
if(ret != ESP_OK ){
|
||||||
|
ESP_LOGE(MAIN_TAG,"failed to set hostname:%d",ret);
|
||||||
|
}
|
||||||
|
xEventGroupWaitBits(wifi_event_group,CONNECTED_BIT,true,true,portMAX_DELAY);
|
||||||
|
tcpip_adapter_ip_info_t ip_info;
|
||||||
|
ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));
|
||||||
|
printf("IP : %s\n", ip4addr_ntoa(&ip_info.ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LoadWlanFromFile(std::string fn, std::string &_ssid, std::string &_passphrase)
|
||||||
|
{
|
||||||
|
string line = "";
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
FILE* pFile;
|
||||||
|
fn = FormatFileName(fn);
|
||||||
|
pFile = fopen(fn.c_str(), "r");
|
||||||
|
|
||||||
|
if (pFile == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char zw[1024];
|
||||||
|
fgets(zw, 1024, pFile);
|
||||||
|
// printf("%s", zw);
|
||||||
|
line = std::string(zw);
|
||||||
|
|
||||||
|
while ((line.size() > 0) || !(feof(pFile)))
|
||||||
|
{
|
||||||
|
// printf("%s", line.c_str());
|
||||||
|
zerlegt = ZerlegeZeile(line);
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "SSID"))
|
||||||
|
_ssid = zerlegt[1];
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "PASSWORD"))
|
||||||
|
_passphrase = zerlegt[1];
|
||||||
|
|
||||||
|
if (fgets(zw, 1024, pFile) == NULL)
|
||||||
|
{
|
||||||
|
line = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line = std::string(zw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
13
code/lib/connect_wlan/connect_wlan.h
Normal file
13
code/lib/connect_wlan/connect_wlan.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef CONNECT_WLAN_H
|
||||||
|
#define CONNECT_WLAN_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
const int CONNECTED_BIT = BIT0;
|
||||||
|
|
||||||
|
void initialise_wifi(std::string _ssid, std::string _passphrase);
|
||||||
|
|
||||||
|
void LoadWlanFromFile(std::string fn, std::string &_ssid, std::string &_passphrase);
|
||||||
|
|
||||||
|
#endif
|
||||||
128
code/lib/conversions/esp_jpg_decode.c
Normal file
128
code/lib/conversions/esp_jpg_decode.c
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "esp_jpg_decode.h"
|
||||||
|
|
||||||
|
#include "esp_system.h"
|
||||||
|
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||||
|
#include "esp32/rom/tjpgd.h"
|
||||||
|
#else
|
||||||
|
#error Target CONFIG_IDF_TARGET is not supported
|
||||||
|
#endif
|
||||||
|
#else // ESP32 Before IDF 4.0
|
||||||
|
#include "rom/tjpgd.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#define TAG ""
|
||||||
|
#else
|
||||||
|
#include "esp_log.h"
|
||||||
|
static const char* TAG = "esp_jpg_decode";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
jpg_scale_t scale;
|
||||||
|
jpg_reader_cb reader;
|
||||||
|
jpg_writer_cb writer;
|
||||||
|
void * arg;
|
||||||
|
size_t len;
|
||||||
|
size_t index;
|
||||||
|
} esp_jpg_decoder_t;
|
||||||
|
|
||||||
|
static const char * jd_errors[] = {
|
||||||
|
"Succeeded",
|
||||||
|
"Interrupted by output function",
|
||||||
|
"Device error or wrong termination of input stream",
|
||||||
|
"Insufficient memory pool for the image",
|
||||||
|
"Insufficient stream input buffer",
|
||||||
|
"Parameter error",
|
||||||
|
"Data format error",
|
||||||
|
"Right format but not supported",
|
||||||
|
"Not supported JPEG standard"
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||||
|
{
|
||||||
|
uint16_t x = rect->left;
|
||||||
|
uint16_t y = rect->top;
|
||||||
|
uint16_t w = rect->right + 1 - x;
|
||||||
|
uint16_t h = rect->bottom + 1 - y;
|
||||||
|
uint8_t *data = (uint8_t *)bitmap;
|
||||||
|
|
||||||
|
esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
|
||||||
|
|
||||||
|
if (jpeg->writer) {
|
||||||
|
return jpeg->writer(jpeg->arg, x, y, w, h, data);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t _jpg_read(JDEC *decoder, uint8_t *buf, uint32_t len)
|
||||||
|
{
|
||||||
|
esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
|
||||||
|
if (jpeg->len && len > (jpeg->len - jpeg->index)) {
|
||||||
|
len = jpeg->len - jpeg->index;
|
||||||
|
}
|
||||||
|
if (len) {
|
||||||
|
len = jpeg->reader(jpeg->arg, jpeg->index, buf, len);
|
||||||
|
if (!len) {
|
||||||
|
ESP_LOGE(TAG, "Read Fail at %u/%u", jpeg->index, jpeg->len);
|
||||||
|
}
|
||||||
|
jpeg->index += len;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg)
|
||||||
|
{
|
||||||
|
static uint8_t work[3100];
|
||||||
|
JDEC decoder;
|
||||||
|
esp_jpg_decoder_t jpeg;
|
||||||
|
|
||||||
|
jpeg.len = len;
|
||||||
|
jpeg.reader = reader;
|
||||||
|
jpeg.writer = writer;
|
||||||
|
jpeg.arg = arg;
|
||||||
|
jpeg.scale = scale;
|
||||||
|
jpeg.index = 0;
|
||||||
|
|
||||||
|
JRESULT jres = jd_prepare(&decoder, _jpg_read, work, 3100, &jpeg);
|
||||||
|
if(jres != JDR_OK){
|
||||||
|
ESP_LOGE(TAG, "JPG Header Parse Failed! %s", jd_errors[jres]);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t output_width = decoder.width / (1 << (uint8_t)(jpeg.scale));
|
||||||
|
uint16_t output_height = decoder.height / (1 << (uint8_t)(jpeg.scale));
|
||||||
|
|
||||||
|
//output start
|
||||||
|
writer(arg, 0, 0, output_width, output_height, NULL);
|
||||||
|
//output write
|
||||||
|
jres = jd_decomp(&decoder, _jpg_write, (uint8_t)jpeg.scale);
|
||||||
|
//output end
|
||||||
|
writer(arg, output_width, output_height, output_width, output_height, NULL);
|
||||||
|
|
||||||
|
if (jres != JDR_OK) {
|
||||||
|
ESP_LOGE(TAG, "JPG Decompression Failed! %s", jd_errors[jres]);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
//check if all data has been consumed.
|
||||||
|
if (len && jpeg.index < len) {
|
||||||
|
_jpg_read(&decoder, NULL, len - jpeg.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
43
code/lib/conversions/esp_jpg_decode.h
Normal file
43
code/lib/conversions/esp_jpg_decode.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef _ESP_JPG_DECODE_H_
|
||||||
|
#define _ESP_JPG_DECODE_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
JPG_SCALE_NONE,
|
||||||
|
JPG_SCALE_2X,
|
||||||
|
JPG_SCALE_4X,
|
||||||
|
JPG_SCALE_8X,
|
||||||
|
JPG_SCALE_MAX = JPG_SCALE_8X
|
||||||
|
} jpg_scale_t;
|
||||||
|
|
||||||
|
typedef size_t (* jpg_reader_cb)(void * arg, size_t index, uint8_t *buf, size_t len);
|
||||||
|
typedef bool (* jpg_writer_cb)(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data);
|
||||||
|
|
||||||
|
esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ESP_JPG_DECODE_H_ */
|
||||||
126
code/lib/conversions/img_converters.h
Normal file
126
code/lib/conversions/img_converters.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef _IMG_CONVERTERS_H_
|
||||||
|
#define _IMG_CONVERTERS_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_camera.h"
|
||||||
|
|
||||||
|
typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert image buffer to JPEG
|
||||||
|
*
|
||||||
|
* @param src Source buffer in RGB565, RGB888, YUYV or GRAYSCALE format
|
||||||
|
* @param src_len Length in bytes of the source buffer
|
||||||
|
* @param width Width in pixels of the source image
|
||||||
|
* @param height Height in pixels of the source image
|
||||||
|
* @param format Format of the source image
|
||||||
|
* @param quality JPEG quality of the resulting image
|
||||||
|
* @param cp Callback to be called to write the bytes of the output JPEG
|
||||||
|
* @param arg Pointer to be passed to the callback
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool fmt2jpg_cb(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpg_out_cb cb, void * arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert camera frame buffer to JPEG
|
||||||
|
*
|
||||||
|
* @param fb Source camera frame buffer
|
||||||
|
* @param quality JPEG quality of the resulting image
|
||||||
|
* @param cp Callback to be called to write the bytes of the output JPEG
|
||||||
|
* @param arg Pointer to be passed to the callback
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert image buffer to JPEG buffer
|
||||||
|
*
|
||||||
|
* @param src Source buffer in RGB565, RGB888, YUYV or GRAYSCALE format
|
||||||
|
* @param src_len Length in bytes of the source buffer
|
||||||
|
* @param width Width in pixels of the source image
|
||||||
|
* @param height Height in pixels of the source image
|
||||||
|
* @param format Format of the source image
|
||||||
|
* @param quality JPEG quality of the resulting image
|
||||||
|
* @param out Pointer to be populated with the address of the resulting buffer
|
||||||
|
* @param out_len Pointer to be populated with the length of the output buffer
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, uint8_t ** out, size_t * out_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert camera frame buffer to JPEG buffer
|
||||||
|
*
|
||||||
|
* @param fb Source camera frame buffer
|
||||||
|
* @param quality JPEG quality of the resulting image
|
||||||
|
* @param out Pointer to be populated with the address of the resulting buffer
|
||||||
|
* @param out_len Pointer to be populated with the length of the output buffer
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool frame2jpg(camera_fb_t * fb, uint8_t quality, uint8_t ** out, size_t * out_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert image buffer to BMP buffer
|
||||||
|
*
|
||||||
|
* @param src Source buffer in JPEG, RGB565, RGB888, YUYV or GRAYSCALE format
|
||||||
|
* @param src_len Length in bytes of the source buffer
|
||||||
|
* @param width Width in pixels of the source image
|
||||||
|
* @param height Height in pixels of the source image
|
||||||
|
* @param format Format of the source image
|
||||||
|
* @param out Pointer to be populated with the address of the resulting buffer
|
||||||
|
* @param out_len Pointer to be populated with the length of the output buffer
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t ** out, size_t * out_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert camera frame buffer to BMP buffer
|
||||||
|
*
|
||||||
|
* @param fb Source camera frame buffer
|
||||||
|
* @param out Pointer to be populated with the address of the resulting buffer
|
||||||
|
* @param out_len Pointer to be populated with the length of the output buffer
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert image buffer to RGB888 buffer (used for face detection)
|
||||||
|
*
|
||||||
|
* @param src Source buffer in JPEG, RGB565, RGB888, YUYV or GRAYSCALE format
|
||||||
|
* @param src_len Length in bytes of the source buffer
|
||||||
|
* @param format Format of the source image
|
||||||
|
* @param rgb_buf Pointer to the output buffer (width * height * 3)
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _IMG_CONVERTERS_H_ */
|
||||||
723
code/lib/conversions/jpge.cpp
Normal file
723
code/lib/conversions/jpge.cpp
Normal file
@@ -0,0 +1,723 @@
|
|||||||
|
// jpge.cpp - C++ class for JPEG compression.
|
||||||
|
// Public domain, Rich Geldreich <richgel99@gmail.com>
|
||||||
|
// v1.01, Dec. 18, 2010 - Initial release
|
||||||
|
// v1.02, Apr. 6, 2011 - Removed 2x2 ordered dither in H2V1 chroma subsampling method load_block_16_8_8(). (The rounding factor was 2, when it should have been 1. Either way, it wasn't helping.)
|
||||||
|
// v1.03, Apr. 16, 2011 - Added support for optimized Huffman code tables, optimized dynamic memory allocation down to only 1 alloc.
|
||||||
|
// Also from Alex Evans: Added RGBA support, linear memory allocator (no longer needed in v1.03).
|
||||||
|
// v1.04, May. 19, 2012: Forgot to set m_pFile ptr to NULL in cfile_stream::close(). Thanks to Owen Kaluza for reporting this bug.
|
||||||
|
// Code tweaks to fix VS2008 static code analysis warnings (all looked harmless).
|
||||||
|
// Code review revealed method load_block_16_8_8() (used for the non-default H2V1 sampling mode to downsample chroma) somehow didn't get the rounding factor fix from v1.02.
|
||||||
|
|
||||||
|
#include "jpge.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
|
#define JPGE_MAX(a,b) (((a)>(b))?(a):(b))
|
||||||
|
#define JPGE_MIN(a,b) (((a)<(b))?(a):(b))
|
||||||
|
|
||||||
|
namespace jpge {
|
||||||
|
|
||||||
|
static inline void *jpge_malloc(size_t nSize) {
|
||||||
|
void * b = malloc(nSize);
|
||||||
|
if(b){
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
|
}
|
||||||
|
static inline void jpge_free(void *p) { free(p); }
|
||||||
|
|
||||||
|
// Various JPEG enums and tables.
|
||||||
|
enum { M_SOF0 = 0xC0, M_DHT = 0xC4, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_APP0 = 0xE0 };
|
||||||
|
enum { DC_LUM_CODES = 12, AC_LUM_CODES = 256, DC_CHROMA_CODES = 12, AC_CHROMA_CODES = 256, MAX_HUFF_SYMBOLS = 257, MAX_HUFF_CODESIZE = 32 };
|
||||||
|
|
||||||
|
static const uint8 s_zag[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
|
||||||
|
static const int16 s_std_lum_quant[64] = { 16,11,12,14,12,10,16,14,13,14,18,17,16,19,24,40,26,24,22,22,24,49,35,37,29,40,58,51,61,60,57,51,56,55,64,72,92,78,64,68,87,69,55,56,80,109,81,87,95,98,103,104,103,62,77,113,121,112,100,120,92,101,103,99 };
|
||||||
|
static const int16 s_std_croma_quant[64] = { 17,18,18,24,21,24,47,26,26,47,99,66,56,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 };
|
||||||
|
static const uint8 s_dc_lum_bits[17] = { 0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 };
|
||||||
|
static const uint8 s_dc_lum_val[DC_LUM_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
|
||||||
|
static const uint8 s_ac_lum_bits[17] = { 0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d };
|
||||||
|
static const uint8 s_ac_lum_val[AC_LUM_CODES] = {
|
||||||
|
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
|
||||||
|
0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
|
||||||
|
0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
|
||||||
|
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
|
||||||
|
0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
|
||||||
|
0xf9,0xfa
|
||||||
|
};
|
||||||
|
static const uint8 s_dc_chroma_bits[17] = { 0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };
|
||||||
|
static const uint8 s_dc_chroma_val[DC_CHROMA_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
|
||||||
|
static const uint8 s_ac_chroma_bits[17] = { 0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 };
|
||||||
|
static const uint8 s_ac_chroma_val[AC_CHROMA_CODES] = {
|
||||||
|
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
|
||||||
|
0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
|
||||||
|
0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
|
||||||
|
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
|
||||||
|
0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
|
||||||
|
0xf9,0xfa
|
||||||
|
};
|
||||||
|
|
||||||
|
const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
|
||||||
|
|
||||||
|
static int32 m_last_quality = 0;
|
||||||
|
static int32 m_quantization_tables[2][64];
|
||||||
|
|
||||||
|
static bool m_huff_initialized = false;
|
||||||
|
static uint m_huff_codes[4][256];
|
||||||
|
static uint8 m_huff_code_sizes[4][256];
|
||||||
|
static uint8 m_huff_bits[4][17];
|
||||||
|
static uint8 m_huff_val[4][256];
|
||||||
|
|
||||||
|
static inline uint8 clamp(int i) {
|
||||||
|
if (i < 0) {
|
||||||
|
i = 0;
|
||||||
|
} else if (i > 255){
|
||||||
|
i = 255;
|
||||||
|
}
|
||||||
|
return static_cast<uint8>(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RGB_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels) {
|
||||||
|
for ( ; num_pixels; pDst += 3, pSrc += 3, num_pixels--) {
|
||||||
|
const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
|
||||||
|
pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
|
||||||
|
pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
|
||||||
|
pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RGB_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels) {
|
||||||
|
for ( ; num_pixels; pDst++, pSrc += 3, num_pixels--) {
|
||||||
|
pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Y_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels) {
|
||||||
|
for( ; num_pixels; pDst += 3, pSrc++, num_pixels--) {
|
||||||
|
pDst[0] = pSrc[0];
|
||||||
|
pDst[1] = 128;
|
||||||
|
pDst[2] = 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward DCT - DCT derived from jfdctint.
|
||||||
|
enum { CONST_BITS = 13, ROW_BITS = 2 };
|
||||||
|
#define DCT_DESCALE(x, n) (((x) + (((int32)1) << ((n) - 1))) >> (n))
|
||||||
|
#define DCT_MUL(var, c) (static_cast<int16>(var) * static_cast<int32>(c))
|
||||||
|
#define DCT1D(s0, s1, s2, s3, s4, s5, s6, s7) \
|
||||||
|
int32 t0 = s0 + s7, t7 = s0 - s7, t1 = s1 + s6, t6 = s1 - s6, t2 = s2 + s5, t5 = s2 - s5, t3 = s3 + s4, t4 = s3 - s4; \
|
||||||
|
int32 t10 = t0 + t3, t13 = t0 - t3, t11 = t1 + t2, t12 = t1 - t2; \
|
||||||
|
int32 u1 = DCT_MUL(t12 + t13, 4433); \
|
||||||
|
s2 = u1 + DCT_MUL(t13, 6270); \
|
||||||
|
s6 = u1 + DCT_MUL(t12, -15137); \
|
||||||
|
u1 = t4 + t7; \
|
||||||
|
int32 u2 = t5 + t6, u3 = t4 + t6, u4 = t5 + t7; \
|
||||||
|
int32 z5 = DCT_MUL(u3 + u4, 9633); \
|
||||||
|
t4 = DCT_MUL(t4, 2446); t5 = DCT_MUL(t5, 16819); \
|
||||||
|
t6 = DCT_MUL(t6, 25172); t7 = DCT_MUL(t7, 12299); \
|
||||||
|
u1 = DCT_MUL(u1, -7373); u2 = DCT_MUL(u2, -20995); \
|
||||||
|
u3 = DCT_MUL(u3, -16069); u4 = DCT_MUL(u4, -3196); \
|
||||||
|
u3 += z5; u4 += z5; \
|
||||||
|
s0 = t10 + t11; s1 = t7 + u1 + u4; s3 = t6 + u2 + u3; s4 = t10 - t11; s5 = t5 + u2 + u4; s7 = t4 + u1 + u3;
|
||||||
|
|
||||||
|
static void DCT2D(int32 *p) {
|
||||||
|
int32 c, *q = p;
|
||||||
|
for (c = 7; c >= 0; c--, q += 8) {
|
||||||
|
int32 s0 = q[0], s1 = q[1], s2 = q[2], s3 = q[3], s4 = q[4], s5 = q[5], s6 = q[6], s7 = q[7];
|
||||||
|
DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
|
||||||
|
q[0] = s0 << ROW_BITS; q[1] = DCT_DESCALE(s1, CONST_BITS-ROW_BITS); q[2] = DCT_DESCALE(s2, CONST_BITS-ROW_BITS); q[3] = DCT_DESCALE(s3, CONST_BITS-ROW_BITS);
|
||||||
|
q[4] = s4 << ROW_BITS; q[5] = DCT_DESCALE(s5, CONST_BITS-ROW_BITS); q[6] = DCT_DESCALE(s6, CONST_BITS-ROW_BITS); q[7] = DCT_DESCALE(s7, CONST_BITS-ROW_BITS);
|
||||||
|
}
|
||||||
|
for (q = p, c = 7; c >= 0; c--, q++) {
|
||||||
|
int32 s0 = q[0*8], s1 = q[1*8], s2 = q[2*8], s3 = q[3*8], s4 = q[4*8], s5 = q[5*8], s6 = q[6*8], s7 = q[7*8];
|
||||||
|
DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
|
||||||
|
q[0*8] = DCT_DESCALE(s0, ROW_BITS+3); q[1*8] = DCT_DESCALE(s1, CONST_BITS+ROW_BITS+3); q[2*8] = DCT_DESCALE(s2, CONST_BITS+ROW_BITS+3); q[3*8] = DCT_DESCALE(s3, CONST_BITS+ROW_BITS+3);
|
||||||
|
q[4*8] = DCT_DESCALE(s4, ROW_BITS+3); q[5*8] = DCT_DESCALE(s5, CONST_BITS+ROW_BITS+3); q[6*8] = DCT_DESCALE(s6, CONST_BITS+ROW_BITS+3); q[7*8] = DCT_DESCALE(s7, CONST_BITS+ROW_BITS+3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the actual canonical Huffman codes/code sizes given the JPEG huff bits and val arrays.
|
||||||
|
static void compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val)
|
||||||
|
{
|
||||||
|
int i, l, last_p, si;
|
||||||
|
static uint8 huff_size[257];
|
||||||
|
static uint huff_code[257];
|
||||||
|
uint code;
|
||||||
|
|
||||||
|
int p = 0;
|
||||||
|
for (l = 1; l <= 16; l++) {
|
||||||
|
for (i = 1; i <= bits[l]; i++) {
|
||||||
|
huff_size[p++] = (char)l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
huff_size[p] = 0;
|
||||||
|
last_p = p; // write sentinel
|
||||||
|
|
||||||
|
code = 0; si = huff_size[0]; p = 0;
|
||||||
|
|
||||||
|
while (huff_size[p]) {
|
||||||
|
while (huff_size[p] == si) {
|
||||||
|
huff_code[p++] = code++;
|
||||||
|
}
|
||||||
|
code <<= 1;
|
||||||
|
si++;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(codes, 0, sizeof(codes[0])*256);
|
||||||
|
memset(code_sizes, 0, sizeof(code_sizes[0])*256);
|
||||||
|
for (p = 0; p < last_p; p++) {
|
||||||
|
codes[val[p]] = huff_code[p];
|
||||||
|
code_sizes[val[p]] = huff_size[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::flush_output_buffer()
|
||||||
|
{
|
||||||
|
if (m_out_buf_left != JPGE_OUT_BUF_SIZE) {
|
||||||
|
m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(m_out_buf, JPGE_OUT_BUF_SIZE - m_out_buf_left);
|
||||||
|
}
|
||||||
|
m_pOut_buf = m_out_buf;
|
||||||
|
m_out_buf_left = JPGE_OUT_BUF_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::emit_byte(uint8 i)
|
||||||
|
{
|
||||||
|
*m_pOut_buf++ = i;
|
||||||
|
if (--m_out_buf_left == 0) {
|
||||||
|
flush_output_buffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::put_bits(uint bits, uint len)
|
||||||
|
{
|
||||||
|
uint8 c = 0;
|
||||||
|
m_bit_buffer |= ((uint32)bits << (24 - (m_bits_in += len)));
|
||||||
|
while (m_bits_in >= 8) {
|
||||||
|
c = (uint8)((m_bit_buffer >> 16) & 0xFF);
|
||||||
|
emit_byte(c);
|
||||||
|
if (c == 0xFF) {
|
||||||
|
emit_byte(0);
|
||||||
|
}
|
||||||
|
m_bit_buffer <<= 8;
|
||||||
|
m_bits_in -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::emit_word(uint i)
|
||||||
|
{
|
||||||
|
emit_byte(uint8(i >> 8)); emit_byte(uint8(i & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
// JPEG marker generation.
|
||||||
|
void jpeg_encoder::emit_marker(int marker)
|
||||||
|
{
|
||||||
|
emit_byte(uint8(0xFF)); emit_byte(uint8(marker));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit JFIF marker
|
||||||
|
void jpeg_encoder::emit_jfif_app0()
|
||||||
|
{
|
||||||
|
emit_marker(M_APP0);
|
||||||
|
emit_word(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1);
|
||||||
|
emit_byte(0x4A); emit_byte(0x46); emit_byte(0x49); emit_byte(0x46); /* Identifier: ASCII "JFIF" */
|
||||||
|
emit_byte(0);
|
||||||
|
emit_byte(1); /* Major version */
|
||||||
|
emit_byte(1); /* Minor version */
|
||||||
|
emit_byte(0); /* Density unit */
|
||||||
|
emit_word(1);
|
||||||
|
emit_word(1);
|
||||||
|
emit_byte(0); /* No thumbnail image */
|
||||||
|
emit_byte(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit quantization tables
|
||||||
|
void jpeg_encoder::emit_dqt()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ((m_num_components == 3) ? 2 : 1); i++)
|
||||||
|
{
|
||||||
|
emit_marker(M_DQT);
|
||||||
|
emit_word(64 + 1 + 2);
|
||||||
|
emit_byte(static_cast<uint8>(i));
|
||||||
|
for (int j = 0; j < 64; j++)
|
||||||
|
emit_byte(static_cast<uint8>(m_quantization_tables[i][j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit start of frame marker
|
||||||
|
void jpeg_encoder::emit_sof()
|
||||||
|
{
|
||||||
|
emit_marker(M_SOF0); /* baseline */
|
||||||
|
emit_word(3 * m_num_components + 2 + 5 + 1);
|
||||||
|
emit_byte(8); /* precision */
|
||||||
|
emit_word(m_image_y);
|
||||||
|
emit_word(m_image_x);
|
||||||
|
emit_byte(m_num_components);
|
||||||
|
for (int i = 0; i < m_num_components; i++)
|
||||||
|
{
|
||||||
|
emit_byte(static_cast<uint8>(i + 1)); /* component ID */
|
||||||
|
emit_byte((m_comp_h_samp[i] << 4) + m_comp_v_samp[i]); /* h and v sampling */
|
||||||
|
emit_byte(i > 0); /* quant. table num */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit Huffman table.
|
||||||
|
void jpeg_encoder::emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag)
|
||||||
|
{
|
||||||
|
emit_marker(M_DHT);
|
||||||
|
|
||||||
|
int length = 0;
|
||||||
|
for (int i = 1; i <= 16; i++)
|
||||||
|
length += bits[i];
|
||||||
|
|
||||||
|
emit_word(length + 2 + 1 + 16);
|
||||||
|
emit_byte(static_cast<uint8>(index + (ac_flag << 4)));
|
||||||
|
|
||||||
|
for (int i = 1; i <= 16; i++)
|
||||||
|
emit_byte(bits[i]);
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
emit_byte(val[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit all Huffman tables.
|
||||||
|
void jpeg_encoder::emit_dhts()
|
||||||
|
{
|
||||||
|
emit_dht(m_huff_bits[0+0], m_huff_val[0+0], 0, false);
|
||||||
|
emit_dht(m_huff_bits[2+0], m_huff_val[2+0], 0, true);
|
||||||
|
if (m_num_components == 3) {
|
||||||
|
emit_dht(m_huff_bits[0+1], m_huff_val[0+1], 1, false);
|
||||||
|
emit_dht(m_huff_bits[2+1], m_huff_val[2+1], 1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit start of scan
|
||||||
|
void jpeg_encoder::emit_sos()
|
||||||
|
{
|
||||||
|
emit_marker(M_SOS);
|
||||||
|
emit_word(2 * m_num_components + 2 + 1 + 3);
|
||||||
|
emit_byte(m_num_components);
|
||||||
|
for (int i = 0; i < m_num_components; i++)
|
||||||
|
{
|
||||||
|
emit_byte(static_cast<uint8>(i + 1));
|
||||||
|
if (i == 0)
|
||||||
|
emit_byte((0 << 4) + 0);
|
||||||
|
else
|
||||||
|
emit_byte((1 << 4) + 1);
|
||||||
|
}
|
||||||
|
emit_byte(0); /* spectral selection */
|
||||||
|
emit_byte(63);
|
||||||
|
emit_byte(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::load_block_8_8_grey(int x)
|
||||||
|
{
|
||||||
|
uint8 *pSrc;
|
||||||
|
sample_array_t *pDst = m_sample_array;
|
||||||
|
x <<= 3;
|
||||||
|
for (int i = 0; i < 8; i++, pDst += 8)
|
||||||
|
{
|
||||||
|
pSrc = m_mcu_lines[i] + x;
|
||||||
|
pDst[0] = pSrc[0] - 128; pDst[1] = pSrc[1] - 128; pDst[2] = pSrc[2] - 128; pDst[3] = pSrc[3] - 128;
|
||||||
|
pDst[4] = pSrc[4] - 128; pDst[5] = pSrc[5] - 128; pDst[6] = pSrc[6] - 128; pDst[7] = pSrc[7] - 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::load_block_8_8(int x, int y, int c)
|
||||||
|
{
|
||||||
|
uint8 *pSrc;
|
||||||
|
sample_array_t *pDst = m_sample_array;
|
||||||
|
x = (x * (8 * 3)) + c;
|
||||||
|
y <<= 3;
|
||||||
|
for (int i = 0; i < 8; i++, pDst += 8)
|
||||||
|
{
|
||||||
|
pSrc = m_mcu_lines[y + i] + x;
|
||||||
|
pDst[0] = pSrc[0 * 3] - 128; pDst[1] = pSrc[1 * 3] - 128; pDst[2] = pSrc[2 * 3] - 128; pDst[3] = pSrc[3 * 3] - 128;
|
||||||
|
pDst[4] = pSrc[4 * 3] - 128; pDst[5] = pSrc[5 * 3] - 128; pDst[6] = pSrc[6 * 3] - 128; pDst[7] = pSrc[7 * 3] - 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::load_block_16_8(int x, int c)
|
||||||
|
{
|
||||||
|
uint8 *pSrc1, *pSrc2;
|
||||||
|
sample_array_t *pDst = m_sample_array;
|
||||||
|
x = (x * (16 * 3)) + c;
|
||||||
|
int a = 0, b = 2;
|
||||||
|
for (int i = 0; i < 16; i += 2, pDst += 8)
|
||||||
|
{
|
||||||
|
pSrc1 = m_mcu_lines[i + 0] + x;
|
||||||
|
pSrc2 = m_mcu_lines[i + 1] + x;
|
||||||
|
pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3] + pSrc2[ 0 * 3] + pSrc2[ 1 * 3] + a) >> 2) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3] + pSrc2[ 2 * 3] + pSrc2[ 3 * 3] + b) >> 2) - 128;
|
||||||
|
pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3] + pSrc2[ 4 * 3] + pSrc2[ 5 * 3] + a) >> 2) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3] + pSrc2[ 6 * 3] + pSrc2[ 7 * 3] + b) >> 2) - 128;
|
||||||
|
pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3] + pSrc2[ 8 * 3] + pSrc2[ 9 * 3] + a) >> 2) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + pSrc2[10 * 3] + pSrc2[11 * 3] + b) >> 2) - 128;
|
||||||
|
pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + pSrc2[12 * 3] + pSrc2[13 * 3] + a) >> 2) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + pSrc2[14 * 3] + pSrc2[15 * 3] + b) >> 2) - 128;
|
||||||
|
int temp = a; a = b; b = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::load_block_16_8_8(int x, int c)
|
||||||
|
{
|
||||||
|
uint8 *pSrc1;
|
||||||
|
sample_array_t *pDst = m_sample_array;
|
||||||
|
x = (x * (16 * 3)) + c;
|
||||||
|
for (int i = 0; i < 8; i++, pDst += 8)
|
||||||
|
{
|
||||||
|
pSrc1 = m_mcu_lines[i + 0] + x;
|
||||||
|
pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3]) >> 1) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3]) >> 1) - 128;
|
||||||
|
pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3]) >> 1) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3]) >> 1) - 128;
|
||||||
|
pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3]) >> 1) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3]) >> 1) - 128;
|
||||||
|
pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3]) >> 1) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3]) >> 1) - 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::load_quantized_coefficients(int component_num)
|
||||||
|
{
|
||||||
|
int32 *q = m_quantization_tables[component_num > 0];
|
||||||
|
int16 *pDst = m_coefficient_array;
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
sample_array_t j = m_sample_array[s_zag[i]];
|
||||||
|
if (j < 0)
|
||||||
|
{
|
||||||
|
if ((j = -j + (*q >> 1)) < *q)
|
||||||
|
*pDst++ = 0;
|
||||||
|
else
|
||||||
|
*pDst++ = static_cast<int16>(-(j / *q));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((j = j + (*q >> 1)) < *q)
|
||||||
|
*pDst++ = 0;
|
||||||
|
else
|
||||||
|
*pDst++ = static_cast<int16>((j / *q));
|
||||||
|
}
|
||||||
|
q++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::code_coefficients_pass_two(int component_num)
|
||||||
|
{
|
||||||
|
int i, j, run_len, nbits, temp1, temp2;
|
||||||
|
int16 *pSrc = m_coefficient_array;
|
||||||
|
uint *codes[2];
|
||||||
|
uint8 *code_sizes[2];
|
||||||
|
|
||||||
|
if (component_num == 0)
|
||||||
|
{
|
||||||
|
codes[0] = m_huff_codes[0 + 0]; codes[1] = m_huff_codes[2 + 0];
|
||||||
|
code_sizes[0] = m_huff_code_sizes[0 + 0]; code_sizes[1] = m_huff_code_sizes[2 + 0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
codes[0] = m_huff_codes[0 + 1]; codes[1] = m_huff_codes[2 + 1];
|
||||||
|
code_sizes[0] = m_huff_code_sizes[0 + 1]; code_sizes[1] = m_huff_code_sizes[2 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
temp1 = temp2 = pSrc[0] - m_last_dc_val[component_num];
|
||||||
|
m_last_dc_val[component_num] = pSrc[0];
|
||||||
|
|
||||||
|
if (temp1 < 0)
|
||||||
|
{
|
||||||
|
temp1 = -temp1; temp2--;
|
||||||
|
}
|
||||||
|
|
||||||
|
nbits = 0;
|
||||||
|
while (temp1)
|
||||||
|
{
|
||||||
|
nbits++; temp1 >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
put_bits(codes[0][nbits], code_sizes[0][nbits]);
|
||||||
|
if (nbits) put_bits(temp2 & ((1 << nbits) - 1), nbits);
|
||||||
|
|
||||||
|
for (run_len = 0, i = 1; i < 64; i++)
|
||||||
|
{
|
||||||
|
if ((temp1 = m_coefficient_array[i]) == 0)
|
||||||
|
run_len++;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (run_len >= 16)
|
||||||
|
{
|
||||||
|
put_bits(codes[1][0xF0], code_sizes[1][0xF0]);
|
||||||
|
run_len -= 16;
|
||||||
|
}
|
||||||
|
if ((temp2 = temp1) < 0)
|
||||||
|
{
|
||||||
|
temp1 = -temp1;
|
||||||
|
temp2--;
|
||||||
|
}
|
||||||
|
nbits = 1;
|
||||||
|
while (temp1 >>= 1)
|
||||||
|
nbits++;
|
||||||
|
j = (run_len << 4) + nbits;
|
||||||
|
put_bits(codes[1][j], code_sizes[1][j]);
|
||||||
|
put_bits(temp2 & ((1 << nbits) - 1), nbits);
|
||||||
|
run_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (run_len)
|
||||||
|
put_bits(codes[1][0], code_sizes[1][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::code_block(int component_num)
|
||||||
|
{
|
||||||
|
DCT2D(m_sample_array);
|
||||||
|
load_quantized_coefficients(component_num);
|
||||||
|
code_coefficients_pass_two(component_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::process_mcu_row()
|
||||||
|
{
|
||||||
|
if (m_num_components == 1)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_mcus_per_row; i++)
|
||||||
|
{
|
||||||
|
load_block_8_8_grey(i); code_block(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_mcus_per_row; i++)
|
||||||
|
{
|
||||||
|
load_block_8_8(i, 0, 0); code_block(0); load_block_8_8(i, 0, 1); code_block(1); load_block_8_8(i, 0, 2); code_block(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_mcus_per_row; i++)
|
||||||
|
{
|
||||||
|
load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
|
||||||
|
load_block_16_8_8(i, 1); code_block(1); load_block_16_8_8(i, 2); code_block(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_mcus_per_row; i++)
|
||||||
|
{
|
||||||
|
load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
|
||||||
|
load_block_8_8(i * 2 + 0, 1, 0); code_block(0); load_block_8_8(i * 2 + 1, 1, 0); code_block(0);
|
||||||
|
load_block_16_8(i, 1); code_block(1); load_block_16_8(i, 2); code_block(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::load_mcu(const void *pSrc)
|
||||||
|
{
|
||||||
|
const uint8* Psrc = reinterpret_cast<const uint8*>(pSrc);
|
||||||
|
|
||||||
|
uint8* pDst = m_mcu_lines[m_mcu_y_ofs]; // OK to write up to m_image_bpl_xlt bytes to pDst
|
||||||
|
|
||||||
|
if (m_num_components == 1) {
|
||||||
|
if (m_image_bpp == 3)
|
||||||
|
RGB_to_Y(pDst, Psrc, m_image_x);
|
||||||
|
else
|
||||||
|
memcpy(pDst, Psrc, m_image_x);
|
||||||
|
} else {
|
||||||
|
if (m_image_bpp == 3)
|
||||||
|
RGB_to_YCC(pDst, Psrc, m_image_x);
|
||||||
|
else
|
||||||
|
Y_to_YCC(pDst, Psrc, m_image_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possibly duplicate pixels at end of scanline if not a multiple of 8 or 16
|
||||||
|
if (m_num_components == 1)
|
||||||
|
memset(m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt, pDst[m_image_bpl_xlt - 1], m_image_x_mcu - m_image_x);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const uint8 y = pDst[m_image_bpl_xlt - 3 + 0], cb = pDst[m_image_bpl_xlt - 3 + 1], cr = pDst[m_image_bpl_xlt - 3 + 2];
|
||||||
|
uint8 *q = m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt;
|
||||||
|
for (int i = m_image_x; i < m_image_x_mcu; i++)
|
||||||
|
{
|
||||||
|
*q++ = y; *q++ = cb; *q++ = cr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++m_mcu_y_ofs == m_mcu_y)
|
||||||
|
{
|
||||||
|
process_mcu_row();
|
||||||
|
m_mcu_y_ofs = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quantization table generation.
|
||||||
|
void jpeg_encoder::compute_quant_table(int32 *pDst, const int16 *pSrc)
|
||||||
|
{
|
||||||
|
int32 q;
|
||||||
|
if (m_params.m_quality < 50)
|
||||||
|
q = 5000 / m_params.m_quality;
|
||||||
|
else
|
||||||
|
q = 200 - m_params.m_quality * 2;
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
int32 j = *pSrc++; j = (j * q + 50L) / 100L;
|
||||||
|
*pDst++ = JPGE_MIN(JPGE_MAX(j, 1), 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Higher-level methods.
|
||||||
|
bool jpeg_encoder::jpg_open(int p_x_res, int p_y_res, int src_channels)
|
||||||
|
{
|
||||||
|
m_num_components = 3;
|
||||||
|
switch (m_params.m_subsampling)
|
||||||
|
{
|
||||||
|
case Y_ONLY:
|
||||||
|
{
|
||||||
|
m_num_components = 1;
|
||||||
|
m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
|
||||||
|
m_mcu_x = 8; m_mcu_y = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case H1V1:
|
||||||
|
{
|
||||||
|
m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
|
||||||
|
m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
|
||||||
|
m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
|
||||||
|
m_mcu_x = 8; m_mcu_y = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case H2V1:
|
||||||
|
{
|
||||||
|
m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 1;
|
||||||
|
m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
|
||||||
|
m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
|
||||||
|
m_mcu_x = 16; m_mcu_y = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case H2V2:
|
||||||
|
{
|
||||||
|
m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 2;
|
||||||
|
m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
|
||||||
|
m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
|
||||||
|
m_mcu_x = 16; m_mcu_y = 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_image_x = p_x_res; m_image_y = p_y_res;
|
||||||
|
m_image_bpp = src_channels;
|
||||||
|
m_image_bpl = m_image_x * src_channels;
|
||||||
|
m_image_x_mcu = (m_image_x + m_mcu_x - 1) & (~(m_mcu_x - 1));
|
||||||
|
m_image_y_mcu = (m_image_y + m_mcu_y - 1) & (~(m_mcu_y - 1));
|
||||||
|
m_image_bpl_xlt = m_image_x * m_num_components;
|
||||||
|
m_image_bpl_mcu = m_image_x_mcu * m_num_components;
|
||||||
|
m_mcus_per_row = m_image_x_mcu / m_mcu_x;
|
||||||
|
|
||||||
|
if ((m_mcu_lines[0] = static_cast<uint8*>(jpge_malloc(m_image_bpl_mcu * m_mcu_y))) == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < m_mcu_y; i++)
|
||||||
|
m_mcu_lines[i] = m_mcu_lines[i-1] + m_image_bpl_mcu;
|
||||||
|
|
||||||
|
if(m_last_quality != m_params.m_quality){
|
||||||
|
m_last_quality = m_params.m_quality;
|
||||||
|
compute_quant_table(m_quantization_tables[0], s_std_lum_quant);
|
||||||
|
compute_quant_table(m_quantization_tables[1], s_std_croma_quant);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_huff_initialized){
|
||||||
|
m_huff_initialized = true;
|
||||||
|
|
||||||
|
memcpy(m_huff_bits[0+0], s_dc_lum_bits, 17); memcpy(m_huff_val[0+0], s_dc_lum_val, DC_LUM_CODES);
|
||||||
|
memcpy(m_huff_bits[2+0], s_ac_lum_bits, 17); memcpy(m_huff_val[2+0], s_ac_lum_val, AC_LUM_CODES);
|
||||||
|
memcpy(m_huff_bits[0+1], s_dc_chroma_bits, 17); memcpy(m_huff_val[0+1], s_dc_chroma_val, DC_CHROMA_CODES);
|
||||||
|
memcpy(m_huff_bits[2+1], s_ac_chroma_bits, 17); memcpy(m_huff_val[2+1], s_ac_chroma_val, AC_CHROMA_CODES);
|
||||||
|
|
||||||
|
compute_huffman_table(&m_huff_codes[0+0][0], &m_huff_code_sizes[0+0][0], m_huff_bits[0+0], m_huff_val[0+0]);
|
||||||
|
compute_huffman_table(&m_huff_codes[2+0][0], &m_huff_code_sizes[2+0][0], m_huff_bits[2+0], m_huff_val[2+0]);
|
||||||
|
compute_huffman_table(&m_huff_codes[0+1][0], &m_huff_code_sizes[0+1][0], m_huff_bits[0+1], m_huff_val[0+1]);
|
||||||
|
compute_huffman_table(&m_huff_codes[2+1][0], &m_huff_code_sizes[2+1][0], m_huff_bits[2+1], m_huff_val[2+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_out_buf_left = JPGE_OUT_BUF_SIZE;
|
||||||
|
m_pOut_buf = m_out_buf;
|
||||||
|
m_bit_buffer = 0;
|
||||||
|
m_bits_in = 0;
|
||||||
|
m_mcu_y_ofs = 0;
|
||||||
|
m_pass_num = 2;
|
||||||
|
memset(m_last_dc_val, 0, 3 * sizeof(m_last_dc_val[0]));
|
||||||
|
|
||||||
|
// Emit all markers at beginning of image file.
|
||||||
|
emit_marker(M_SOI);
|
||||||
|
emit_jfif_app0();
|
||||||
|
emit_dqt();
|
||||||
|
emit_sof();
|
||||||
|
emit_dhts();
|
||||||
|
emit_sos();
|
||||||
|
|
||||||
|
return m_all_stream_writes_succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jpeg_encoder::process_end_of_image()
|
||||||
|
{
|
||||||
|
if (m_mcu_y_ofs) {
|
||||||
|
if (m_mcu_y_ofs < 16) { // check here just to shut up static analysis
|
||||||
|
for (int i = m_mcu_y_ofs; i < m_mcu_y; i++) {
|
||||||
|
memcpy(m_mcu_lines[i], m_mcu_lines[m_mcu_y_ofs - 1], m_image_bpl_mcu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process_mcu_row();
|
||||||
|
}
|
||||||
|
|
||||||
|
put_bits(0x7F, 7);
|
||||||
|
emit_marker(M_EOI);
|
||||||
|
flush_output_buffer();
|
||||||
|
m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(NULL, 0);
|
||||||
|
m_pass_num++; // purposely bump up m_pass_num, for debugging
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::clear()
|
||||||
|
{
|
||||||
|
m_mcu_lines[0] = NULL;
|
||||||
|
m_pass_num = 0;
|
||||||
|
m_all_stream_writes_succeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_encoder::jpeg_encoder()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_encoder::~jpeg_encoder()
|
||||||
|
{
|
||||||
|
deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jpeg_encoder::init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params)
|
||||||
|
{
|
||||||
|
deinit();
|
||||||
|
if (((!pStream) || (width < 1) || (height < 1)) || ((src_channels != 1) && (src_channels != 3) && (src_channels != 4)) || (!comp_params.check())) return false;
|
||||||
|
m_pStream = pStream;
|
||||||
|
m_params = comp_params;
|
||||||
|
return jpg_open(width, height, src_channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jpeg_encoder::deinit()
|
||||||
|
{
|
||||||
|
jpge_free(m_mcu_lines[0]);
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jpeg_encoder::process_scanline(const void* pScanline)
|
||||||
|
{
|
||||||
|
if ((m_pass_num < 1) || (m_pass_num > 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_all_stream_writes_succeeded) {
|
||||||
|
if (!pScanline) {
|
||||||
|
if (!process_end_of_image()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
load_mcu(pScanline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_all_stream_writes_succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace jpge
|
||||||
142
code/lib/conversions/jpge.h
Normal file
142
code/lib/conversions/jpge.h
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
// jpge.h - C++ class for JPEG compression.
|
||||||
|
// Public domain, Rich Geldreich <richgel99@gmail.com>
|
||||||
|
// Alex Evans: Added RGBA support, linear memory allocator.
|
||||||
|
#ifndef JPEG_ENCODER_H
|
||||||
|
#define JPEG_ENCODER_H
|
||||||
|
|
||||||
|
namespace jpge
|
||||||
|
{
|
||||||
|
typedef unsigned char uint8;
|
||||||
|
typedef signed short int16;
|
||||||
|
typedef signed int int32;
|
||||||
|
typedef unsigned short uint16;
|
||||||
|
typedef unsigned int uint32;
|
||||||
|
typedef unsigned int uint;
|
||||||
|
|
||||||
|
// JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common.
|
||||||
|
enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 };
|
||||||
|
|
||||||
|
// JPEG compression parameters structure.
|
||||||
|
struct params {
|
||||||
|
inline params() : m_quality(85), m_subsampling(H2V2) { }
|
||||||
|
|
||||||
|
inline bool check() const {
|
||||||
|
if ((m_quality < 1) || (m_quality > 100)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((uint)m_subsampling > (uint)H2V2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quality: 1-100, higher is better. Typical values are around 50-95.
|
||||||
|
int m_quality;
|
||||||
|
|
||||||
|
// m_subsampling:
|
||||||
|
// 0 = Y (grayscale) only
|
||||||
|
// 1 = H1V1 subsampling (YCbCr 1x1x1, 3 blocks per MCU)
|
||||||
|
// 2 = H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU)
|
||||||
|
// 3 = H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common)
|
||||||
|
subsampling_t m_subsampling;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Output stream abstract class - used by the jpeg_encoder class to write to the output stream.
|
||||||
|
// put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts.
|
||||||
|
class output_stream {
|
||||||
|
public:
|
||||||
|
virtual ~output_stream() { };
|
||||||
|
virtual bool put_buf(const void* Pbuf, int len) = 0;
|
||||||
|
virtual uint get_size() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lower level jpeg_encoder class - useful if more control is needed than the above helper functions.
|
||||||
|
class jpeg_encoder {
|
||||||
|
public:
|
||||||
|
jpeg_encoder();
|
||||||
|
~jpeg_encoder();
|
||||||
|
|
||||||
|
// Initializes the compressor.
|
||||||
|
// pStream: The stream object to use for writing compressed data.
|
||||||
|
// params - Compression parameters structure, defined above.
|
||||||
|
// width, height - Image dimensions.
|
||||||
|
// channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data.
|
||||||
|
// Returns false on out of memory or if a stream write fails.
|
||||||
|
bool init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params = params());
|
||||||
|
|
||||||
|
// Call this method with each source scanline.
|
||||||
|
// width * src_channels bytes per scanline is expected (RGB or Y format).
|
||||||
|
// You must call with NULL after all scanlines are processed to finish compression.
|
||||||
|
// Returns false on out of memory or if a stream write fails.
|
||||||
|
bool process_scanline(const void* pScanline);
|
||||||
|
|
||||||
|
// Deinitializes the compressor, freeing any allocated memory. May be called at any time.
|
||||||
|
void deinit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
jpeg_encoder(const jpeg_encoder &);
|
||||||
|
jpeg_encoder &operator =(const jpeg_encoder &);
|
||||||
|
|
||||||
|
typedef int32 sample_array_t;
|
||||||
|
enum { JPGE_OUT_BUF_SIZE = 512 };
|
||||||
|
|
||||||
|
output_stream *m_pStream;
|
||||||
|
params m_params;
|
||||||
|
uint8 m_num_components;
|
||||||
|
uint8 m_comp_h_samp[3], m_comp_v_samp[3];
|
||||||
|
int m_image_x, m_image_y, m_image_bpp, m_image_bpl;
|
||||||
|
int m_image_x_mcu, m_image_y_mcu;
|
||||||
|
int m_image_bpl_xlt, m_image_bpl_mcu;
|
||||||
|
int m_mcus_per_row;
|
||||||
|
int m_mcu_x, m_mcu_y;
|
||||||
|
uint8 *m_mcu_lines[16];
|
||||||
|
uint8 m_mcu_y_ofs;
|
||||||
|
sample_array_t m_sample_array[64];
|
||||||
|
int16 m_coefficient_array[64];
|
||||||
|
|
||||||
|
int m_last_dc_val[3];
|
||||||
|
uint8 m_out_buf[JPGE_OUT_BUF_SIZE];
|
||||||
|
uint8 *m_pOut_buf;
|
||||||
|
uint m_out_buf_left;
|
||||||
|
uint32 m_bit_buffer;
|
||||||
|
uint m_bits_in;
|
||||||
|
uint8 m_pass_num;
|
||||||
|
bool m_all_stream_writes_succeeded;
|
||||||
|
|
||||||
|
bool jpg_open(int p_x_res, int p_y_res, int src_channels);
|
||||||
|
|
||||||
|
void flush_output_buffer();
|
||||||
|
void put_bits(uint bits, uint len);
|
||||||
|
|
||||||
|
void emit_byte(uint8 i);
|
||||||
|
void emit_word(uint i);
|
||||||
|
void emit_marker(int marker);
|
||||||
|
|
||||||
|
void emit_jfif_app0();
|
||||||
|
void emit_dqt();
|
||||||
|
void emit_sof();
|
||||||
|
void emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag);
|
||||||
|
void emit_dhts();
|
||||||
|
void emit_sos();
|
||||||
|
|
||||||
|
void compute_quant_table(int32 *dst, const int16 *src);
|
||||||
|
void load_quantized_coefficients(int component_num);
|
||||||
|
|
||||||
|
void load_block_8_8_grey(int x);
|
||||||
|
void load_block_8_8(int x, int y, int c);
|
||||||
|
void load_block_16_8(int x, int c);
|
||||||
|
void load_block_16_8_8(int x, int c);
|
||||||
|
|
||||||
|
void code_coefficients_pass_two(int component_num);
|
||||||
|
void code_block(int component_num);
|
||||||
|
|
||||||
|
void process_mcu_row();
|
||||||
|
bool process_end_of_image();
|
||||||
|
void load_mcu(const void* src);
|
||||||
|
void clear();
|
||||||
|
void init();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace jpge
|
||||||
|
|
||||||
|
#endif // JPEG_ENCODER
|
||||||
315
code/lib/conversions/to_bmp.c
Normal file
315
code/lib/conversions/to_bmp.c
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "img_converters.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "yuv.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_jpg_decode.h"
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#define TAG ""
|
||||||
|
#else
|
||||||
|
#include "esp_log.h"
|
||||||
|
static const char* TAG = "to_bmp";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const int BMP_HEADER_LEN = 54;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t filesize;
|
||||||
|
uint32_t reserved;
|
||||||
|
uint32_t fileoffset_to_pixelarray;
|
||||||
|
uint32_t dibheadersize;
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
uint16_t planes;
|
||||||
|
uint16_t bitsperpixel;
|
||||||
|
uint32_t compression;
|
||||||
|
uint32_t imagesize;
|
||||||
|
uint32_t ypixelpermeter;
|
||||||
|
uint32_t xpixelpermeter;
|
||||||
|
uint32_t numcolorspallette;
|
||||||
|
uint32_t mostimpcolor;
|
||||||
|
} bmp_header_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
uint16_t data_offset;
|
||||||
|
const uint8_t *input;
|
||||||
|
uint8_t *output;
|
||||||
|
} rgb_jpg_decoder;
|
||||||
|
|
||||||
|
static void *_malloc(size_t size)
|
||||||
|
{
|
||||||
|
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
//output buffer and image width
|
||||||
|
static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data)
|
||||||
|
{
|
||||||
|
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
|
||||||
|
if(!data){
|
||||||
|
if(x == 0 && y == 0){
|
||||||
|
//write start
|
||||||
|
jpeg->width = w;
|
||||||
|
jpeg->height = h;
|
||||||
|
//if output is null, this is BMP
|
||||||
|
if(!jpeg->output){
|
||||||
|
jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset);
|
||||||
|
if(!jpeg->output){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//write end
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t jw = jpeg->width*3;
|
||||||
|
size_t t = y * jw;
|
||||||
|
size_t b = t + (h * jw);
|
||||||
|
size_t l = x * 3;
|
||||||
|
uint8_t *out = jpeg->output+jpeg->data_offset;
|
||||||
|
uint8_t *o = out;
|
||||||
|
size_t iy, ix;
|
||||||
|
|
||||||
|
w = w * 3;
|
||||||
|
|
||||||
|
for(iy=t; iy<b; iy+=jw) {
|
||||||
|
o = out+iy+l;
|
||||||
|
for(ix=0; ix<w; ix+= 3) {
|
||||||
|
o[ix] = data[ix+2];
|
||||||
|
o[ix+1] = data[ix+1];
|
||||||
|
o[ix+2] = data[ix];
|
||||||
|
}
|
||||||
|
data+=w;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//input buffer
|
||||||
|
static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
|
||||||
|
if(buf) {
|
||||||
|
memcpy(buf, jpeg->input + index, len);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale)
|
||||||
|
{
|
||||||
|
rgb_jpg_decoder jpeg;
|
||||||
|
jpeg.width = 0;
|
||||||
|
jpeg.height = 0;
|
||||||
|
jpeg.input = src;
|
||||||
|
jpeg.output = out;
|
||||||
|
jpeg.data_offset = 0;
|
||||||
|
|
||||||
|
if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len)
|
||||||
|
{
|
||||||
|
|
||||||
|
rgb_jpg_decoder jpeg;
|
||||||
|
jpeg.width = 0;
|
||||||
|
jpeg.height = 0;
|
||||||
|
jpeg.input = src;
|
||||||
|
jpeg.output = NULL;
|
||||||
|
jpeg.data_offset = BMP_HEADER_LEN;
|
||||||
|
|
||||||
|
if(esp_jpg_decode(src_len, JPG_SCALE_NONE, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t output_size = jpeg.width*jpeg.height*3;
|
||||||
|
|
||||||
|
jpeg.output[0] = 'B';
|
||||||
|
jpeg.output[1] = 'M';
|
||||||
|
bmp_header_t * bitmap = (bmp_header_t*)&jpeg.output[2];
|
||||||
|
bitmap->reserved = 0;
|
||||||
|
bitmap->filesize = output_size+BMP_HEADER_LEN;
|
||||||
|
bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN;
|
||||||
|
bitmap->dibheadersize = 40;
|
||||||
|
bitmap->width = jpeg.width;
|
||||||
|
bitmap->height = -jpeg.height;//set negative for top to bottom
|
||||||
|
bitmap->planes = 1;
|
||||||
|
bitmap->bitsperpixel = 24;
|
||||||
|
bitmap->compression = 0;
|
||||||
|
bitmap->imagesize = output_size;
|
||||||
|
bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI
|
||||||
|
bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI
|
||||||
|
bitmap->numcolorspallette = 0;
|
||||||
|
bitmap->mostimpcolor = 0;
|
||||||
|
|
||||||
|
*out = jpeg.output;
|
||||||
|
*out_len = output_size+BMP_HEADER_LEN;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf)
|
||||||
|
{
|
||||||
|
int pix_count = 0;
|
||||||
|
if(format == PIXFORMAT_JPEG) {
|
||||||
|
return jpg2rgb888(src_buf, src_len, rgb_buf, JPG_SCALE_NONE);
|
||||||
|
} else if(format == PIXFORMAT_RGB888) {
|
||||||
|
memcpy(rgb_buf, src_buf, src_len);
|
||||||
|
} else if(format == PIXFORMAT_RGB565) {
|
||||||
|
int i;
|
||||||
|
uint8_t hb, lb;
|
||||||
|
pix_count = src_len / 2;
|
||||||
|
for(i=0; i<pix_count; i++) {
|
||||||
|
hb = *src_buf++;
|
||||||
|
lb = *src_buf++;
|
||||||
|
*rgb_buf++ = (lb & 0x1F) << 3;
|
||||||
|
*rgb_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
|
||||||
|
*rgb_buf++ = hb & 0xF8;
|
||||||
|
}
|
||||||
|
} else if(format == PIXFORMAT_GRAYSCALE) {
|
||||||
|
int i;
|
||||||
|
uint8_t b;
|
||||||
|
pix_count = src_len;
|
||||||
|
for(i=0; i<pix_count; i++) {
|
||||||
|
b = *src_buf++;
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
}
|
||||||
|
} else if(format == PIXFORMAT_YUV422) {
|
||||||
|
pix_count = src_len / 2;
|
||||||
|
int i, maxi = pix_count / 2;
|
||||||
|
uint8_t y0, y1, u, v;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
for(i=0; i<maxi; i++) {
|
||||||
|
y0 = *src_buf++;
|
||||||
|
u = *src_buf++;
|
||||||
|
y1 = *src_buf++;
|
||||||
|
v = *src_buf++;
|
||||||
|
|
||||||
|
yuv2rgb(y0, u, v, &r, &g, &b);
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
*rgb_buf++ = g;
|
||||||
|
*rgb_buf++ = r;
|
||||||
|
|
||||||
|
yuv2rgb(y1, u, v, &r, &g, &b);
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
*rgb_buf++ = g;
|
||||||
|
*rgb_buf++ = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t ** out, size_t * out_len)
|
||||||
|
{
|
||||||
|
if(format == PIXFORMAT_JPEG) {
|
||||||
|
return jpg2bmp(src, src_len, out, out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = NULL;
|
||||||
|
*out_len = 0;
|
||||||
|
|
||||||
|
int pix_count = width*height;
|
||||||
|
size_t out_size = (pix_count * 3) + BMP_HEADER_LEN;
|
||||||
|
uint8_t * out_buf = (uint8_t *)_malloc(out_size);
|
||||||
|
if(!out_buf) {
|
||||||
|
ESP_LOGE(TAG, "_malloc failed! %u", out_size);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_buf[0] = 'B';
|
||||||
|
out_buf[1] = 'M';
|
||||||
|
bmp_header_t * bitmap = (bmp_header_t*)&out_buf[2];
|
||||||
|
bitmap->reserved = 0;
|
||||||
|
bitmap->filesize = out_size;
|
||||||
|
bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN;
|
||||||
|
bitmap->dibheadersize = 40;
|
||||||
|
bitmap->width = width;
|
||||||
|
bitmap->height = -height;//set negative for top to bottom
|
||||||
|
bitmap->planes = 1;
|
||||||
|
bitmap->bitsperpixel = 24;
|
||||||
|
bitmap->compression = 0;
|
||||||
|
bitmap->imagesize = pix_count * 3;
|
||||||
|
bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI
|
||||||
|
bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI
|
||||||
|
bitmap->numcolorspallette = 0;
|
||||||
|
bitmap->mostimpcolor = 0;
|
||||||
|
|
||||||
|
uint8_t * rgb_buf = out_buf + BMP_HEADER_LEN;
|
||||||
|
uint8_t * src_buf = src;
|
||||||
|
|
||||||
|
|
||||||
|
//convert data to RGB888
|
||||||
|
if(format == PIXFORMAT_RGB888) {
|
||||||
|
memcpy(rgb_buf, src_buf, pix_count*3);
|
||||||
|
} else if(format == PIXFORMAT_RGB565) {
|
||||||
|
int i;
|
||||||
|
uint8_t hb, lb;
|
||||||
|
for(i=0; i<pix_count; i++) {
|
||||||
|
hb = *src_buf++;
|
||||||
|
lb = *src_buf++;
|
||||||
|
*rgb_buf++ = (lb & 0x1F) << 3;
|
||||||
|
*rgb_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
|
||||||
|
*rgb_buf++ = hb & 0xF8;
|
||||||
|
}
|
||||||
|
} else if(format == PIXFORMAT_GRAYSCALE) {
|
||||||
|
int i;
|
||||||
|
uint8_t b;
|
||||||
|
for(i=0; i<pix_count; i++) {
|
||||||
|
b = *src_buf++;
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
}
|
||||||
|
} else if(format == PIXFORMAT_YUV422) {
|
||||||
|
int i, maxi = pix_count / 2;
|
||||||
|
uint8_t y0, y1, u, v;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
for(i=0; i<maxi; i++) {
|
||||||
|
y0 = *src_buf++;
|
||||||
|
u = *src_buf++;
|
||||||
|
y1 = *src_buf++;
|
||||||
|
v = *src_buf++;
|
||||||
|
|
||||||
|
yuv2rgb(y0, u, v, &r, &g, &b);
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
*rgb_buf++ = g;
|
||||||
|
*rgb_buf++ = r;
|
||||||
|
|
||||||
|
yuv2rgb(y1, u, v, &r, &g, &b);
|
||||||
|
*rgb_buf++ = b;
|
||||||
|
*rgb_buf++ = g;
|
||||||
|
*rgb_buf++ = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out = out_buf;
|
||||||
|
*out_len = out_size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len)
|
||||||
|
{
|
||||||
|
return fmt2bmp(fb->buf, fb->len, fb->width, fb->height, fb->format, out, out_len);
|
||||||
|
}
|
||||||
242
code/lib/conversions/to_jpg.cpp
Normal file
242
code/lib/conversions/to_jpg.cpp
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include <esp_camera.h>
|
||||||
|
#include "img_converters.h"
|
||||||
|
#include "jpge.h"
|
||||||
|
#include "yuv.h"
|
||||||
|
|
||||||
|
#include "esp_system.h"
|
||||||
|
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||||
|
#include "esp32/spiram.h"
|
||||||
|
#else
|
||||||
|
#error Target CONFIG_IDF_TARGET is not supported
|
||||||
|
#endif
|
||||||
|
#else // ESP32 Before IDF 4.0
|
||||||
|
#include "esp_spiram.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#define TAG ""
|
||||||
|
#else
|
||||||
|
#include "esp_log.h"
|
||||||
|
static const char* TAG = "to_jpg";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void *_malloc(size_t size)
|
||||||
|
{
|
||||||
|
void * res = malloc(size);
|
||||||
|
if(res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static IRAM_ATTR void convert_line_format(uint8_t * src, pixformat_t format, uint8_t * dst, size_t width, size_t in_channels, size_t line)
|
||||||
|
{
|
||||||
|
int i=0, o=0, l=0;
|
||||||
|
if(format == PIXFORMAT_GRAYSCALE) {
|
||||||
|
memcpy(dst, src + line * width, width);
|
||||||
|
} else if(format == PIXFORMAT_RGB888) {
|
||||||
|
l = width * 3;
|
||||||
|
src += l * line;
|
||||||
|
for(i=0; i<l; i+=3) {
|
||||||
|
dst[o++] = src[i+2];
|
||||||
|
dst[o++] = src[i+1];
|
||||||
|
dst[o++] = src[i];
|
||||||
|
}
|
||||||
|
} else if(format == PIXFORMAT_RGB565) {
|
||||||
|
l = width * 2;
|
||||||
|
src += l * line;
|
||||||
|
for(i=0; i<l; i+=2) {
|
||||||
|
dst[o++] = src[i] & 0xF8;
|
||||||
|
dst[o++] = (src[i] & 0x07) << 5 | (src[i+1] & 0xE0) >> 3;
|
||||||
|
dst[o++] = (src[i+1] & 0x1F) << 3;
|
||||||
|
}
|
||||||
|
} else if(format == PIXFORMAT_YUV422) {
|
||||||
|
uint8_t y0, y1, u, v;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
l = width * 2;
|
||||||
|
src += l * line;
|
||||||
|
for(i=0; i<l; i+=4) {
|
||||||
|
y0 = src[i];
|
||||||
|
u = src[i+1];
|
||||||
|
y1 = src[i+2];
|
||||||
|
v = src[i+3];
|
||||||
|
|
||||||
|
yuv2rgb(y0, u, v, &r, &g, &b);
|
||||||
|
dst[o++] = r;
|
||||||
|
dst[o++] = g;
|
||||||
|
dst[o++] = b;
|
||||||
|
|
||||||
|
yuv2rgb(y1, u, v, &r, &g, &b);
|
||||||
|
dst[o++] = r;
|
||||||
|
dst[o++] = g;
|
||||||
|
dst[o++] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert_image(uint8_t *src, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpge::output_stream *dst_stream)
|
||||||
|
{
|
||||||
|
int num_channels = 3;
|
||||||
|
jpge::subsampling_t subsampling = jpge::H2V2;
|
||||||
|
|
||||||
|
if(format == PIXFORMAT_GRAYSCALE) {
|
||||||
|
num_channels = 1;
|
||||||
|
subsampling = jpge::Y_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!quality) {
|
||||||
|
quality = 1;
|
||||||
|
} else if(quality > 100) {
|
||||||
|
quality = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
jpge::params comp_params = jpge::params();
|
||||||
|
comp_params.m_subsampling = subsampling;
|
||||||
|
comp_params.m_quality = quality;
|
||||||
|
|
||||||
|
jpge::jpeg_encoder dst_image;
|
||||||
|
|
||||||
|
if (!dst_image.init(dst_stream, width, height, num_channels, comp_params)) {
|
||||||
|
ESP_LOGE(TAG, "JPG encoder init failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* line = (uint8_t*)_malloc(width * num_channels);
|
||||||
|
if(!line) {
|
||||||
|
ESP_LOGE(TAG, "Scan line malloc failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
convert_line_format(src, format, line, width, num_channels, i);
|
||||||
|
if (!dst_image.process_scanline(line)) {
|
||||||
|
ESP_LOGE(TAG, "JPG process line %u failed", i);
|
||||||
|
free(line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
if (!dst_image.process_scanline(NULL)) {
|
||||||
|
ESP_LOGE(TAG, "JPG image finish failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dst_image.deinit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class callback_stream : public jpge::output_stream {
|
||||||
|
protected:
|
||||||
|
jpg_out_cb ocb;
|
||||||
|
void * oarg;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
callback_stream(jpg_out_cb cb, void * arg) : ocb(cb), oarg(arg), index(0) { }
|
||||||
|
virtual ~callback_stream() { }
|
||||||
|
virtual bool put_buf(const void* data, int len)
|
||||||
|
{
|
||||||
|
index += ocb(oarg, index, data, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual size_t get_size() const
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool fmt2jpg_cb(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpg_out_cb cb, void * arg)
|
||||||
|
{
|
||||||
|
callback_stream dst_stream(cb, arg);
|
||||||
|
return convert_image(src, width, height, format, quality, &dst_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg)
|
||||||
|
{
|
||||||
|
return fmt2jpg_cb(fb->buf, fb->len, fb->width, fb->height, fb->format, quality, cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class memory_stream : public jpge::output_stream {
|
||||||
|
protected:
|
||||||
|
uint8_t *out_buf;
|
||||||
|
size_t max_len, index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
memory_stream(void *pBuf, uint buf_size) : out_buf(static_cast<uint8_t*>(pBuf)), max_len(buf_size), index(0) { }
|
||||||
|
|
||||||
|
virtual ~memory_stream() { }
|
||||||
|
|
||||||
|
virtual bool put_buf(const void* pBuf, int len)
|
||||||
|
{
|
||||||
|
if (!pBuf) {
|
||||||
|
//end of image
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((size_t)len > (max_len - index)) {
|
||||||
|
ESP_LOGW(TAG, "JPG output overflow: %d bytes", len - (max_len - index));
|
||||||
|
len = max_len - index;
|
||||||
|
}
|
||||||
|
if (len) {
|
||||||
|
memcpy(out_buf + index, pBuf, len);
|
||||||
|
index += len;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t get_size() const
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, uint8_t ** out, size_t * out_len)
|
||||||
|
{
|
||||||
|
//todo: allocate proper buffer for holding JPEG data
|
||||||
|
//this should be enough for CIF frame size
|
||||||
|
// int jpg_buf_len = 64*1024;
|
||||||
|
int jpg_buf_len = 256*1024; // Anpassung wg. zu kleiner Bitmaps
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t * jpg_buf = (uint8_t *)_malloc(jpg_buf_len);
|
||||||
|
if(jpg_buf == NULL) {
|
||||||
|
ESP_LOGE(TAG, "JPG buffer malloc failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memory_stream dst_stream(jpg_buf, jpg_buf_len);
|
||||||
|
|
||||||
|
if(!convert_image(src, width, height, format, quality, &dst_stream)) {
|
||||||
|
free(jpg_buf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = jpg_buf;
|
||||||
|
*out_len = dst_stream.get_size();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool frame2jpg(camera_fb_t * fb, uint8_t quality, uint8_t ** out, size_t * out_len)
|
||||||
|
{
|
||||||
|
return fmt2jpg(fb->buf, fb->len, fb->width, fb->height, fb->format, quality, out, out_len);
|
||||||
|
}
|
||||||
298
code/lib/conversions/yuv.c
Normal file
298
code/lib/conversions/yuv.c
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "yuv.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int16_t vY;
|
||||||
|
int16_t vVr;
|
||||||
|
int16_t vVg;
|
||||||
|
int16_t vUg;
|
||||||
|
int16_t vUb;
|
||||||
|
} yuv_table_row;
|
||||||
|
|
||||||
|
static const yuv_table_row yuv_table[256] = {
|
||||||
|
// Y Vr Vg Ug Ub // #
|
||||||
|
{ -18, -204, 50, 104, -258 }, // 0
|
||||||
|
{ -17, -202, 49, 103, -256 }, // 1
|
||||||
|
{ -16, -201, 49, 102, -254 }, // 2
|
||||||
|
{ -15, -199, 48, 101, -252 }, // 3
|
||||||
|
{ -13, -197, 48, 100, -250 }, // 4
|
||||||
|
{ -12, -196, 48, 99, -248 }, // 5
|
||||||
|
{ -11, -194, 47, 99, -246 }, // 6
|
||||||
|
{ -10, -193, 47, 98, -244 }, // 7
|
||||||
|
{ -9, -191, 46, 97, -242 }, // 8
|
||||||
|
{ -8, -189, 46, 96, -240 }, // 9
|
||||||
|
{ -6, -188, 46, 95, -238 }, // 10
|
||||||
|
{ -5, -186, 45, 95, -236 }, // 11
|
||||||
|
{ -4, -185, 45, 94, -234 }, // 12
|
||||||
|
{ -3, -183, 44, 93, -232 }, // 13
|
||||||
|
{ -2, -181, 44, 92, -230 }, // 14
|
||||||
|
{ -1, -180, 44, 91, -228 }, // 15
|
||||||
|
{ 0, -178, 43, 91, -226 }, // 16
|
||||||
|
{ 1, -177, 43, 90, -223 }, // 17
|
||||||
|
{ 2, -175, 43, 89, -221 }, // 18
|
||||||
|
{ 3, -173, 42, 88, -219 }, // 19
|
||||||
|
{ 4, -172, 42, 87, -217 }, // 20
|
||||||
|
{ 5, -170, 41, 86, -215 }, // 21
|
||||||
|
{ 6, -169, 41, 86, -213 }, // 22
|
||||||
|
{ 8, -167, 41, 85, -211 }, // 23
|
||||||
|
{ 9, -165, 40, 84, -209 }, // 24
|
||||||
|
{ 10, -164, 40, 83, -207 }, // 25
|
||||||
|
{ 11, -162, 39, 82, -205 }, // 26
|
||||||
|
{ 12, -161, 39, 82, -203 }, // 27
|
||||||
|
{ 13, -159, 39, 81, -201 }, // 28
|
||||||
|
{ 15, -158, 38, 80, -199 }, // 29
|
||||||
|
{ 16, -156, 38, 79, -197 }, // 30
|
||||||
|
{ 17, -154, 37, 78, -195 }, // 31
|
||||||
|
{ 18, -153, 37, 78, -193 }, // 32
|
||||||
|
{ 19, -151, 37, 77, -191 }, // 33
|
||||||
|
{ 20, -150, 36, 76, -189 }, // 34
|
||||||
|
{ 22, -148, 36, 75, -187 }, // 35
|
||||||
|
{ 23, -146, 35, 74, -185 }, // 36
|
||||||
|
{ 24, -145, 35, 73, -183 }, // 37
|
||||||
|
{ 25, -143, 35, 73, -181 }, // 38
|
||||||
|
{ 26, -142, 34, 72, -179 }, // 39
|
||||||
|
{ 27, -140, 34, 71, -177 }, // 40
|
||||||
|
{ 29, -138, 34, 70, -175 }, // 41
|
||||||
|
{ 30, -137, 33, 69, -173 }, // 42
|
||||||
|
{ 31, -135, 33, 69, -171 }, // 43
|
||||||
|
{ 32, -134, 32, 68, -169 }, // 44
|
||||||
|
{ 33, -132, 32, 67, -167 }, // 45
|
||||||
|
{ 34, -130, 32, 66, -165 }, // 46
|
||||||
|
{ 36, -129, 31, 65, -163 }, // 47
|
||||||
|
{ 37, -127, 31, 65, -161 }, // 48
|
||||||
|
{ 38, -126, 30, 64, -159 }, // 49
|
||||||
|
{ 39, -124, 30, 63, -157 }, // 50
|
||||||
|
{ 40, -122, 30, 62, -155 }, // 51
|
||||||
|
{ 41, -121, 29, 61, -153 }, // 52
|
||||||
|
{ 43, -119, 29, 60, -151 }, // 53
|
||||||
|
{ 44, -118, 28, 60, -149 }, // 54
|
||||||
|
{ 45, -116, 28, 59, -147 }, // 55
|
||||||
|
{ 46, -114, 28, 58, -145 }, // 56
|
||||||
|
{ 47, -113, 27, 57, -143 }, // 57
|
||||||
|
{ 48, -111, 27, 56, -141 }, // 58
|
||||||
|
{ 50, -110, 26, 56, -139 }, // 59
|
||||||
|
{ 51, -108, 26, 55, -137 }, // 60
|
||||||
|
{ 52, -106, 26, 54, -135 }, // 61
|
||||||
|
{ 53, -105, 25, 53, -133 }, // 62
|
||||||
|
{ 54, -103, 25, 52, -131 }, // 63
|
||||||
|
{ 55, -102, 25, 52, -129 }, // 64
|
||||||
|
{ 57, -100, 24, 51, -127 }, // 65
|
||||||
|
{ 58, -98, 24, 50, -125 }, // 66
|
||||||
|
{ 59, -97, 23, 49, -123 }, // 67
|
||||||
|
{ 60, -95, 23, 48, -121 }, // 68
|
||||||
|
{ 61, -94, 23, 47, -119 }, // 69
|
||||||
|
{ 62, -92, 22, 47, -117 }, // 70
|
||||||
|
{ 64, -90, 22, 46, -115 }, // 71
|
||||||
|
{ 65, -89, 21, 45, -113 }, // 72
|
||||||
|
{ 66, -87, 21, 44, -110 }, // 73
|
||||||
|
{ 67, -86, 21, 43, -108 }, // 74
|
||||||
|
{ 68, -84, 20, 43, -106 }, // 75
|
||||||
|
{ 69, -82, 20, 42, -104 }, // 76
|
||||||
|
{ 71, -81, 19, 41, -102 }, // 77
|
||||||
|
{ 72, -79, 19, 40, -100 }, // 78
|
||||||
|
{ 73, -78, 19, 39, -98 }, // 79
|
||||||
|
{ 74, -76, 18, 39, -96 }, // 80
|
||||||
|
{ 75, -75, 18, 38, -94 }, // 81
|
||||||
|
{ 76, -73, 17, 37, -92 }, // 82
|
||||||
|
{ 77, -71, 17, 36, -90 }, // 83
|
||||||
|
{ 79, -70, 17, 35, -88 }, // 84
|
||||||
|
{ 80, -68, 16, 34, -86 }, // 85
|
||||||
|
{ 81, -67, 16, 34, -84 }, // 86
|
||||||
|
{ 82, -65, 16, 33, -82 }, // 87
|
||||||
|
{ 83, -63, 15, 32, -80 }, // 88
|
||||||
|
{ 84, -62, 15, 31, -78 }, // 89
|
||||||
|
{ 86, -60, 14, 30, -76 }, // 90
|
||||||
|
{ 87, -59, 14, 30, -74 }, // 91
|
||||||
|
{ 88, -57, 14, 29, -72 }, // 92
|
||||||
|
{ 89, -55, 13, 28, -70 }, // 93
|
||||||
|
{ 90, -54, 13, 27, -68 }, // 94
|
||||||
|
{ 91, -52, 12, 26, -66 }, // 95
|
||||||
|
{ 93, -51, 12, 26, -64 }, // 96
|
||||||
|
{ 94, -49, 12, 25, -62 }, // 97
|
||||||
|
{ 95, -47, 11, 24, -60 }, // 98
|
||||||
|
{ 96, -46, 11, 23, -58 }, // 99
|
||||||
|
{ 97, -44, 10, 22, -56 }, // 100
|
||||||
|
{ 98, -43, 10, 21, -54 }, // 101
|
||||||
|
{ 100, -41, 10, 21, -52 }, // 102
|
||||||
|
{ 101, -39, 9, 20, -50 }, // 103
|
||||||
|
{ 102, -38, 9, 19, -48 }, // 104
|
||||||
|
{ 103, -36, 8, 18, -46 }, // 105
|
||||||
|
{ 104, -35, 8, 17, -44 }, // 106
|
||||||
|
{ 105, -33, 8, 17, -42 }, // 107
|
||||||
|
{ 107, -31, 7, 16, -40 }, // 108
|
||||||
|
{ 108, -30, 7, 15, -38 }, // 109
|
||||||
|
{ 109, -28, 7, 14, -36 }, // 110
|
||||||
|
{ 110, -27, 6, 13, -34 }, // 111
|
||||||
|
{ 111, -25, 6, 13, -32 }, // 112
|
||||||
|
{ 112, -23, 5, 12, -30 }, // 113
|
||||||
|
{ 114, -22, 5, 11, -28 }, // 114
|
||||||
|
{ 115, -20, 5, 10, -26 }, // 115
|
||||||
|
{ 116, -19, 4, 9, -24 }, // 116
|
||||||
|
{ 117, -17, 4, 8, -22 }, // 117
|
||||||
|
{ 118, -15, 3, 8, -20 }, // 118
|
||||||
|
{ 119, -14, 3, 7, -18 }, // 119
|
||||||
|
{ 121, -12, 3, 6, -16 }, // 120
|
||||||
|
{ 122, -11, 2, 5, -14 }, // 121
|
||||||
|
{ 123, -9, 2, 4, -12 }, // 122
|
||||||
|
{ 124, -7, 1, 4, -10 }, // 123
|
||||||
|
{ 125, -6, 1, 3, -8 }, // 124
|
||||||
|
{ 126, -4, 1, 2, -6 }, // 125
|
||||||
|
{ 128, -3, 0, 1, -4 }, // 126
|
||||||
|
{ 129, -1, 0, 0, -2 }, // 127
|
||||||
|
{ 130, 0, 0, 0, 0 }, // 128
|
||||||
|
{ 131, 1, 0, 0, 2 }, // 129
|
||||||
|
{ 132, 3, 0, -1, 4 }, // 130
|
||||||
|
{ 133, 4, -1, -2, 6 }, // 131
|
||||||
|
{ 135, 6, -1, -3, 8 }, // 132
|
||||||
|
{ 136, 7, -1, -4, 10 }, // 133
|
||||||
|
{ 137, 9, -2, -4, 12 }, // 134
|
||||||
|
{ 138, 11, -2, -5, 14 }, // 135
|
||||||
|
{ 139, 12, -3, -6, 16 }, // 136
|
||||||
|
{ 140, 14, -3, -7, 18 }, // 137
|
||||||
|
{ 142, 15, -3, -8, 20 }, // 138
|
||||||
|
{ 143, 17, -4, -8, 22 }, // 139
|
||||||
|
{ 144, 19, -4, -9, 24 }, // 140
|
||||||
|
{ 145, 20, -5, -10, 26 }, // 141
|
||||||
|
{ 146, 22, -5, -11, 28 }, // 142
|
||||||
|
{ 147, 23, -5, -12, 30 }, // 143
|
||||||
|
{ 148, 25, -6, -13, 32 }, // 144
|
||||||
|
{ 150, 27, -6, -13, 34 }, // 145
|
||||||
|
{ 151, 28, -7, -14, 36 }, // 146
|
||||||
|
{ 152, 30, -7, -15, 38 }, // 147
|
||||||
|
{ 153, 31, -7, -16, 40 }, // 148
|
||||||
|
{ 154, 33, -8, -17, 42 }, // 149
|
||||||
|
{ 155, 35, -8, -17, 44 }, // 150
|
||||||
|
{ 157, 36, -8, -18, 46 }, // 151
|
||||||
|
{ 158, 38, -9, -19, 48 }, // 152
|
||||||
|
{ 159, 39, -9, -20, 50 }, // 153
|
||||||
|
{ 160, 41, -10, -21, 52 }, // 154
|
||||||
|
{ 161, 43, -10, -21, 54 }, // 155
|
||||||
|
{ 162, 44, -10, -22, 56 }, // 156
|
||||||
|
{ 164, 46, -11, -23, 58 }, // 157
|
||||||
|
{ 165, 47, -11, -24, 60 }, // 158
|
||||||
|
{ 166, 49, -12, -25, 62 }, // 159
|
||||||
|
{ 167, 51, -12, -26, 64 }, // 160
|
||||||
|
{ 168, 52, -12, -26, 66 }, // 161
|
||||||
|
{ 169, 54, -13, -27, 68 }, // 162
|
||||||
|
{ 171, 55, -13, -28, 70 }, // 163
|
||||||
|
{ 172, 57, -14, -29, 72 }, // 164
|
||||||
|
{ 173, 59, -14, -30, 74 }, // 165
|
||||||
|
{ 174, 60, -14, -30, 76 }, // 166
|
||||||
|
{ 175, 62, -15, -31, 78 }, // 167
|
||||||
|
{ 176, 63, -15, -32, 80 }, // 168
|
||||||
|
{ 178, 65, -16, -33, 82 }, // 169
|
||||||
|
{ 179, 67, -16, -34, 84 }, // 170
|
||||||
|
{ 180, 68, -16, -34, 86 }, // 171
|
||||||
|
{ 181, 70, -17, -35, 88 }, // 172
|
||||||
|
{ 182, 71, -17, -36, 90 }, // 173
|
||||||
|
{ 183, 73, -17, -37, 92 }, // 174
|
||||||
|
{ 185, 75, -18, -38, 94 }, // 175
|
||||||
|
{ 186, 76, -18, -39, 96 }, // 176
|
||||||
|
{ 187, 78, -19, -39, 98 }, // 177
|
||||||
|
{ 188, 79, -19, -40, 100 }, // 178
|
||||||
|
{ 189, 81, -19, -41, 102 }, // 179
|
||||||
|
{ 190, 82, -20, -42, 104 }, // 180
|
||||||
|
{ 192, 84, -20, -43, 106 }, // 181
|
||||||
|
{ 193, 86, -21, -43, 108 }, // 182
|
||||||
|
{ 194, 87, -21, -44, 110 }, // 183
|
||||||
|
{ 195, 89, -21, -45, 113 }, // 184
|
||||||
|
{ 196, 90, -22, -46, 115 }, // 185
|
||||||
|
{ 197, 92, -22, -47, 117 }, // 186
|
||||||
|
{ 199, 94, -23, -47, 119 }, // 187
|
||||||
|
{ 200, 95, -23, -48, 121 }, // 188
|
||||||
|
{ 201, 97, -23, -49, 123 }, // 189
|
||||||
|
{ 202, 98, -24, -50, 125 }, // 190
|
||||||
|
{ 203, 100, -24, -51, 127 }, // 191
|
||||||
|
{ 204, 102, -25, -52, 129 }, // 192
|
||||||
|
{ 206, 103, -25, -52, 131 }, // 193
|
||||||
|
{ 207, 105, -25, -53, 133 }, // 194
|
||||||
|
{ 208, 106, -26, -54, 135 }, // 195
|
||||||
|
{ 209, 108, -26, -55, 137 }, // 196
|
||||||
|
{ 210, 110, -26, -56, 139 }, // 197
|
||||||
|
{ 211, 111, -27, -56, 141 }, // 198
|
||||||
|
{ 213, 113, -27, -57, 143 }, // 199
|
||||||
|
{ 214, 114, -28, -58, 145 }, // 200
|
||||||
|
{ 215, 116, -28, -59, 147 }, // 201
|
||||||
|
{ 216, 118, -28, -60, 149 }, // 202
|
||||||
|
{ 217, 119, -29, -60, 151 }, // 203
|
||||||
|
{ 218, 121, -29, -61, 153 }, // 204
|
||||||
|
{ 219, 122, -30, -62, 155 }, // 205
|
||||||
|
{ 221, 124, -30, -63, 157 }, // 206
|
||||||
|
{ 222, 126, -30, -64, 159 }, // 207
|
||||||
|
{ 223, 127, -31, -65, 161 }, // 208
|
||||||
|
{ 224, 129, -31, -65, 163 }, // 209
|
||||||
|
{ 225, 130, -32, -66, 165 }, // 210
|
||||||
|
{ 226, 132, -32, -67, 167 }, // 211
|
||||||
|
{ 228, 134, -32, -68, 169 }, // 212
|
||||||
|
{ 229, 135, -33, -69, 171 }, // 213
|
||||||
|
{ 230, 137, -33, -69, 173 }, // 214
|
||||||
|
{ 231, 138, -34, -70, 175 }, // 215
|
||||||
|
{ 232, 140, -34, -71, 177 }, // 216
|
||||||
|
{ 233, 142, -34, -72, 179 }, // 217
|
||||||
|
{ 235, 143, -35, -73, 181 }, // 218
|
||||||
|
{ 236, 145, -35, -73, 183 }, // 219
|
||||||
|
{ 237, 146, -35, -74, 185 }, // 220
|
||||||
|
{ 238, 148, -36, -75, 187 }, // 221
|
||||||
|
{ 239, 150, -36, -76, 189 }, // 222
|
||||||
|
{ 240, 151, -37, -77, 191 }, // 223
|
||||||
|
{ 242, 153, -37, -78, 193 }, // 224
|
||||||
|
{ 243, 154, -37, -78, 195 }, // 225
|
||||||
|
{ 244, 156, -38, -79, 197 }, // 226
|
||||||
|
{ 245, 158, -38, -80, 199 }, // 227
|
||||||
|
{ 246, 159, -39, -81, 201 }, // 228
|
||||||
|
{ 247, 161, -39, -82, 203 }, // 229
|
||||||
|
{ 249, 162, -39, -82, 205 }, // 230
|
||||||
|
{ 250, 164, -40, -83, 207 }, // 231
|
||||||
|
{ 251, 165, -40, -84, 209 }, // 232
|
||||||
|
{ 252, 167, -41, -85, 211 }, // 233
|
||||||
|
{ 253, 169, -41, -86, 213 }, // 234
|
||||||
|
{ 254, 170, -41, -86, 215 }, // 235
|
||||||
|
{ 256, 172, -42, -87, 217 }, // 236
|
||||||
|
{ 257, 173, -42, -88, 219 }, // 237
|
||||||
|
{ 258, 175, -43, -89, 221 }, // 238
|
||||||
|
{ 259, 177, -43, -90, 223 }, // 239
|
||||||
|
{ 260, 178, -43, -91, 226 }, // 240
|
||||||
|
{ 261, 180, -44, -91, 228 }, // 241
|
||||||
|
{ 263, 181, -44, -92, 230 }, // 242
|
||||||
|
{ 264, 183, -44, -93, 232 }, // 243
|
||||||
|
{ 265, 185, -45, -94, 234 }, // 244
|
||||||
|
{ 266, 186, -45, -95, 236 }, // 245
|
||||||
|
{ 267, 188, -46, -95, 238 }, // 246
|
||||||
|
{ 268, 189, -46, -96, 240 }, // 247
|
||||||
|
{ 270, 191, -46, -97, 242 }, // 248
|
||||||
|
{ 271, 193, -47, -98, 244 }, // 249
|
||||||
|
{ 272, 194, -47, -99, 246 }, // 250
|
||||||
|
{ 273, 196, -48, -99, 248 }, // 251
|
||||||
|
{ 274, 197, -48, -100, 250 }, // 252
|
||||||
|
{ 275, 199, -48, -101, 252 }, // 253
|
||||||
|
{ 277, 201, -49, -102, 254 }, // 254
|
||||||
|
{ 278, 202, -49, -103, 256 } // 255
|
||||||
|
};
|
||||||
|
|
||||||
|
#define YUYV_CONSTRAIN(v) ((v)<0)?0:(((v)>255)?255:(v))
|
||||||
|
|
||||||
|
void IRAM_ATTR yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b)
|
||||||
|
{
|
||||||
|
int16_t ri, gi, bi;
|
||||||
|
|
||||||
|
ri = yuv_table[y].vY + yuv_table[v].vVr;
|
||||||
|
gi = yuv_table[y].vY + yuv_table[u].vUg + yuv_table[v].vVg;
|
||||||
|
bi = yuv_table[y].vY + yuv_table[u].vUb;
|
||||||
|
|
||||||
|
*r = YUYV_CONSTRAIN(ri);
|
||||||
|
*g = YUYV_CONSTRAIN(gi);
|
||||||
|
*b = YUYV_CONSTRAIN(bi);
|
||||||
|
}
|
||||||
29
code/lib/conversions/yuv.h
Normal file
29
code/lib/conversions/yuv.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef _CONVERSIONS_YUV_H_
|
||||||
|
#define _CONVERSIONS_YUV_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _CONVERSIONS_YUV_H_ */
|
||||||
1520
code/lib/driver/camera.c
Normal file
1520
code/lib/driver/camera.c
Normal file
File diff suppressed because it is too large
Load Diff
49
code/lib/driver/camera_common.h
Normal file
49
code/lib/driver/camera_common.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_intr_alloc.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_camera.h"
|
||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
#include "esp_system.h"
|
||||||
|
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||||
|
#include "esp32/rom/lldesc.h"
|
||||||
|
#else
|
||||||
|
#error Target CONFIG_IDF_TARGET is not supported
|
||||||
|
#endif
|
||||||
|
#else // ESP32 Before IDF 4.0
|
||||||
|
#include "rom/lldesc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint8_t sample2;
|
||||||
|
uint8_t unused2;
|
||||||
|
uint8_t sample1;
|
||||||
|
uint8_t unused1;
|
||||||
|
};
|
||||||
|
uint32_t val;
|
||||||
|
} dma_elem_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||||
|
* fifo receives: 00 s1 00 s2, 00 s2 00 s3, 00 s3 00 s4, ...
|
||||||
|
*/
|
||||||
|
SM_0A0B_0B0C = 0,
|
||||||
|
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||||
|
* fifo receives: 00 s1 00 s2, 00 s3 00 s4, ...
|
||||||
|
*/
|
||||||
|
SM_0A0B_0C0D = 1,
|
||||||
|
/* camera sends byte sequence: s1, s2, s3, s4, ...
|
||||||
|
* fifo receives: 00 s1 00 00, 00 s2 00 00, 00 s3 00 00, ...
|
||||||
|
*/
|
||||||
|
SM_0A00_0B00 = 3,
|
||||||
|
} i2s_sampling_mode_t;
|
||||||
|
|
||||||
200
code/lib/driver/esp_camera.h
Normal file
200
code/lib/driver/esp_camera.h
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/*
|
||||||
|
* Example Use
|
||||||
|
*
|
||||||
|
static camera_config_t camera_example_config = {
|
||||||
|
.pin_pwdn = PIN_PWDN,
|
||||||
|
.pin_reset = PIN_RESET,
|
||||||
|
.pin_xclk = PIN_XCLK,
|
||||||
|
.pin_sscb_sda = PIN_SIOD,
|
||||||
|
.pin_sscb_scl = PIN_SIOC,
|
||||||
|
.pin_d7 = PIN_D7,
|
||||||
|
.pin_d6 = PIN_D6,
|
||||||
|
.pin_d5 = PIN_D5,
|
||||||
|
.pin_d4 = PIN_D4,
|
||||||
|
.pin_d3 = PIN_D3,
|
||||||
|
.pin_d2 = PIN_D2,
|
||||||
|
.pin_d1 = PIN_D1,
|
||||||
|
.pin_d0 = PIN_D0,
|
||||||
|
.pin_vsync = PIN_VSYNC,
|
||||||
|
.pin_href = PIN_HREF,
|
||||||
|
.pin_pclk = PIN_PCLK,
|
||||||
|
|
||||||
|
.xclk_freq_hz = 20000000,
|
||||||
|
.ledc_timer = LEDC_TIMER_0,
|
||||||
|
.ledc_channel = LEDC_CHANNEL_0,
|
||||||
|
.pixel_format = PIXFORMAT_JPEG,
|
||||||
|
.frame_size = FRAMESIZE_SVGA,
|
||||||
|
.jpeg_quality = 10,
|
||||||
|
.fb_count = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t camera_example_init(){
|
||||||
|
return esp_camera_init(&camera_example_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t camera_example_capture(){
|
||||||
|
//capture a frame
|
||||||
|
camera_fb_t * fb = esp_camera_fb_get();
|
||||||
|
if (!fb) {
|
||||||
|
ESP_LOGE(TAG, "Frame buffer could not be acquired");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//replace this with your own function
|
||||||
|
display_image(fb->width, fb->height, fb->pixformat, fb->buf, fb->len);
|
||||||
|
|
||||||
|
//return the frame buffer back to be reused
|
||||||
|
esp_camera_fb_return(fb);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef ESPCAMERADEF
|
||||||
|
#define ESPCAMERADEF
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
#include "sensor.h"
|
||||||
|
#include "sys/time.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration structure for camera initialization
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int pin_pwdn; /*!< GPIO pin for camera power down line */
|
||||||
|
int pin_reset; /*!< GPIO pin for camera reset line */
|
||||||
|
int pin_xclk; /*!< GPIO pin for camera XCLK line */
|
||||||
|
int pin_sscb_sda; /*!< GPIO pin for camera SDA line */
|
||||||
|
int pin_sscb_scl; /*!< GPIO pin for camera SCL line */
|
||||||
|
int pin_d7; /*!< GPIO pin for camera D7 line */
|
||||||
|
int pin_d6; /*!< GPIO pin for camera D6 line */
|
||||||
|
int pin_d5; /*!< GPIO pin for camera D5 line */
|
||||||
|
int pin_d4; /*!< GPIO pin for camera D4 line */
|
||||||
|
int pin_d3; /*!< GPIO pin for camera D3 line */
|
||||||
|
int pin_d2; /*!< GPIO pin for camera D2 line */
|
||||||
|
int pin_d1; /*!< GPIO pin for camera D1 line */
|
||||||
|
int pin_d0; /*!< GPIO pin for camera D0 line */
|
||||||
|
int pin_vsync; /*!< GPIO pin for camera VSYNC line */
|
||||||
|
int pin_href; /*!< GPIO pin for camera HREF line */
|
||||||
|
int pin_pclk; /*!< GPIO pin for camera PCLK line */
|
||||||
|
|
||||||
|
int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental) */
|
||||||
|
|
||||||
|
ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */
|
||||||
|
ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */
|
||||||
|
|
||||||
|
pixformat_t pixel_format; /*!< Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG */
|
||||||
|
framesize_t frame_size; /*!< Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA */
|
||||||
|
|
||||||
|
int jpeg_quality; /*!< Quality of JPEG output. 0-63 lower means higher quality */
|
||||||
|
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
|
||||||
|
} camera_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data structure of camera frame buffer
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t * buf; /*!< Pointer to the pixel data */
|
||||||
|
size_t len; /*!< Length of the buffer in bytes */
|
||||||
|
size_t width; /*!< Width of the buffer in pixels */
|
||||||
|
size_t height; /*!< Height of the buffer in pixels */
|
||||||
|
pixformat_t format; /*!< Format of the pixel data */
|
||||||
|
struct timeval timestamp; /*!< Timestamp since boot of the first DMA buffer of the frame */
|
||||||
|
} camera_fb_t;
|
||||||
|
|
||||||
|
#define ESP_ERR_CAMERA_BASE 0x20000
|
||||||
|
#define ESP_ERR_CAMERA_NOT_DETECTED (ESP_ERR_CAMERA_BASE + 1)
|
||||||
|
#define ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE (ESP_ERR_CAMERA_BASE + 2)
|
||||||
|
#define ESP_ERR_CAMERA_FAILED_TO_SET_OUT_FORMAT (ESP_ERR_CAMERA_BASE + 3)
|
||||||
|
#define ESP_ERR_CAMERA_NOT_SUPPORTED (ESP_ERR_CAMERA_BASE + 4)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the camera driver
|
||||||
|
*
|
||||||
|
* @note call camera_probe before calling this function
|
||||||
|
*
|
||||||
|
* This function detects and configures camera over I2C interface,
|
||||||
|
* allocates framebuffer and DMA buffers,
|
||||||
|
* initializes parallel I2S input, and sets up DMA descriptors.
|
||||||
|
*
|
||||||
|
* Currently this function can only be called once and there is
|
||||||
|
* no way to de-initialize this module.
|
||||||
|
*
|
||||||
|
* @param config Camera configuration parameters
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_camera_init(const camera_config_t* config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deinitialize the camera driver
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if the driver hasn't been initialized yet
|
||||||
|
*/
|
||||||
|
esp_err_t esp_camera_deinit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtain pointer to a frame buffer.
|
||||||
|
*
|
||||||
|
* @return pointer to the frame buffer
|
||||||
|
*/
|
||||||
|
camera_fb_t* esp_camera_fb_get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the frame buffer to be reused again.
|
||||||
|
*
|
||||||
|
* @param fb Pointer to the frame buffer
|
||||||
|
*/
|
||||||
|
void esp_camera_fb_return(camera_fb_t * fb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a pointer to the image sensor control structure
|
||||||
|
*
|
||||||
|
* @return pointer to the sensor
|
||||||
|
*/
|
||||||
|
sensor_t * esp_camera_sensor_get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save camera settings to non-volatile-storage (NVS)
|
||||||
|
*
|
||||||
|
* @param key A unique nvs key name for the camera settings
|
||||||
|
*/
|
||||||
|
esp_err_t esp_camera_save_to_nvs(const char *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load camera settings from non-volatile-storage (NVS)
|
||||||
|
*
|
||||||
|
* @param key A unique nvs key name for the camera settings
|
||||||
|
*/
|
||||||
|
esp_err_t esp_camera_load_from_nvs(const char *key);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "img_converters.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
260
code/lib/driver/sccb.c
Normal file
260
code/lib/driver/sccb.c
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* SCCB (I2C like) driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include "sccb.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#else
|
||||||
|
#include "esp_log.h"
|
||||||
|
static const char* TAG = "sccb";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#undef CONFIG_SCCB_HARDWARE_I2C
|
||||||
|
|
||||||
|
#define LITTLETOBIG(x) ((x<<8)|(x>>8))
|
||||||
|
|
||||||
|
#ifdef CONFIG_SCCB_HARDWARE_I2C
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
|
||||||
|
#define SCCB_FREQ 100000 /*!< I2C master frequency*/
|
||||||
|
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||||
|
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||||
|
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||||
|
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||||
|
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||||
|
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||||
|
#if CONFIG_SCCB_HARDWARE_I2C_PORT1
|
||||||
|
const int SCCB_I2C_PORT = 1;
|
||||||
|
#else
|
||||||
|
const int SCCB_I2C_PORT = 0;
|
||||||
|
#endif
|
||||||
|
static uint8_t ESP_SLAVE_ADDR = 0x3c;
|
||||||
|
#else
|
||||||
|
#include "twi.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int SCCB_Init(int pin_sda, int pin_scl)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "pin_sda %d pin_scl %d\n", pin_sda, pin_scl);
|
||||||
|
#ifdef CONFIG_SCCB_HARDWARE_I2C
|
||||||
|
//log_i("SCCB_Init start");
|
||||||
|
i2c_config_t conf;
|
||||||
|
conf.mode = I2C_MODE_MASTER;
|
||||||
|
conf.sda_io_num = pin_sda;
|
||||||
|
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||||
|
conf.scl_io_num = pin_scl;
|
||||||
|
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||||
|
conf.master.clk_speed = SCCB_FREQ;
|
||||||
|
|
||||||
|
i2c_param_config(SCCB_I2C_PORT, &conf);
|
||||||
|
i2c_driver_install(SCCB_I2C_PORT, conf.mode, 0, 0, 0);
|
||||||
|
#else
|
||||||
|
twi_init(pin_sda, pin_scl);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SCCB_Probe()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SCCB_HARDWARE_I2C
|
||||||
|
uint8_t slave_addr = 0x0;
|
||||||
|
while(slave_addr < 0x7f) {
|
||||||
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
if( ret == ESP_OK) {
|
||||||
|
ESP_SLAVE_ADDR = slave_addr;
|
||||||
|
return ESP_SLAVE_ADDR;
|
||||||
|
}
|
||||||
|
slave_addr++;
|
||||||
|
}
|
||||||
|
return ESP_SLAVE_ADDR;
|
||||||
|
#else
|
||||||
|
uint8_t reg = 0x00;
|
||||||
|
uint8_t slv_addr = 0x00;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "SCCB_Probe start");
|
||||||
|
for (uint8_t i = 0; i < 127; i++) {
|
||||||
|
if (twi_writeTo(i, ®, 1, true) == 0) {
|
||||||
|
slv_addr = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i!=126) {
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS); // Necessary for OV7725 camera (not for OV2640).
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slv_addr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SCCB_HARDWARE_I2C
|
||||||
|
uint8_t data=0;
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||||
|
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
if(ret != ESP_OK) return -1;
|
||||||
|
cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
|
||||||
|
i2c_master_read_byte(cmd, &data, NACK_VAL);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
if(ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
#else
|
||||||
|
uint8_t data=0;
|
||||||
|
|
||||||
|
int rc = twi_writeTo(slv_addr, ®, 1, true);
|
||||||
|
if (rc != 0) {
|
||||||
|
data = 0xff;
|
||||||
|
} else {
|
||||||
|
rc = twi_readFrom(slv_addr, &data, 1, true);
|
||||||
|
if (rc != 0) {
|
||||||
|
data=0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "SCCB_Read [%02x] failed rc=%d\n", reg, rc);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SCCB_HARDWARE_I2C
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||||
|
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
||||||
|
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
if(ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
|
||||||
|
}
|
||||||
|
return ret == ESP_OK ? 0 : -1;
|
||||||
|
#else
|
||||||
|
uint8_t ret=0;
|
||||||
|
uint8_t buf[] = {reg, data};
|
||||||
|
|
||||||
|
if(twi_writeTo(slv_addr, buf, 2, true) != 0) {
|
||||||
|
ret=0xFF;
|
||||||
|
}
|
||||||
|
if (ret != 0) {
|
||||||
|
ESP_LOGE(TAG, "SCCB_Write [%02x]=%02x failed\n", reg, data);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SCCB_HARDWARE_I2C
|
||||||
|
uint8_t data=0;
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||||
|
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||||
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||||
|
i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
|
||||||
|
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
if(ret != ESP_OK) return -1;
|
||||||
|
cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
|
||||||
|
i2c_master_read_byte(cmd, &data, NACK_VAL);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
if(ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
#else
|
||||||
|
uint8_t data=0;
|
||||||
|
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||||
|
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||||
|
uint8_t buf[] = {reg_u8[0], reg_u8[1]};
|
||||||
|
|
||||||
|
int rc = twi_writeTo(slv_addr, buf, 2, true);
|
||||||
|
if (rc != 0) {
|
||||||
|
data = 0xff;
|
||||||
|
} else {
|
||||||
|
rc = twi_readFrom(slv_addr, &data, 1, true);
|
||||||
|
if (rc != 0) {
|
||||||
|
data=0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "R [%04x] fail rc=%d\n", reg, rc);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data)
|
||||||
|
{
|
||||||
|
static uint16_t i = 0;
|
||||||
|
#ifdef CONFIG_SCCB_HARDWARE_I2C
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||||
|
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||||
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
|
i2c_master_start(cmd);
|
||||||
|
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||||
|
i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
|
||||||
|
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
|
||||||
|
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
|
||||||
|
i2c_master_stop(cmd);
|
||||||
|
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
|
||||||
|
i2c_cmd_link_delete(cmd);
|
||||||
|
if(ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "W [%04x]=%02x %d fail\n", reg, data, i++);
|
||||||
|
}
|
||||||
|
return ret == ESP_OK ? 0 : -1;
|
||||||
|
#else
|
||||||
|
uint8_t ret=0;
|
||||||
|
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||||
|
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||||
|
uint8_t buf[] = {reg_u8[0], reg_u8[1], data};
|
||||||
|
|
||||||
|
if(twi_writeTo(slv_addr, buf, 3, true) != 0) {
|
||||||
|
ret = 0xFF;
|
||||||
|
}
|
||||||
|
if (ret != 0) {
|
||||||
|
ESP_LOGE(TAG, "W [%04x]=%02x %d fail\n", reg, data, i++);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
18
code/lib/driver/sccb.h
Normal file
18
code/lib/driver/sccb.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* SCCB (I2C like) driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef __SCCB_H__
|
||||||
|
#define __SCCB_H__
|
||||||
|
#include <stdint.h>
|
||||||
|
int SCCB_Init(int pin_sda, int pin_scl);
|
||||||
|
uint8_t SCCB_Probe();
|
||||||
|
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg);
|
||||||
|
uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data);
|
||||||
|
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg);
|
||||||
|
uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data);
|
||||||
|
#endif // __SCCB_H__
|
||||||
28
code/lib/driver/sensor.c
Normal file
28
code/lib/driver/sensor.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||||
|
{ 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */
|
||||||
|
{ 160, 120, ASPECT_RATIO_4X3 }, /* QQVGA */
|
||||||
|
{ 176, 144, ASPECT_RATIO_5X4 }, /* QCIF */
|
||||||
|
{ 240, 176, ASPECT_RATIO_4X3 }, /* HQVGA */
|
||||||
|
{ 240, 240, ASPECT_RATIO_1X1 }, /* 240x240 */
|
||||||
|
{ 320, 240, ASPECT_RATIO_4X3 }, /* QVGA */
|
||||||
|
{ 400, 296, ASPECT_RATIO_4X3 }, /* CIF */
|
||||||
|
{ 480, 320, ASPECT_RATIO_3X2 }, /* HVGA */
|
||||||
|
{ 640, 480, ASPECT_RATIO_4X3 }, /* VGA */
|
||||||
|
{ 800, 600, ASPECT_RATIO_4X3 }, /* SVGA */
|
||||||
|
{ 1024, 768, ASPECT_RATIO_4X3 }, /* XGA */
|
||||||
|
{ 1280, 720, ASPECT_RATIO_16X9 }, /* HD */
|
||||||
|
{ 1280, 1024, ASPECT_RATIO_5X4 }, /* SXGA */
|
||||||
|
{ 1600, 1200, ASPECT_RATIO_4X3 }, /* UXGA */
|
||||||
|
// 3MP Sensors
|
||||||
|
{ 1920, 1080, ASPECT_RATIO_16X9 }, /* FHD */
|
||||||
|
{ 720, 1280, ASPECT_RATIO_9X16 }, /* Portrait HD */
|
||||||
|
{ 864, 1536, ASPECT_RATIO_9X16 }, /* Portrait 3MP */
|
||||||
|
{ 2048, 1536, ASPECT_RATIO_4X3 }, /* QXGA */
|
||||||
|
// 5MP Sensors
|
||||||
|
{ 2560, 1440, ASPECT_RATIO_16X9 }, /* QHD */
|
||||||
|
{ 2560, 1600, ASPECT_RATIO_16X10 }, /* WQXGA */
|
||||||
|
{ 1088, 1920, ASPECT_RATIO_9X16 }, /* Portrait FHD */
|
||||||
|
{ 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */
|
||||||
|
};
|
||||||
191
code/lib/driver/sensor.h
Normal file
191
code/lib/driver/sensor.h
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* Sensor abstraction layer.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef __SENSOR_H__
|
||||||
|
#define __SENSOR_H__
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define OV9650_PID (0x96)
|
||||||
|
#define OV7725_PID (0x77)
|
||||||
|
#define OV2640_PID (0x26)
|
||||||
|
#define OV3660_PID (0x36)
|
||||||
|
#define OV5640_PID (0x56)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||||
|
PIXFORMAT_YUV422, // 2BPP/YUV422
|
||||||
|
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
|
||||||
|
PIXFORMAT_JPEG, // JPEG/COMPRESSED
|
||||||
|
PIXFORMAT_RGB888, // 3BPP/RGB888
|
||||||
|
PIXFORMAT_RAW, // RAW
|
||||||
|
PIXFORMAT_RGB444, // 3BP2P/RGB444
|
||||||
|
PIXFORMAT_RGB555, // 3BP2P/RGB555
|
||||||
|
} pixformat_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FRAMESIZE_96X96, // 96x96
|
||||||
|
FRAMESIZE_QQVGA, // 160x120
|
||||||
|
FRAMESIZE_QCIF, // 176x144
|
||||||
|
FRAMESIZE_HQVGA, // 240x176
|
||||||
|
FRAMESIZE_240X240, // 240x240
|
||||||
|
FRAMESIZE_QVGA, // 320x240
|
||||||
|
FRAMESIZE_CIF, // 400x296
|
||||||
|
FRAMESIZE_HVGA, // 480x320
|
||||||
|
FRAMESIZE_VGA, // 640x480
|
||||||
|
FRAMESIZE_SVGA, // 800x600
|
||||||
|
FRAMESIZE_XGA, // 1024x768
|
||||||
|
FRAMESIZE_HD, // 1280x720
|
||||||
|
FRAMESIZE_SXGA, // 1280x1024
|
||||||
|
FRAMESIZE_UXGA, // 1600x1200
|
||||||
|
// 3MP Sensors
|
||||||
|
FRAMESIZE_FHD, // 1920x1080
|
||||||
|
FRAMESIZE_P_HD, // 720x1280
|
||||||
|
FRAMESIZE_P_3MP, // 864x1536
|
||||||
|
FRAMESIZE_QXGA, // 2048x1536
|
||||||
|
// 5MP Sensors
|
||||||
|
FRAMESIZE_QHD, // 2560x1440
|
||||||
|
FRAMESIZE_WQXGA, // 2560x1600
|
||||||
|
FRAMESIZE_P_FHD, // 1080x1920
|
||||||
|
FRAMESIZE_QSXGA, // 2560x1920
|
||||||
|
FRAMESIZE_INVALID
|
||||||
|
} framesize_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ASPECT_RATIO_4X3,
|
||||||
|
ASPECT_RATIO_3X2,
|
||||||
|
ASPECT_RATIO_16X10,
|
||||||
|
ASPECT_RATIO_5X3,
|
||||||
|
ASPECT_RATIO_16X9,
|
||||||
|
ASPECT_RATIO_21X9,
|
||||||
|
ASPECT_RATIO_5X4,
|
||||||
|
ASPECT_RATIO_1X1,
|
||||||
|
ASPECT_RATIO_9X16
|
||||||
|
} aspect_ratio_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GAINCEILING_2X,
|
||||||
|
GAINCEILING_4X,
|
||||||
|
GAINCEILING_8X,
|
||||||
|
GAINCEILING_16X,
|
||||||
|
GAINCEILING_32X,
|
||||||
|
GAINCEILING_64X,
|
||||||
|
GAINCEILING_128X,
|
||||||
|
} gainceiling_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t max_width;
|
||||||
|
uint16_t max_height;
|
||||||
|
uint16_t start_x;
|
||||||
|
uint16_t start_y;
|
||||||
|
uint16_t end_x;
|
||||||
|
uint16_t end_y;
|
||||||
|
uint16_t offset_x;
|
||||||
|
uint16_t offset_y;
|
||||||
|
uint16_t total_x;
|
||||||
|
uint16_t total_y;
|
||||||
|
} ratio_settings_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const uint16_t width;
|
||||||
|
const uint16_t height;
|
||||||
|
const aspect_ratio_t aspect_ratio;
|
||||||
|
} resolution_info_t;
|
||||||
|
|
||||||
|
// Resolution table (in sensor.c)
|
||||||
|
extern const resolution_info_t resolution[];
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t MIDH;
|
||||||
|
uint8_t MIDL;
|
||||||
|
uint8_t PID;
|
||||||
|
uint8_t VER;
|
||||||
|
} sensor_id_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
framesize_t framesize;//0 - 10
|
||||||
|
bool scale;
|
||||||
|
bool binning;
|
||||||
|
uint8_t quality;//0 - 63
|
||||||
|
int8_t brightness;//-2 - 2
|
||||||
|
int8_t contrast;//-2 - 2
|
||||||
|
int8_t saturation;//-2 - 2
|
||||||
|
int8_t sharpness;//-2 - 2
|
||||||
|
uint8_t denoise;
|
||||||
|
uint8_t special_effect;//0 - 6
|
||||||
|
uint8_t wb_mode;//0 - 4
|
||||||
|
uint8_t awb;
|
||||||
|
uint8_t awb_gain;
|
||||||
|
uint8_t aec;
|
||||||
|
uint8_t aec2;
|
||||||
|
int8_t ae_level;//-2 - 2
|
||||||
|
uint16_t aec_value;//0 - 1200
|
||||||
|
uint8_t agc;
|
||||||
|
uint8_t agc_gain;//0 - 30
|
||||||
|
uint8_t gainceiling;//0 - 6
|
||||||
|
uint8_t bpc;
|
||||||
|
uint8_t wpc;
|
||||||
|
uint8_t raw_gma;
|
||||||
|
uint8_t lenc;
|
||||||
|
uint8_t hmirror;
|
||||||
|
uint8_t vflip;
|
||||||
|
uint8_t dcw;
|
||||||
|
uint8_t colorbar;
|
||||||
|
} camera_status_t;
|
||||||
|
|
||||||
|
typedef struct _sensor sensor_t;
|
||||||
|
typedef struct _sensor {
|
||||||
|
sensor_id_t id; // Sensor ID.
|
||||||
|
uint8_t slv_addr; // Sensor I2C slave address.
|
||||||
|
pixformat_t pixformat;
|
||||||
|
camera_status_t status;
|
||||||
|
int xclk_freq_hz;
|
||||||
|
|
||||||
|
// Sensor function pointers
|
||||||
|
int (*init_status) (sensor_t *sensor);
|
||||||
|
int (*reset) (sensor_t *sensor);
|
||||||
|
int (*set_pixformat) (sensor_t *sensor, pixformat_t pixformat);
|
||||||
|
int (*set_framesize) (sensor_t *sensor, framesize_t framesize);
|
||||||
|
int (*set_contrast) (sensor_t *sensor, int level);
|
||||||
|
int (*set_brightness) (sensor_t *sensor, int level);
|
||||||
|
int (*set_saturation) (sensor_t *sensor, int level);
|
||||||
|
int (*set_sharpness) (sensor_t *sensor, int level);
|
||||||
|
int (*set_denoise) (sensor_t *sensor, int level);
|
||||||
|
int (*set_gainceiling) (sensor_t *sensor, gainceiling_t gainceiling);
|
||||||
|
int (*set_quality) (sensor_t *sensor, int quality);
|
||||||
|
int (*set_colorbar) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_whitebal) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_gain_ctrl) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_exposure_ctrl) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_hmirror) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_vflip) (sensor_t *sensor, int enable);
|
||||||
|
|
||||||
|
int (*set_aec2) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_awb_gain) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_agc_gain) (sensor_t *sensor, int gain);
|
||||||
|
int (*set_aec_value) (sensor_t *sensor, int gain);
|
||||||
|
|
||||||
|
int (*set_special_effect) (sensor_t *sensor, int effect);
|
||||||
|
int (*set_wb_mode) (sensor_t *sensor, int mode);
|
||||||
|
int (*set_ae_level) (sensor_t *sensor, int level);
|
||||||
|
|
||||||
|
int (*set_dcw) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_bpc) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_wpc) (sensor_t *sensor, int enable);
|
||||||
|
|
||||||
|
int (*set_raw_gma) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_lenc) (sensor_t *sensor, int enable);
|
||||||
|
|
||||||
|
int (*get_reg) (sensor_t *sensor, int reg, int mask);
|
||||||
|
int (*set_reg) (sensor_t *sensor, int reg, int mask, int value);
|
||||||
|
int (*set_res_raw) (sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning);
|
||||||
|
int (*set_pll) (sensor_t *sensor, int bypass, int mul, int sys, int root, int pre, int seld5, int pclken, int pclk);
|
||||||
|
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
|
||||||
|
} sensor_t;
|
||||||
|
|
||||||
|
#endif /* __SENSOR_H__ */
|
||||||
432
code/lib/driver/twi.c
Normal file
432
code/lib/driver/twi.c
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
/*
|
||||||
|
si2c.c - Software I2C library for ESP31B
|
||||||
|
|
||||||
|
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||||
|
This file is part of the ESP31B core for Arduino environment.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "twi.h"
|
||||||
|
#include "soc/gpio_reg.h"
|
||||||
|
#include "soc/gpio_struct.h"
|
||||||
|
#include "soc/io_mux_reg.h"
|
||||||
|
#include "driver/rtc_io.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define LOW 0x0
|
||||||
|
#define HIGH 0x1
|
||||||
|
|
||||||
|
//GPIO FUNCTIONS
|
||||||
|
#define INPUT 0x01
|
||||||
|
#define OUTPUT 0x02
|
||||||
|
#define PULLUP 0x04
|
||||||
|
#define INPUT_PULLUP 0x05
|
||||||
|
#define PULLDOWN 0x08
|
||||||
|
#define INPUT_PULLDOWN 0x09
|
||||||
|
#define OPEN_DRAIN 0x10
|
||||||
|
#define OUTPUT_OPEN_DRAIN 0x12
|
||||||
|
#define SPECIAL 0xF0
|
||||||
|
#define FUNCTION_1 0x00
|
||||||
|
#define FUNCTION_2 0x20
|
||||||
|
#define FUNCTION_3 0x40
|
||||||
|
#define FUNCTION_4 0x60
|
||||||
|
#define FUNCTION_5 0x80
|
||||||
|
#define FUNCTION_6 0xA0
|
||||||
|
|
||||||
|
#define ESP_REG(addr) *((volatile uint32_t *)(addr))
|
||||||
|
|
||||||
|
const uint8_t pin_to_mux[40] = { 0x44, 0x88, 0x40, 0x84, 0x48, 0x6c, 0x60, 0x64, 0x68, 0x54, 0x58, 0x5c, 0x34, 0x38, 0x30, 0x3c, 0x4c, 0x50, 0x70, 0x74, 0x78, 0x7c, 0x80, 0x8c, 0, 0x24, 0x28, 0x2c, 0, 0, 0, 0, 0x1c, 0x20, 0x14, 0x18, 0x04, 0x08, 0x0c, 0x10};
|
||||||
|
|
||||||
|
static void pinMode(uint8_t pin, uint8_t mode)
|
||||||
|
{
|
||||||
|
if(pin >= 40) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t rtc_reg = rtc_gpio_desc[pin].reg;
|
||||||
|
|
||||||
|
//RTC pins PULL settings
|
||||||
|
if(rtc_reg) {
|
||||||
|
//lock rtc
|
||||||
|
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
|
||||||
|
if(mode & PULLUP) {
|
||||||
|
ESP_REG(rtc_reg) = (ESP_REG(rtc_reg) | rtc_gpio_desc[pin].pullup) & ~(rtc_gpio_desc[pin].pulldown);
|
||||||
|
} else if(mode & PULLDOWN) {
|
||||||
|
ESP_REG(rtc_reg) = (ESP_REG(rtc_reg) | rtc_gpio_desc[pin].pulldown) & ~(rtc_gpio_desc[pin].pullup);
|
||||||
|
} else {
|
||||||
|
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
|
||||||
|
}
|
||||||
|
//unlock rtc
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t pinFunction = 0, pinControl = 0;
|
||||||
|
|
||||||
|
//lock gpio
|
||||||
|
if(mode & INPUT) {
|
||||||
|
if(pin < 32) {
|
||||||
|
GPIO.enable_w1tc = BIT(pin);
|
||||||
|
} else {
|
||||||
|
GPIO.enable1_w1tc.val = BIT(pin - 32);
|
||||||
|
}
|
||||||
|
} else if(mode & OUTPUT) {
|
||||||
|
if(pin > 33) {
|
||||||
|
//unlock gpio
|
||||||
|
return;//pins above 33 can be only inputs
|
||||||
|
} else if(pin < 32) {
|
||||||
|
GPIO.enable_w1ts = BIT(pin);
|
||||||
|
} else {
|
||||||
|
GPIO.enable1_w1ts.val = BIT(pin - 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode & PULLUP) {
|
||||||
|
pinFunction |= FUN_PU;
|
||||||
|
} else if(mode & PULLDOWN) {
|
||||||
|
pinFunction |= FUN_PD;
|
||||||
|
}
|
||||||
|
|
||||||
|
pinFunction |= ((uint32_t)2 << FUN_DRV_S);//what are the drivers?
|
||||||
|
pinFunction |= FUN_IE;//input enable but required for output as well?
|
||||||
|
|
||||||
|
if(mode & (INPUT | OUTPUT)) {
|
||||||
|
pinFunction |= ((uint32_t)2 << MCU_SEL_S);
|
||||||
|
} else if(mode == SPECIAL) {
|
||||||
|
pinFunction |= ((uint32_t)(((pin)==1||(pin)==3)?0:1) << MCU_SEL_S);
|
||||||
|
} else {
|
||||||
|
pinFunction |= ((uint32_t)(mode >> 5) << MCU_SEL_S);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_REG(DR_REG_IO_MUX_BASE + pin_to_mux[pin]) = pinFunction;
|
||||||
|
|
||||||
|
if(mode & OPEN_DRAIN) {
|
||||||
|
pinControl = (1 << GPIO_PIN0_PAD_DRIVER_S);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPIO.pin[pin].val = pinControl;
|
||||||
|
//unlock gpio
|
||||||
|
}
|
||||||
|
|
||||||
|
static void digitalWrite(uint8_t pin, uint8_t val)
|
||||||
|
{
|
||||||
|
if(val) {
|
||||||
|
if(pin < 32) {
|
||||||
|
GPIO.out_w1ts = BIT(pin);
|
||||||
|
} else if(pin < 34) {
|
||||||
|
GPIO.out1_w1ts.val = BIT(pin - 32);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(pin < 32) {
|
||||||
|
GPIO.out_w1tc = BIT(pin);
|
||||||
|
} else if(pin < 34) {
|
||||||
|
GPIO.out1_w1tc.val = BIT(pin - 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned char twi_dcount = 18;
|
||||||
|
static unsigned char twi_sda, twi_scl;
|
||||||
|
|
||||||
|
|
||||||
|
static inline void SDA_LOW()
|
||||||
|
{
|
||||||
|
//Enable SDA (becomes output and since GPO is 0 for the pin,
|
||||||
|
// it will pull the line low)
|
||||||
|
if (twi_sda < 32) {
|
||||||
|
GPIO.enable_w1ts = BIT(twi_sda);
|
||||||
|
} else {
|
||||||
|
GPIO.enable1_w1ts.val = BIT(twi_sda - 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void SDA_HIGH()
|
||||||
|
{
|
||||||
|
//Disable SDA (becomes input and since it has pullup it will go high)
|
||||||
|
if (twi_sda < 32) {
|
||||||
|
GPIO.enable_w1tc = BIT(twi_sda);
|
||||||
|
} else {
|
||||||
|
GPIO.enable1_w1tc.val = BIT(twi_sda - 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t SDA_READ()
|
||||||
|
{
|
||||||
|
if (twi_sda < 32) {
|
||||||
|
return (GPIO.in & BIT(twi_sda)) != 0;
|
||||||
|
} else {
|
||||||
|
return (GPIO.in1.val & BIT(twi_sda - 32)) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SCL_LOW()
|
||||||
|
{
|
||||||
|
if (twi_scl < 32) {
|
||||||
|
GPIO.enable_w1ts = BIT(twi_scl);
|
||||||
|
} else {
|
||||||
|
GPIO.enable1_w1ts.val = BIT(twi_scl - 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SCL_HIGH()
|
||||||
|
{
|
||||||
|
if (twi_scl < 32) {
|
||||||
|
GPIO.enable_w1tc = BIT(twi_scl);
|
||||||
|
} else {
|
||||||
|
GPIO.enable1_w1tc.val = BIT(twi_scl - 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t SCL_READ()
|
||||||
|
{
|
||||||
|
if (twi_scl < 32) {
|
||||||
|
return (GPIO.in & BIT(twi_scl)) != 0;
|
||||||
|
} else {
|
||||||
|
return (GPIO.in1.val & BIT(twi_scl - 32)) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FCPU80
|
||||||
|
#define FCPU80 80000000L
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if F_CPU == FCPU80
|
||||||
|
#define TWI_CLOCK_STRETCH 800
|
||||||
|
#else
|
||||||
|
#define TWI_CLOCK_STRETCH 1600
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void twi_setClock(unsigned int freq)
|
||||||
|
{
|
||||||
|
#if F_CPU == FCPU80
|
||||||
|
if(freq <= 100000) {
|
||||||
|
twi_dcount = 19; //about 100KHz
|
||||||
|
} else if(freq <= 200000) {
|
||||||
|
twi_dcount = 8; //about 200KHz
|
||||||
|
} else if(freq <= 300000) {
|
||||||
|
twi_dcount = 3; //about 300KHz
|
||||||
|
} else if(freq <= 400000) {
|
||||||
|
twi_dcount = 1; //about 400KHz
|
||||||
|
} else {
|
||||||
|
twi_dcount = 1; //about 400KHz
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(freq <= 100000) {
|
||||||
|
twi_dcount = 32; //about 100KHz
|
||||||
|
} else if(freq <= 200000) {
|
||||||
|
twi_dcount = 14; //about 200KHz
|
||||||
|
} else if(freq <= 300000) {
|
||||||
|
twi_dcount = 8; //about 300KHz
|
||||||
|
} else if(freq <= 400000) {
|
||||||
|
twi_dcount = 5; //about 400KHz
|
||||||
|
} else if(freq <= 500000) {
|
||||||
|
twi_dcount = 3; //about 500KHz
|
||||||
|
} else if(freq <= 600000) {
|
||||||
|
twi_dcount = 2; //about 600KHz
|
||||||
|
} else {
|
||||||
|
twi_dcount = 1; //about 700KHz
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void twi_init(unsigned char sda, unsigned char scl)
|
||||||
|
{
|
||||||
|
twi_sda = sda;
|
||||||
|
twi_scl = scl;
|
||||||
|
pinMode(twi_sda, OUTPUT);
|
||||||
|
pinMode(twi_scl, OUTPUT);
|
||||||
|
|
||||||
|
digitalWrite(twi_sda, 0);
|
||||||
|
digitalWrite(twi_scl, 0);
|
||||||
|
|
||||||
|
pinMode(twi_sda, INPUT_PULLUP);
|
||||||
|
pinMode(twi_scl, INPUT_PULLUP);
|
||||||
|
twi_setClock(100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void twi_stop(void)
|
||||||
|
{
|
||||||
|
pinMode(twi_sda, INPUT);
|
||||||
|
pinMode(twi_scl, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void twi_delay(unsigned char v)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||||
|
unsigned int reg;
|
||||||
|
for(i=0; i<v; i++) {
|
||||||
|
reg = REG_READ(GPIO_IN_REG);
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool twi_write_start(void)
|
||||||
|
{
|
||||||
|
SCL_HIGH();
|
||||||
|
SDA_HIGH();
|
||||||
|
if (SDA_READ() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
SDA_LOW();
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool twi_write_stop(void)
|
||||||
|
{
|
||||||
|
unsigned int i = 0;
|
||||||
|
SCL_LOW();
|
||||||
|
SDA_LOW();
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
SCL_HIGH();
|
||||||
|
while (SCL_READ() == 0 && (i++) < TWI_CLOCK_STRETCH);// Clock stretching (up to 100us)
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
SDA_HIGH();
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_log = false;
|
||||||
|
static bool twi_write_bit(bool bit)
|
||||||
|
{
|
||||||
|
unsigned int i = 0;
|
||||||
|
SCL_LOW();
|
||||||
|
if (bit) {
|
||||||
|
SDA_HIGH();
|
||||||
|
if (do_log) {
|
||||||
|
twi_delay(twi_dcount+1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SDA_LOW();
|
||||||
|
if (do_log) {}
|
||||||
|
}
|
||||||
|
twi_delay(twi_dcount+1);
|
||||||
|
SCL_HIGH();
|
||||||
|
while (SCL_READ() == 0 && (i++) < TWI_CLOCK_STRETCH);// Clock stretching (up to 100us)
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool twi_read_bit(void)
|
||||||
|
{
|
||||||
|
unsigned int i = 0;
|
||||||
|
SCL_LOW();
|
||||||
|
SDA_HIGH();
|
||||||
|
twi_delay(twi_dcount+2);
|
||||||
|
SCL_HIGH();
|
||||||
|
while (SCL_READ() == 0 && (i++) < TWI_CLOCK_STRETCH);// Clock stretching (up to 100us)
|
||||||
|
bool bit = SDA_READ();
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool twi_write_byte(unsigned char byte)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (byte == 0x43) {
|
||||||
|
// printf("TWB %02x ", (uint32_t) byte);
|
||||||
|
// do_log = true;
|
||||||
|
}
|
||||||
|
unsigned char bit;
|
||||||
|
for (bit = 0; bit < 8; bit++) {
|
||||||
|
twi_write_bit((byte & 0x80) != 0);
|
||||||
|
byte <<= 1;
|
||||||
|
}
|
||||||
|
if (do_log) {
|
||||||
|
printf("\n");
|
||||||
|
do_log = false;
|
||||||
|
}
|
||||||
|
return !twi_read_bit();//NACK/ACK
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char twi_read_byte(bool nack)
|
||||||
|
{
|
||||||
|
unsigned char byte = 0;
|
||||||
|
unsigned char bit;
|
||||||
|
for (bit = 0; bit < 8; bit++) {
|
||||||
|
byte = (byte << 1) | twi_read_bit();
|
||||||
|
}
|
||||||
|
twi_write_bit(nack);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
if(!twi_write_start()) {
|
||||||
|
return 4; //line busy
|
||||||
|
}
|
||||||
|
if(!twi_write_byte(((address << 1) | 0) & 0xFF)) {
|
||||||
|
if (sendStop) {
|
||||||
|
twi_write_stop();
|
||||||
|
}
|
||||||
|
return 2; //received NACK on transmit of address
|
||||||
|
}
|
||||||
|
for(i=0; i<len; i++) {
|
||||||
|
if(!twi_write_byte(buf[i])) {
|
||||||
|
if (sendStop) {
|
||||||
|
twi_write_stop();
|
||||||
|
}
|
||||||
|
return 3;//received NACK on transmit of data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(sendStop) {
|
||||||
|
twi_write_stop();
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
while(SDA_READ() == 0 && (i++) < 10) {
|
||||||
|
SCL_LOW();
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
SCL_HIGH();
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
if(!twi_write_start()) {
|
||||||
|
return 4; //line busy
|
||||||
|
}
|
||||||
|
if(!twi_write_byte(((address << 1) | 1) & 0xFF)) {
|
||||||
|
if (sendStop) {
|
||||||
|
twi_write_stop();
|
||||||
|
}
|
||||||
|
return 2;//received NACK on transmit of address
|
||||||
|
}
|
||||||
|
for(i=0; i<(len-1); i++) {
|
||||||
|
buf[i] = twi_read_byte(false);
|
||||||
|
}
|
||||||
|
buf[len-1] = twi_read_byte(true);
|
||||||
|
if(sendStop) {
|
||||||
|
twi_write_stop();
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
while(SDA_READ() == 0 && (i++) < 10) {
|
||||||
|
SCL_LOW();
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
SCL_HIGH();
|
||||||
|
twi_delay(twi_dcount);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
38
code/lib/driver/twi.h
Normal file
38
code/lib/driver/twi.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
twi.h - Software I2C library for ESP31B
|
||||||
|
|
||||||
|
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||||
|
This file is part of the ESP31B core for Arduino environment.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#ifndef SI2C_h
|
||||||
|
#define SI2C_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void twi_init(unsigned char sda, unsigned char scl);
|
||||||
|
void twi_stop(void);
|
||||||
|
void twi_setClock(unsigned int freq);
|
||||||
|
uint8_t twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop);
|
||||||
|
uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
64
code/lib/driver/xclk.c
Normal file
64
code/lib/driver/xclk.c
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "xclk.h"
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#else
|
||||||
|
#include "esp_log.h"
|
||||||
|
static const char* TAG = "camera_xclk";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
|
||||||
|
{
|
||||||
|
ledc_timer_config_t timer_conf;
|
||||||
|
timer_conf.duty_resolution = 2;
|
||||||
|
timer_conf.freq_hz = xclk_freq_hz;
|
||||||
|
timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||||
|
timer_conf.clk_cfg = LEDC_AUTO_CLK;
|
||||||
|
#endif
|
||||||
|
// timer_conf.clk_cfg = LEDC_USE_APB_CLK;
|
||||||
|
|
||||||
|
timer_conf.timer_num = (ledc_timer_t)ledc_timer;
|
||||||
|
esp_err_t err = ledc_timer_config(&timer_conf);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "ledc_timer_config failed for freq %d, rc=%x", xclk_freq_hz, err);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t camera_enable_out_clock(camera_config_t* config)
|
||||||
|
{
|
||||||
|
periph_module_enable(PERIPH_LEDC_MODULE);
|
||||||
|
|
||||||
|
esp_err_t err = xclk_timer_conf(config->ledc_timer, config->xclk_freq_hz);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ledc_channel_config_t ch_conf;
|
||||||
|
ch_conf.gpio_num = config->pin_xclk;
|
||||||
|
ch_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
|
||||||
|
ch_conf.channel = config->ledc_channel;
|
||||||
|
ch_conf.intr_type = LEDC_INTR_DISABLE;
|
||||||
|
ch_conf.timer_sel = config->ledc_timer;
|
||||||
|
ch_conf.duty = 2;
|
||||||
|
ch_conf.hpoint = 0;
|
||||||
|
err = ledc_channel_config(&ch_conf);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "ledc_channel_config failed, rc=%x", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void camera_disable_out_clock()
|
||||||
|
{
|
||||||
|
periph_module_disable(PERIPH_LEDC_MODULE);
|
||||||
|
}
|
||||||
7
code/lib/driver/xclk.h
Normal file
7
code/lib/driver/xclk.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "camera_common.h"
|
||||||
|
|
||||||
|
esp_err_t camera_enable_out_clock();
|
||||||
|
|
||||||
|
void camera_disable_out_clock();
|
||||||
254
code/lib/jomjol_controlcamera/ClassControllCamera.cpp
Normal file
254
code/lib/jomjol_controlcamera/ClassControllCamera.cpp
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
#include "ClassControllCamera.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
#include "CFindTemplate.h"
|
||||||
|
|
||||||
|
#include "camera_define.h"
|
||||||
|
|
||||||
|
CCamera Camera;
|
||||||
|
|
||||||
|
|
||||||
|
#define FLASH_GPIO GPIO_NUM_4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
httpd_req_t *req;
|
||||||
|
size_t len;
|
||||||
|
} jpg_chunking_t;
|
||||||
|
|
||||||
|
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
|
||||||
|
jpg_chunking_t *j = (jpg_chunking_t *)arg;
|
||||||
|
if(!index){
|
||||||
|
j->len = 0;
|
||||||
|
}
|
||||||
|
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
j->len += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CCamera::SetQualitySize(int qual, framesize_t resol)
|
||||||
|
{
|
||||||
|
sensor_t * s = esp_camera_sensor_get();
|
||||||
|
s->set_quality(s, qual);
|
||||||
|
s->set_framesize(s, resol);
|
||||||
|
ActualResolution = resol;
|
||||||
|
ActualQuality = qual;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
||||||
|
{
|
||||||
|
// nm = "/sdcard/josef_zw.bmp";
|
||||||
|
string ftype;
|
||||||
|
|
||||||
|
if (delay > 0)
|
||||||
|
{
|
||||||
|
LightOnOff(true);
|
||||||
|
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||||
|
vTaskDelay( xDelay );
|
||||||
|
}
|
||||||
|
|
||||||
|
camera_fb_t * fb = esp_camera_fb_get();
|
||||||
|
if (!fb) {
|
||||||
|
ESP_LOGE(TAGCAMERACLASS, "Camera Capture Failed");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
printf("w %d, h %d, size %d\n", fb->width, fb->height, fb->len);
|
||||||
|
|
||||||
|
nm = FormatFileName(nm);
|
||||||
|
printf("Save Camera to : %s\n", nm.c_str());
|
||||||
|
ftype = toUpper(getFileType(nm));
|
||||||
|
printf("Filetype: %s\n", ftype.c_str());
|
||||||
|
|
||||||
|
uint8_t * buf = NULL;
|
||||||
|
size_t buf_len = 0;
|
||||||
|
bool converted = false;
|
||||||
|
|
||||||
|
if (ftype.compare("BMP") == 0)
|
||||||
|
{
|
||||||
|
frame2bmp(fb, &buf, &buf_len);
|
||||||
|
converted = true;
|
||||||
|
}
|
||||||
|
if (ftype.compare("JPG") == 0)
|
||||||
|
{
|
||||||
|
if(fb->format != PIXFORMAT_JPEG){
|
||||||
|
bool jpeg_converted = frame2jpg(fb, ActualQuality, &buf, &buf_len);
|
||||||
|
converted = true;
|
||||||
|
if(!jpeg_converted){
|
||||||
|
ESP_LOGE(TAGCAMERACLASS, "JPEG compression failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf_len = fb->len;
|
||||||
|
buf = fb->buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE * fp = fopen(nm.c_str(), "wb");
|
||||||
|
if (fp == NULL) /* If an error occurs during the file creation */
|
||||||
|
{
|
||||||
|
fprintf(stderr, "fopen() failed for '%s'\n", nm.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fwrite(buf, sizeof(uint8_t), buf_len, fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
if (converted)
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
esp_camera_fb_return(fb);
|
||||||
|
|
||||||
|
if (delay > 0)
|
||||||
|
{
|
||||||
|
LightOnOff(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
||||||
|
{
|
||||||
|
camera_fb_t * fb = NULL;
|
||||||
|
esp_err_t res = ESP_OK;
|
||||||
|
size_t fb_len = 0;
|
||||||
|
int64_t fr_start = esp_timer_get_time();
|
||||||
|
|
||||||
|
fb = esp_camera_fb_get();
|
||||||
|
if (!fb) {
|
||||||
|
ESP_LOGE(TAGCAMERACLASS, "Camera capture failed");
|
||||||
|
httpd_resp_send_500(req);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
res = httpd_resp_set_type(req, "image/jpeg");
|
||||||
|
if(res == ESP_OK){
|
||||||
|
res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res == ESP_OK){
|
||||||
|
if(fb->format == PIXFORMAT_JPEG){
|
||||||
|
fb_len = fb->len;
|
||||||
|
res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
|
||||||
|
} else {
|
||||||
|
jpg_chunking_t jchunk = {req, 0};
|
||||||
|
res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
|
||||||
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
|
fb_len = jchunk.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esp_camera_fb_return(fb);
|
||||||
|
int64_t fr_end = esp_timer_get_time();
|
||||||
|
|
||||||
|
ESP_LOGI(TAGCAMERACLASS, "JPG: %uKB %ums", (uint32_t)(fb_len/1024), (uint32_t)((fr_end - fr_start)/1000));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCamera::LightOnOff(bool status)
|
||||||
|
{
|
||||||
|
// Init the GPIO
|
||||||
|
gpio_pad_select_gpio(FLASH_GPIO);
|
||||||
|
/* Set the GPIO as a push/pull output */
|
||||||
|
gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT);
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
gpio_set_level(FLASH_GPIO, 1);
|
||||||
|
else
|
||||||
|
gpio_set_level(FLASH_GPIO, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol)
|
||||||
|
{
|
||||||
|
char _query[100];
|
||||||
|
char _qual[10];
|
||||||
|
char _size[10];
|
||||||
|
|
||||||
|
resol = ActualResolution;
|
||||||
|
qual = ActualQuality;
|
||||||
|
|
||||||
|
|
||||||
|
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Query: "); printf(_query); printf("\n");
|
||||||
|
if (httpd_query_key_value(_query, "size", _size, 10) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Size: "); printf(_size); printf("\n");
|
||||||
|
if (strcmp(_size, "QVGA") == 0)
|
||||||
|
resol = FRAMESIZE_QVGA; // 320x240
|
||||||
|
if (strcmp(_size, "VGA") == 0)
|
||||||
|
resol = FRAMESIZE_VGA; // 640x480
|
||||||
|
if (strcmp(_size, "SVGA") == 0)
|
||||||
|
resol = FRAMESIZE_SVGA; // 800x600
|
||||||
|
if (strcmp(_size, "XGA") == 0)
|
||||||
|
resol = FRAMESIZE_XGA; // 1024x768
|
||||||
|
if (strcmp(_size, "SXGA") == 0)
|
||||||
|
resol = FRAMESIZE_SXGA; // 1280x1024
|
||||||
|
if (strcmp(_size, "UXGA") == 0)
|
||||||
|
resol = FRAMESIZE_UXGA; // 1600x1200
|
||||||
|
}
|
||||||
|
if (httpd_query_key_value(_query, "quality", _qual, 10) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Quality: "); printf(_qual); printf("\n");
|
||||||
|
qual = atoi(_qual);
|
||||||
|
|
||||||
|
if (qual > 63)
|
||||||
|
qual = 63;
|
||||||
|
if (qual < 0)
|
||||||
|
qual = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
framesize_t CCamera::TextToFramesize(const char * _size)
|
||||||
|
{
|
||||||
|
if (strcmp(_size, "QVGA") == 0)
|
||||||
|
return FRAMESIZE_QVGA; // 320x240
|
||||||
|
if (strcmp(_size, "VGA") == 0)
|
||||||
|
return FRAMESIZE_VGA; // 640x480
|
||||||
|
if (strcmp(_size, "SVGA") == 0)
|
||||||
|
return FRAMESIZE_SVGA; // 800x600
|
||||||
|
if (strcmp(_size, "XGA") == 0)
|
||||||
|
return FRAMESIZE_XGA; // 1024x768
|
||||||
|
if (strcmp(_size, "SXGA") == 0)
|
||||||
|
return FRAMESIZE_SXGA; // 1280x1024
|
||||||
|
if (strcmp(_size, "UXGA") == 0)
|
||||||
|
return FRAMESIZE_UXGA; // 1600x1200
|
||||||
|
return ActualResolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CCamera::CCamera()
|
||||||
|
{
|
||||||
|
printf("CreateClassCamera\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t CCamera::InitCam()
|
||||||
|
{
|
||||||
|
printf("Init Flash\n");
|
||||||
|
//power up the camera if PWDN pin is defined
|
||||||
|
if(PWDN_GPIO_NUM != -1){
|
||||||
|
// Init the GPIO
|
||||||
|
gpio_pad_select_gpio(PWDN_GPIO_NUM);
|
||||||
|
/* Set the GPIO as a push/pull output */
|
||||||
|
gpio_set_direction(PWDN_GPIO_NUM, GPIO_MODE_OUTPUT);
|
||||||
|
gpio_set_level(PWDN_GPIO_NUM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Init Camera\n");
|
||||||
|
ActualQuality = camera_config.jpeg_quality;
|
||||||
|
ActualResolution = camera_config.frame_size;
|
||||||
|
//initialize the camera
|
||||||
|
esp_err_t err = esp_camera_init(&camera_config);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAGCAMERACLASS, "Camera Init Failed");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
46
code/lib/jomjol_controlcamera/ClassControllCamera.h
Normal file
46
code/lib/jomjol_controlcamera/ClassControllCamera.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef CLASSCONTROLLCAMERA_H
|
||||||
|
#define CLASSCONTROLLCAMERA_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
|
||||||
|
#include "esp_camera.h"
|
||||||
|
#include <string>
|
||||||
|
#include "esp_http_server.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define CAMERA_MODEL_AI_THINKER
|
||||||
|
|
||||||
|
|
||||||
|
static const char *TAGCAMERACLASS = "server_part_camera";
|
||||||
|
|
||||||
|
|
||||||
|
class CCamera {
|
||||||
|
protected:
|
||||||
|
int ActualQuality;
|
||||||
|
framesize_t ActualResolution;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CCamera();
|
||||||
|
esp_err_t InitCam();
|
||||||
|
|
||||||
|
void LightOnOff(bool status);
|
||||||
|
esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0);
|
||||||
|
void SetQualitySize(int qual, framesize_t resol);
|
||||||
|
void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
|
||||||
|
|
||||||
|
framesize_t TextToFramesize(const char * text);
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t CaptureToFile(std::string nm, int delay = 0);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern CCamera Camera;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
97
code/lib/jomjol_controlcamera/camera_define.h
Normal file
97
code/lib/jomjol_controlcamera/camera_define.h
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#define CAMERA_MODEL_AI_THINKER
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(CAMERA_MODEL_WROVER_KIT)
|
||||||
|
#define PWDN_GPIO_NUM -1
|
||||||
|
#define RESET_GPIO_NUM -1
|
||||||
|
#define XCLK_GPIO_NUM 21
|
||||||
|
#define SIOD_GPIO_NUM 26
|
||||||
|
#define SIOC_GPIO_NUM 27
|
||||||
|
|
||||||
|
#define Y9_GPIO_NUM 35
|
||||||
|
#define Y8_GPIO_NUM 34
|
||||||
|
#define Y7_GPIO_NUM 39
|
||||||
|
#define Y6_GPIO_NUM 36
|
||||||
|
#define Y5_GPIO_NUM 19
|
||||||
|
#define Y4_GPIO_NUM 18
|
||||||
|
#define Y3_GPIO_NUM 5
|
||||||
|
#define Y2_GPIO_NUM 4
|
||||||
|
#define VSYNC_GPIO_NUM 25
|
||||||
|
#define HREF_GPIO_NUM 23
|
||||||
|
#define PCLK_GPIO_NUM 22
|
||||||
|
|
||||||
|
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
|
||||||
|
#define PWDN_GPIO_NUM -1
|
||||||
|
#define RESET_GPIO_NUM 15
|
||||||
|
#define XCLK_GPIO_NUM 27
|
||||||
|
#define SIOD_GPIO_NUM 25
|
||||||
|
#define SIOC_GPIO_NUM 23
|
||||||
|
|
||||||
|
#define Y9_GPIO_NUM 19
|
||||||
|
#define Y8_GPIO_NUM 36
|
||||||
|
#define Y7_GPIO_NUM 18
|
||||||
|
#define Y6_GPIO_NUM 39
|
||||||
|
#define Y5_GPIO_NUM 5
|
||||||
|
#define Y4_GPIO_NUM 34
|
||||||
|
#define Y3_GPIO_NUM 35
|
||||||
|
#define Y2_GPIO_NUM 32
|
||||||
|
#define VSYNC_GPIO_NUM 22
|
||||||
|
#define HREF_GPIO_NUM 26
|
||||||
|
#define PCLK_GPIO_NUM 21
|
||||||
|
|
||||||
|
#elif defined(CAMERA_MODEL_AI_THINKER)
|
||||||
|
#define PWDN_GPIO_NUM GPIO_NUM_32
|
||||||
|
#define RESET_GPIO_NUM -1
|
||||||
|
#define XCLK_GPIO_NUM GPIO_NUM_0
|
||||||
|
#define SIOD_GPIO_NUM GPIO_NUM_26
|
||||||
|
#define SIOC_GPIO_NUM GPIO_NUM_27
|
||||||
|
|
||||||
|
#define Y9_GPIO_NUM GPIO_NUM_35
|
||||||
|
#define Y8_GPIO_NUM GPIO_NUM_34
|
||||||
|
#define Y7_GPIO_NUM GPIO_NUM_39
|
||||||
|
#define Y6_GPIO_NUM GPIO_NUM_36
|
||||||
|
#define Y5_GPIO_NUM GPIO_NUM_21
|
||||||
|
#define Y4_GPIO_NUM GPIO_NUM_19
|
||||||
|
#define Y3_GPIO_NUM GPIO_NUM_18
|
||||||
|
#define Y2_GPIO_NUM GPIO_NUM_5
|
||||||
|
#define VSYNC_GPIO_NUM GPIO_NUM_25
|
||||||
|
#define HREF_GPIO_NUM GPIO_NUM_23
|
||||||
|
#define PCLK_GPIO_NUM GPIO_NUM_22
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Camera model not selected"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static camera_config_t camera_config = {
|
||||||
|
.pin_pwdn = PWDN_GPIO_NUM,
|
||||||
|
.pin_reset = RESET_GPIO_NUM,
|
||||||
|
.pin_xclk = XCLK_GPIO_NUM,
|
||||||
|
.pin_sscb_sda = SIOD_GPIO_NUM,
|
||||||
|
.pin_sscb_scl = SIOC_GPIO_NUM,
|
||||||
|
|
||||||
|
.pin_d7 = Y9_GPIO_NUM,
|
||||||
|
.pin_d6 = Y8_GPIO_NUM,
|
||||||
|
.pin_d5 = Y7_GPIO_NUM,
|
||||||
|
.pin_d4 = Y6_GPIO_NUM,
|
||||||
|
.pin_d3 = Y5_GPIO_NUM,
|
||||||
|
.pin_d2 = Y4_GPIO_NUM,
|
||||||
|
.pin_d1 = Y3_GPIO_NUM,
|
||||||
|
.pin_d0 = Y2_GPIO_NUM,
|
||||||
|
.pin_vsync = VSYNC_GPIO_NUM,
|
||||||
|
.pin_href = HREF_GPIO_NUM,
|
||||||
|
.pin_pclk = PCLK_GPIO_NUM,
|
||||||
|
|
||||||
|
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
|
||||||
|
.xclk_freq_hz = 20000000,
|
||||||
|
.ledc_timer = LEDC_TIMER_0,
|
||||||
|
.ledc_channel = LEDC_CHANNEL_0,
|
||||||
|
|
||||||
|
.pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG
|
||||||
|
// .pixel_format = PIXFORMAT_RGB888,//YUV422,GRAYSCALE,RGB565,JPEG
|
||||||
|
.frame_size = FRAMESIZE_UXGA,//QQVGA-QXGA Do not use sizes above QVGA when not JPEG
|
||||||
|
|
||||||
|
.jpeg_quality = 5, //0-63 lower number means higher quality
|
||||||
|
.fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||||
|
};
|
||||||
200
code/lib/jomjol_controlcamera/esp_camera.h
Normal file
200
code/lib/jomjol_controlcamera/esp_camera.h
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/*
|
||||||
|
* Example Use
|
||||||
|
*
|
||||||
|
static camera_config_t camera_example_config = {
|
||||||
|
.pin_pwdn = PIN_PWDN,
|
||||||
|
.pin_reset = PIN_RESET,
|
||||||
|
.pin_xclk = PIN_XCLK,
|
||||||
|
.pin_sscb_sda = PIN_SIOD,
|
||||||
|
.pin_sscb_scl = PIN_SIOC,
|
||||||
|
.pin_d7 = PIN_D7,
|
||||||
|
.pin_d6 = PIN_D6,
|
||||||
|
.pin_d5 = PIN_D5,
|
||||||
|
.pin_d4 = PIN_D4,
|
||||||
|
.pin_d3 = PIN_D3,
|
||||||
|
.pin_d2 = PIN_D2,
|
||||||
|
.pin_d1 = PIN_D1,
|
||||||
|
.pin_d0 = PIN_D0,
|
||||||
|
.pin_vsync = PIN_VSYNC,
|
||||||
|
.pin_href = PIN_HREF,
|
||||||
|
.pin_pclk = PIN_PCLK,
|
||||||
|
|
||||||
|
.xclk_freq_hz = 20000000,
|
||||||
|
.ledc_timer = LEDC_TIMER_0,
|
||||||
|
.ledc_channel = LEDC_CHANNEL_0,
|
||||||
|
.pixel_format = PIXFORMAT_JPEG,
|
||||||
|
.frame_size = FRAMESIZE_SVGA,
|
||||||
|
.jpeg_quality = 10,
|
||||||
|
.fb_count = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t camera_example_init(){
|
||||||
|
return esp_camera_init(&camera_example_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t camera_example_capture(){
|
||||||
|
//capture a frame
|
||||||
|
camera_fb_t * fb = esp_camera_fb_get();
|
||||||
|
if (!fb) {
|
||||||
|
ESP_LOGE(TAG, "Frame buffer could not be acquired");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//replace this with your own function
|
||||||
|
display_image(fb->width, fb->height, fb->pixformat, fb->buf, fb->len);
|
||||||
|
|
||||||
|
//return the frame buffer back to be reused
|
||||||
|
esp_camera_fb_return(fb);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef ESPCAMERADEF
|
||||||
|
#define ESPCAMERADEF
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
#include "sensor.h"
|
||||||
|
#include "sys/time.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration structure for camera initialization
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int pin_pwdn; /*!< GPIO pin for camera power down line */
|
||||||
|
int pin_reset; /*!< GPIO pin for camera reset line */
|
||||||
|
int pin_xclk; /*!< GPIO pin for camera XCLK line */
|
||||||
|
int pin_sscb_sda; /*!< GPIO pin for camera SDA line */
|
||||||
|
int pin_sscb_scl; /*!< GPIO pin for camera SCL line */
|
||||||
|
int pin_d7; /*!< GPIO pin for camera D7 line */
|
||||||
|
int pin_d6; /*!< GPIO pin for camera D6 line */
|
||||||
|
int pin_d5; /*!< GPIO pin for camera D5 line */
|
||||||
|
int pin_d4; /*!< GPIO pin for camera D4 line */
|
||||||
|
int pin_d3; /*!< GPIO pin for camera D3 line */
|
||||||
|
int pin_d2; /*!< GPIO pin for camera D2 line */
|
||||||
|
int pin_d1; /*!< GPIO pin for camera D1 line */
|
||||||
|
int pin_d0; /*!< GPIO pin for camera D0 line */
|
||||||
|
int pin_vsync; /*!< GPIO pin for camera VSYNC line */
|
||||||
|
int pin_href; /*!< GPIO pin for camera HREF line */
|
||||||
|
int pin_pclk; /*!< GPIO pin for camera PCLK line */
|
||||||
|
|
||||||
|
int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental) */
|
||||||
|
|
||||||
|
ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */
|
||||||
|
ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */
|
||||||
|
|
||||||
|
pixformat_t pixel_format; /*!< Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG */
|
||||||
|
framesize_t frame_size; /*!< Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA */
|
||||||
|
|
||||||
|
int jpeg_quality; /*!< Quality of JPEG output. 0-63 lower means higher quality */
|
||||||
|
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
|
||||||
|
} camera_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data structure of camera frame buffer
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t * buf; /*!< Pointer to the pixel data */
|
||||||
|
size_t len; /*!< Length of the buffer in bytes */
|
||||||
|
size_t width; /*!< Width of the buffer in pixels */
|
||||||
|
size_t height; /*!< Height of the buffer in pixels */
|
||||||
|
pixformat_t format; /*!< Format of the pixel data */
|
||||||
|
struct timeval timestamp; /*!< Timestamp since boot of the first DMA buffer of the frame */
|
||||||
|
} camera_fb_t;
|
||||||
|
|
||||||
|
#define ESP_ERR_CAMERA_BASE 0x20000
|
||||||
|
#define ESP_ERR_CAMERA_NOT_DETECTED (ESP_ERR_CAMERA_BASE + 1)
|
||||||
|
#define ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE (ESP_ERR_CAMERA_BASE + 2)
|
||||||
|
#define ESP_ERR_CAMERA_FAILED_TO_SET_OUT_FORMAT (ESP_ERR_CAMERA_BASE + 3)
|
||||||
|
#define ESP_ERR_CAMERA_NOT_SUPPORTED (ESP_ERR_CAMERA_BASE + 4)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the camera driver
|
||||||
|
*
|
||||||
|
* @note call camera_probe before calling this function
|
||||||
|
*
|
||||||
|
* This function detects and configures camera over I2C interface,
|
||||||
|
* allocates framebuffer and DMA buffers,
|
||||||
|
* initializes parallel I2S input, and sets up DMA descriptors.
|
||||||
|
*
|
||||||
|
* Currently this function can only be called once and there is
|
||||||
|
* no way to de-initialize this module.
|
||||||
|
*
|
||||||
|
* @param config Camera configuration parameters
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_camera_init(const camera_config_t* config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deinitialize the camera driver
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if the driver hasn't been initialized yet
|
||||||
|
*/
|
||||||
|
esp_err_t esp_camera_deinit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtain pointer to a frame buffer.
|
||||||
|
*
|
||||||
|
* @return pointer to the frame buffer
|
||||||
|
*/
|
||||||
|
camera_fb_t* esp_camera_fb_get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the frame buffer to be reused again.
|
||||||
|
*
|
||||||
|
* @param fb Pointer to the frame buffer
|
||||||
|
*/
|
||||||
|
void esp_camera_fb_return(camera_fb_t * fb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a pointer to the image sensor control structure
|
||||||
|
*
|
||||||
|
* @return pointer to the sensor
|
||||||
|
*/
|
||||||
|
sensor_t * esp_camera_sensor_get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save camera settings to non-volatile-storage (NVS)
|
||||||
|
*
|
||||||
|
* @param key A unique nvs key name for the camera settings
|
||||||
|
*/
|
||||||
|
esp_err_t esp_camera_save_to_nvs(const char *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load camera settings from non-volatile-storage (NVS)
|
||||||
|
*
|
||||||
|
* @param key A unique nvs key name for the camera settings
|
||||||
|
*/
|
||||||
|
esp_err_t esp_camera_load_from_nvs(const char *key);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "img_converters.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
126
code/lib/jomjol_controlcamera/img_converters.h
Normal file
126
code/lib/jomjol_controlcamera/img_converters.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef _IMG_CONVERTERS_H_
|
||||||
|
#define _IMG_CONVERTERS_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_camera.h"
|
||||||
|
|
||||||
|
typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert image buffer to JPEG
|
||||||
|
*
|
||||||
|
* @param src Source buffer in RGB565, RGB888, YUYV or GRAYSCALE format
|
||||||
|
* @param src_len Length in bytes of the source buffer
|
||||||
|
* @param width Width in pixels of the source image
|
||||||
|
* @param height Height in pixels of the source image
|
||||||
|
* @param format Format of the source image
|
||||||
|
* @param quality JPEG quality of the resulting image
|
||||||
|
* @param cp Callback to be called to write the bytes of the output JPEG
|
||||||
|
* @param arg Pointer to be passed to the callback
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool fmt2jpg_cb(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpg_out_cb cb, void * arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert camera frame buffer to JPEG
|
||||||
|
*
|
||||||
|
* @param fb Source camera frame buffer
|
||||||
|
* @param quality JPEG quality of the resulting image
|
||||||
|
* @param cp Callback to be called to write the bytes of the output JPEG
|
||||||
|
* @param arg Pointer to be passed to the callback
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert image buffer to JPEG buffer
|
||||||
|
*
|
||||||
|
* @param src Source buffer in RGB565, RGB888, YUYV or GRAYSCALE format
|
||||||
|
* @param src_len Length in bytes of the source buffer
|
||||||
|
* @param width Width in pixels of the source image
|
||||||
|
* @param height Height in pixels of the source image
|
||||||
|
* @param format Format of the source image
|
||||||
|
* @param quality JPEG quality of the resulting image
|
||||||
|
* @param out Pointer to be populated with the address of the resulting buffer
|
||||||
|
* @param out_len Pointer to be populated with the length of the output buffer
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, uint8_t ** out, size_t * out_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert camera frame buffer to JPEG buffer
|
||||||
|
*
|
||||||
|
* @param fb Source camera frame buffer
|
||||||
|
* @param quality JPEG quality of the resulting image
|
||||||
|
* @param out Pointer to be populated with the address of the resulting buffer
|
||||||
|
* @param out_len Pointer to be populated with the length of the output buffer
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool frame2jpg(camera_fb_t * fb, uint8_t quality, uint8_t ** out, size_t * out_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert image buffer to BMP buffer
|
||||||
|
*
|
||||||
|
* @param src Source buffer in JPEG, RGB565, RGB888, YUYV or GRAYSCALE format
|
||||||
|
* @param src_len Length in bytes of the source buffer
|
||||||
|
* @param width Width in pixels of the source image
|
||||||
|
* @param height Height in pixels of the source image
|
||||||
|
* @param format Format of the source image
|
||||||
|
* @param out Pointer to be populated with the address of the resulting buffer
|
||||||
|
* @param out_len Pointer to be populated with the length of the output buffer
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t ** out, size_t * out_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert camera frame buffer to BMP buffer
|
||||||
|
*
|
||||||
|
* @param fb Source camera frame buffer
|
||||||
|
* @param out Pointer to be populated with the address of the resulting buffer
|
||||||
|
* @param out_len Pointer to be populated with the length of the output buffer
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert image buffer to RGB888 buffer (used for face detection)
|
||||||
|
*
|
||||||
|
* @param src Source buffer in JPEG, RGB565, RGB888, YUYV or GRAYSCALE format
|
||||||
|
* @param src_len Length in bytes of the source buffer
|
||||||
|
* @param format Format of the source image
|
||||||
|
* @param rgb_buf Pointer to the output buffer (width * height * 3)
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _IMG_CONVERTERS_H_ */
|
||||||
191
code/lib/jomjol_controlcamera/sensor.h
Normal file
191
code/lib/jomjol_controlcamera/sensor.h
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* Sensor abstraction layer.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef __SENSOR_H__
|
||||||
|
#define __SENSOR_H__
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define OV9650_PID (0x96)
|
||||||
|
#define OV7725_PID (0x77)
|
||||||
|
#define OV2640_PID (0x26)
|
||||||
|
#define OV3660_PID (0x36)
|
||||||
|
#define OV5640_PID (0x56)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||||
|
PIXFORMAT_YUV422, // 2BPP/YUV422
|
||||||
|
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
|
||||||
|
PIXFORMAT_JPEG, // JPEG/COMPRESSED
|
||||||
|
PIXFORMAT_RGB888, // 3BPP/RGB888
|
||||||
|
PIXFORMAT_RAW, // RAW
|
||||||
|
PIXFORMAT_RGB444, // 3BP2P/RGB444
|
||||||
|
PIXFORMAT_RGB555, // 3BP2P/RGB555
|
||||||
|
} pixformat_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FRAMESIZE_96X96, // 96x96
|
||||||
|
FRAMESIZE_QQVGA, // 160x120
|
||||||
|
FRAMESIZE_QCIF, // 176x144
|
||||||
|
FRAMESIZE_HQVGA, // 240x176
|
||||||
|
FRAMESIZE_240X240, // 240x240
|
||||||
|
FRAMESIZE_QVGA, // 320x240
|
||||||
|
FRAMESIZE_CIF, // 400x296
|
||||||
|
FRAMESIZE_HVGA, // 480x320
|
||||||
|
FRAMESIZE_VGA, // 640x480
|
||||||
|
FRAMESIZE_SVGA, // 800x600
|
||||||
|
FRAMESIZE_XGA, // 1024x768
|
||||||
|
FRAMESIZE_HD, // 1280x720
|
||||||
|
FRAMESIZE_SXGA, // 1280x1024
|
||||||
|
FRAMESIZE_UXGA, // 1600x1200
|
||||||
|
// 3MP Sensors
|
||||||
|
FRAMESIZE_FHD, // 1920x1080
|
||||||
|
FRAMESIZE_P_HD, // 720x1280
|
||||||
|
FRAMESIZE_P_3MP, // 864x1536
|
||||||
|
FRAMESIZE_QXGA, // 2048x1536
|
||||||
|
// 5MP Sensors
|
||||||
|
FRAMESIZE_QHD, // 2560x1440
|
||||||
|
FRAMESIZE_WQXGA, // 2560x1600
|
||||||
|
FRAMESIZE_P_FHD, // 1080x1920
|
||||||
|
FRAMESIZE_QSXGA, // 2560x1920
|
||||||
|
FRAMESIZE_INVALID
|
||||||
|
} framesize_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ASPECT_RATIO_4X3,
|
||||||
|
ASPECT_RATIO_3X2,
|
||||||
|
ASPECT_RATIO_16X10,
|
||||||
|
ASPECT_RATIO_5X3,
|
||||||
|
ASPECT_RATIO_16X9,
|
||||||
|
ASPECT_RATIO_21X9,
|
||||||
|
ASPECT_RATIO_5X4,
|
||||||
|
ASPECT_RATIO_1X1,
|
||||||
|
ASPECT_RATIO_9X16
|
||||||
|
} aspect_ratio_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GAINCEILING_2X,
|
||||||
|
GAINCEILING_4X,
|
||||||
|
GAINCEILING_8X,
|
||||||
|
GAINCEILING_16X,
|
||||||
|
GAINCEILING_32X,
|
||||||
|
GAINCEILING_64X,
|
||||||
|
GAINCEILING_128X,
|
||||||
|
} gainceiling_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t max_width;
|
||||||
|
uint16_t max_height;
|
||||||
|
uint16_t start_x;
|
||||||
|
uint16_t start_y;
|
||||||
|
uint16_t end_x;
|
||||||
|
uint16_t end_y;
|
||||||
|
uint16_t offset_x;
|
||||||
|
uint16_t offset_y;
|
||||||
|
uint16_t total_x;
|
||||||
|
uint16_t total_y;
|
||||||
|
} ratio_settings_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const uint16_t width;
|
||||||
|
const uint16_t height;
|
||||||
|
const aspect_ratio_t aspect_ratio;
|
||||||
|
} resolution_info_t;
|
||||||
|
|
||||||
|
// Resolution table (in sensor.c)
|
||||||
|
extern const resolution_info_t resolution[];
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t MIDH;
|
||||||
|
uint8_t MIDL;
|
||||||
|
uint8_t PID;
|
||||||
|
uint8_t VER;
|
||||||
|
} sensor_id_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
framesize_t framesize;//0 - 10
|
||||||
|
bool scale;
|
||||||
|
bool binning;
|
||||||
|
uint8_t quality;//0 - 63
|
||||||
|
int8_t brightness;//-2 - 2
|
||||||
|
int8_t contrast;//-2 - 2
|
||||||
|
int8_t saturation;//-2 - 2
|
||||||
|
int8_t sharpness;//-2 - 2
|
||||||
|
uint8_t denoise;
|
||||||
|
uint8_t special_effect;//0 - 6
|
||||||
|
uint8_t wb_mode;//0 - 4
|
||||||
|
uint8_t awb;
|
||||||
|
uint8_t awb_gain;
|
||||||
|
uint8_t aec;
|
||||||
|
uint8_t aec2;
|
||||||
|
int8_t ae_level;//-2 - 2
|
||||||
|
uint16_t aec_value;//0 - 1200
|
||||||
|
uint8_t agc;
|
||||||
|
uint8_t agc_gain;//0 - 30
|
||||||
|
uint8_t gainceiling;//0 - 6
|
||||||
|
uint8_t bpc;
|
||||||
|
uint8_t wpc;
|
||||||
|
uint8_t raw_gma;
|
||||||
|
uint8_t lenc;
|
||||||
|
uint8_t hmirror;
|
||||||
|
uint8_t vflip;
|
||||||
|
uint8_t dcw;
|
||||||
|
uint8_t colorbar;
|
||||||
|
} camera_status_t;
|
||||||
|
|
||||||
|
typedef struct _sensor sensor_t;
|
||||||
|
typedef struct _sensor {
|
||||||
|
sensor_id_t id; // Sensor ID.
|
||||||
|
uint8_t slv_addr; // Sensor I2C slave address.
|
||||||
|
pixformat_t pixformat;
|
||||||
|
camera_status_t status;
|
||||||
|
int xclk_freq_hz;
|
||||||
|
|
||||||
|
// Sensor function pointers
|
||||||
|
int (*init_status) (sensor_t *sensor);
|
||||||
|
int (*reset) (sensor_t *sensor);
|
||||||
|
int (*set_pixformat) (sensor_t *sensor, pixformat_t pixformat);
|
||||||
|
int (*set_framesize) (sensor_t *sensor, framesize_t framesize);
|
||||||
|
int (*set_contrast) (sensor_t *sensor, int level);
|
||||||
|
int (*set_brightness) (sensor_t *sensor, int level);
|
||||||
|
int (*set_saturation) (sensor_t *sensor, int level);
|
||||||
|
int (*set_sharpness) (sensor_t *sensor, int level);
|
||||||
|
int (*set_denoise) (sensor_t *sensor, int level);
|
||||||
|
int (*set_gainceiling) (sensor_t *sensor, gainceiling_t gainceiling);
|
||||||
|
int (*set_quality) (sensor_t *sensor, int quality);
|
||||||
|
int (*set_colorbar) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_whitebal) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_gain_ctrl) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_exposure_ctrl) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_hmirror) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_vflip) (sensor_t *sensor, int enable);
|
||||||
|
|
||||||
|
int (*set_aec2) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_awb_gain) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_agc_gain) (sensor_t *sensor, int gain);
|
||||||
|
int (*set_aec_value) (sensor_t *sensor, int gain);
|
||||||
|
|
||||||
|
int (*set_special_effect) (sensor_t *sensor, int effect);
|
||||||
|
int (*set_wb_mode) (sensor_t *sensor, int mode);
|
||||||
|
int (*set_ae_level) (sensor_t *sensor, int level);
|
||||||
|
|
||||||
|
int (*set_dcw) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_bpc) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_wpc) (sensor_t *sensor, int enable);
|
||||||
|
|
||||||
|
int (*set_raw_gma) (sensor_t *sensor, int enable);
|
||||||
|
int (*set_lenc) (sensor_t *sensor, int enable);
|
||||||
|
|
||||||
|
int (*get_reg) (sensor_t *sensor, int reg, int mask);
|
||||||
|
int (*set_reg) (sensor_t *sensor, int reg, int mask, int value);
|
||||||
|
int (*set_res_raw) (sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning);
|
||||||
|
int (*set_pll) (sensor_t *sensor, int bypass, int mul, int sys, int root, int pre, int seld5, int pclken, int pclk);
|
||||||
|
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
|
||||||
|
} sensor_t;
|
||||||
|
|
||||||
|
#endif /* __SENSOR_H__ */
|
||||||
175
code/lib/jomjol_controlcamera/server_camera.cpp
Normal file
175
code/lib/jomjol_controlcamera/server_camera.cpp
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
#include "server_camera.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include "esp_camera.h"
|
||||||
|
#include "ClassControllCamera.h"
|
||||||
|
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
|
#define SCRATCH_BUFSIZE2 8192
|
||||||
|
char scratch2[SCRATCH_BUFSIZE2];
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t handler_lightOn(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("handler_lightOn");
|
||||||
|
printf("handler_lightOn uri:\n"); printf(req->uri); printf("\n");
|
||||||
|
Camera.LightOnOff(true);
|
||||||
|
const char* resp_str = (const char*) req->user_ctx;
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
return ESP_OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t handler_lightOff(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("handler_lightOff");
|
||||||
|
printf("handler_lightOff uri:\n"); printf(req->uri); printf("\n");
|
||||||
|
Camera.LightOnOff(false);
|
||||||
|
const char* resp_str = (const char*) req->user_ctx;
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
return ESP_OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t handler_capture(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("handler_capture");
|
||||||
|
int quality;
|
||||||
|
framesize_t res;
|
||||||
|
|
||||||
|
Camera.GetCameraParameter(req, quality, res);
|
||||||
|
printf("Size: %d", res); printf(" Quality: %d\n", quality);
|
||||||
|
Camera.SetQualitySize(quality, res);
|
||||||
|
|
||||||
|
esp_err_t ressult;
|
||||||
|
ressult = Camera.CaptureToHTTP(req);
|
||||||
|
return ressult;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t handler_capture_with_ligth(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("handler_capture_with_ligth");
|
||||||
|
char _query[100];
|
||||||
|
char _delay[10];
|
||||||
|
|
||||||
|
int quality;
|
||||||
|
framesize_t res;
|
||||||
|
int delay = 2500;
|
||||||
|
|
||||||
|
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Query: "); printf(_query); printf("\n");
|
||||||
|
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Delay: "); printf(_delay); printf("\n");
|
||||||
|
delay = atoi(_delay);
|
||||||
|
|
||||||
|
if (delay < 0)
|
||||||
|
delay = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Camera.GetCameraParameter(req, quality, res);
|
||||||
|
printf("Size: %d", res); printf(" Quality: %d\n", quality);
|
||||||
|
Camera.SetQualitySize(quality, res);
|
||||||
|
|
||||||
|
Camera.LightOnOff(true);
|
||||||
|
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||||
|
vTaskDelay( xDelay );
|
||||||
|
|
||||||
|
esp_err_t ressult;
|
||||||
|
ressult = Camera.CaptureToHTTP(req);
|
||||||
|
|
||||||
|
Camera.LightOnOff(false);
|
||||||
|
|
||||||
|
return ressult;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("handler_capture_save_to_file");
|
||||||
|
char _query[100];
|
||||||
|
char _delay[10];
|
||||||
|
int delay = 0;
|
||||||
|
char filename[100];
|
||||||
|
std::string fn = "/sdcard/";
|
||||||
|
|
||||||
|
|
||||||
|
int quality;
|
||||||
|
framesize_t res;
|
||||||
|
|
||||||
|
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Query: "); printf(_query); printf("\n");
|
||||||
|
if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK)
|
||||||
|
{
|
||||||
|
fn.append(filename);
|
||||||
|
printf("Filename: "); printf(fn.c_str()); printf("\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fn.append("noname.jpg");
|
||||||
|
|
||||||
|
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Delay: "); printf(_delay); printf("\n");
|
||||||
|
delay = atoi(_delay);
|
||||||
|
|
||||||
|
if (delay < 0)
|
||||||
|
delay = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fn.append("noname.jpg");
|
||||||
|
|
||||||
|
Camera.GetCameraParameter(req, quality, res);
|
||||||
|
printf("Size: %d", res); printf(" Quality: %d\n", quality);
|
||||||
|
Camera.SetQualitySize(quality, res);
|
||||||
|
|
||||||
|
esp_err_t ressult;
|
||||||
|
ressult = Camera.CaptureToFile(fn, delay);
|
||||||
|
|
||||||
|
const char* resp_str = (const char*) fn.c_str();
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
|
||||||
|
return ressult;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void register_server_camera_uri(httpd_handle_t server)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAGPARTCAMERA, "server_part_camera - Registering URI handlers");
|
||||||
|
|
||||||
|
httpd_uri_t camuri = { };
|
||||||
|
camuri.method = HTTP_GET;
|
||||||
|
|
||||||
|
camuri.uri = "/lighton";
|
||||||
|
camuri.handler = handler_lightOn;
|
||||||
|
camuri.user_ctx = (void*) "Light On";
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
|
camuri.uri = "/lightoff";
|
||||||
|
camuri.handler = handler_lightOff;
|
||||||
|
camuri.user_ctx = (void*) "Light Off";
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
|
camuri.uri = "/capture";
|
||||||
|
camuri.handler = handler_capture;
|
||||||
|
camuri.user_ctx = NULL;
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
|
camuri.uri = "/capture_with_flashlight";
|
||||||
|
camuri.handler = handler_capture_with_ligth;
|
||||||
|
camuri.user_ctx = NULL;
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
|
camuri.uri = "/save";
|
||||||
|
camuri.handler = handler_capture_save_to_file;
|
||||||
|
camuri.user_ctx = NULL;
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
}
|
||||||
14
code/lib/jomjol_controlcamera/server_camera.h
Normal file
14
code/lib/jomjol_controlcamera/server_camera.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef JOMJOL_CONTROLCAMERA_H
|
||||||
|
#define JOMJOL_CONTROLCAMERA_H
|
||||||
|
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#include <esp_http_server.h>
|
||||||
|
|
||||||
|
//#include "ClassControllCamera.h"
|
||||||
|
|
||||||
|
static const char *TAGPARTCAMERA = "server_camera";
|
||||||
|
|
||||||
|
void register_server_camera_uri(httpd_handle_t server);
|
||||||
|
|
||||||
|
#endif
|
||||||
539
code/lib/jomjol_fileserver_ota/server_file.cpp
Normal file
539
code/lib/jomjol_fileserver_ota/server_file.cpp
Normal file
@@ -0,0 +1,539 @@
|
|||||||
|
/* HTTP File Server Example
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this
|
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "server_file.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "esp_vfs.h"
|
||||||
|
#include "esp_spiffs.h"
|
||||||
|
#include "esp_http_server.h"
|
||||||
|
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
|
#include "server_help.h"
|
||||||
|
|
||||||
|
/* Max length a file path can have on storage */
|
||||||
|
// #define FILE_PATH_MAX (ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN)
|
||||||
|
#define FILE_PATH_MAX (255)
|
||||||
|
|
||||||
|
/* Max size of an individual file. Make sure this
|
||||||
|
* value is same as that set in upload_script.html */
|
||||||
|
#define MAX_FILE_SIZE (2000*1024) // 200 KB
|
||||||
|
#define MAX_FILE_SIZE_STR "2000KB"
|
||||||
|
|
||||||
|
/* Scratch buffer size */
|
||||||
|
#define SCRATCH_BUFSIZE 8192
|
||||||
|
|
||||||
|
struct file_server_data {
|
||||||
|
/* Base path of file storage */
|
||||||
|
char base_path[ESP_VFS_PATH_MAX + 1];
|
||||||
|
|
||||||
|
/* Scratch buffer for temporary storage during file transfer */
|
||||||
|
char scratch[SCRATCH_BUFSIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *TAG = "file_server";
|
||||||
|
|
||||||
|
/* Handler to redirect incoming GET request for /index.html to /
|
||||||
|
* This can be overridden by uploading file with same name */
|
||||||
|
static esp_err_t index_html_get_handler(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
httpd_resp_set_status(req, "307 Temporary Redirect");
|
||||||
|
httpd_resp_set_hdr(req, "Location", "/");
|
||||||
|
httpd_resp_send(req, NULL, 0); // Response body can be empty
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handler to respond with an icon file embedded in flash.
|
||||||
|
* Browsers expect to GET website icon at URI /favicon.ico.
|
||||||
|
* This can be overridden by uploading file with same name */
|
||||||
|
static esp_err_t favicon_get_handler(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
extern const unsigned char favicon_ico_start[] asm("_binary_favicon_ico_start");
|
||||||
|
extern const unsigned char favicon_ico_end[] asm("_binary_favicon_ico_end");
|
||||||
|
const size_t favicon_ico_size = (favicon_ico_end - favicon_ico_start);
|
||||||
|
httpd_resp_set_type(req, "image/x-icon");
|
||||||
|
httpd_resp_send(req, (const char *)favicon_ico_start, favicon_ico_size);
|
||||||
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send HTTP response with a run-time generated html consisting of
|
||||||
|
* a list of all files and folders under the requested path.
|
||||||
|
* In case of SPIFFS this returns empty list when path is any
|
||||||
|
* string other than '/', since SPIFFS doesn't support directories */
|
||||||
|
static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath)
|
||||||
|
{
|
||||||
|
char entrypath[FILE_PATH_MAX];
|
||||||
|
char entrysize[16];
|
||||||
|
const char *entrytype;
|
||||||
|
|
||||||
|
struct dirent *entry;
|
||||||
|
struct stat entry_stat;
|
||||||
|
|
||||||
|
char dirpath_corrected[FILE_PATH_MAX];
|
||||||
|
strcpy(dirpath_corrected, dirpath);
|
||||||
|
|
||||||
|
file_server_data * server_data = (file_server_data *) req->user_ctx;
|
||||||
|
if ((strlen(dirpath_corrected)-1) > strlen(server_data->base_path)) // if dirpath is not mountpoint, the last "\" needs to be removed
|
||||||
|
dirpath_corrected[strlen(dirpath_corrected)-1] = '\0';
|
||||||
|
|
||||||
|
DIR *dir = opendir(dirpath_corrected);
|
||||||
|
|
||||||
|
const size_t dirpath_len = strlen(dirpath);
|
||||||
|
printf("Dirpath: <%s>, Pathlength: %d\n", dirpath, dirpath_len);
|
||||||
|
|
||||||
|
/* Retrieve the base path of file storage to construct the full path */
|
||||||
|
strlcpy(entrypath, dirpath, sizeof(entrypath));
|
||||||
|
printf("entrypath: <%s>\n", entrypath);
|
||||||
|
|
||||||
|
if (!dir) {
|
||||||
|
ESP_LOGE(TAG, "Failed to stat dir : %s", dirpath);
|
||||||
|
/* Respond with 404 Not Found */
|
||||||
|
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Directory does not exist");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send HTML file header */
|
||||||
|
httpd_resp_sendstr_chunk(req, "<!DOCTYPE html><html><body>");
|
||||||
|
|
||||||
|
/* Get handle to embedded file upload script */
|
||||||
|
extern const unsigned char upload_script_start[] asm("_binary_upload_script_html_start");
|
||||||
|
extern const unsigned char upload_script_end[] asm("_binary_upload_script_html_end");
|
||||||
|
const size_t upload_script_size = (upload_script_end - upload_script_start);
|
||||||
|
|
||||||
|
/* Add file upload form and script which on execution sends a POST request to /upload */
|
||||||
|
httpd_resp_send_chunk(req, (const char *)upload_script_start, upload_script_size);
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
/*
|
||||||
|
FILE *fd = fopen("/sdcard/html/file_server_upload_script.html", "r");
|
||||||
|
char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
|
||||||
|
size_t chunksize;
|
||||||
|
do {
|
||||||
|
chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);
|
||||||
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
|
fclose(fd);
|
||||||
|
ESP_LOGE(TAG, "File sending failed!");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
} while (chunksize != 0);
|
||||||
|
fclose(fd);
|
||||||
|
ESP_LOGI(TAG, "File sending complete");
|
||||||
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
|
*/
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
/* Send file-list table definition and column labels */
|
||||||
|
httpd_resp_sendstr_chunk(req,
|
||||||
|
"<table class=\"fixed\" border=\"1\">"
|
||||||
|
"<col width=\"800px\" /><col width=\"300px\" /><col width=\"300px\" /><col width=\"100px\" />"
|
||||||
|
"<thead><tr><th>Name</th><th>Type</th><th>Size (Bytes)</th><th>Delete</th></tr></thead>"
|
||||||
|
"<tbody>");
|
||||||
|
|
||||||
|
/* Iterate over all files / folders and fetch their names and sizes */
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
entrytype = (entry->d_type == DT_DIR ? "directory" : "file");
|
||||||
|
|
||||||
|
strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);
|
||||||
|
printf("Entrypath: %s\n", entrypath);
|
||||||
|
if (stat(entrypath, &entry_stat) == -1) {
|
||||||
|
ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entry->d_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sprintf(entrysize, "%ld", entry_stat.st_size);
|
||||||
|
ESP_LOGI(TAG, "Found %s : %s (%s bytes)", entrytype, entry->d_name, entrysize);
|
||||||
|
|
||||||
|
/* Send chunk of HTML file containing table entries with file name and size */
|
||||||
|
httpd_resp_sendstr_chunk(req, "<tr><td><a href=\"");
|
||||||
|
httpd_resp_sendstr_chunk(req, req->uri);
|
||||||
|
httpd_resp_sendstr_chunk(req, entry->d_name);
|
||||||
|
if (entry->d_type == DT_DIR) {
|
||||||
|
httpd_resp_sendstr_chunk(req, "/");
|
||||||
|
}
|
||||||
|
httpd_resp_sendstr_chunk(req, "\">");
|
||||||
|
httpd_resp_sendstr_chunk(req, entry->d_name);
|
||||||
|
httpd_resp_sendstr_chunk(req, "</a></td><td>");
|
||||||
|
httpd_resp_sendstr_chunk(req, entrytype);
|
||||||
|
httpd_resp_sendstr_chunk(req, "</td><td>");
|
||||||
|
httpd_resp_sendstr_chunk(req, entrysize);
|
||||||
|
httpd_resp_sendstr_chunk(req, "</td><td>");
|
||||||
|
httpd_resp_sendstr_chunk(req, "<form method=\"post\" action=\"/delete");
|
||||||
|
httpd_resp_sendstr_chunk(req, req->uri + strlen("/fileserver"));
|
||||||
|
httpd_resp_sendstr_chunk(req, entry->d_name);
|
||||||
|
httpd_resp_sendstr_chunk(req, "\"><button type=\"submit\">Delete</button></form>");
|
||||||
|
httpd_resp_sendstr_chunk(req, "</td></tr>\n");
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
/* Finish the file list table */
|
||||||
|
httpd_resp_sendstr_chunk(req, "</tbody></table>");
|
||||||
|
|
||||||
|
/* Send remaining chunk of HTML file to complete it */
|
||||||
|
httpd_resp_sendstr_chunk(req, "</body></html>");
|
||||||
|
|
||||||
|
/* Send empty chunk to signal HTTP response completion */
|
||||||
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IS_FILE_EXT(filename, ext) \
|
||||||
|
(strcasecmp(&filename[strlen(filename) - sizeof(ext) + 1], ext) == 0)
|
||||||
|
|
||||||
|
|
||||||
|
/* Handler to download a file kept on the server */
|
||||||
|
static esp_err_t download_get_handler(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("download_get_handler");
|
||||||
|
char filepath[FILE_PATH_MAX];
|
||||||
|
FILE *fd = NULL;
|
||||||
|
struct stat file_stat;
|
||||||
|
printf("uri: %s\n", req->uri);
|
||||||
|
|
||||||
|
const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
|
||||||
|
req->uri + sizeof("/fileserver") - 1, sizeof(filepath));
|
||||||
|
|
||||||
|
printf("1 uri: %s, filename: %s, filepath: %s\n", req->uri, filename, filepath);
|
||||||
|
|
||||||
|
// filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
|
||||||
|
// req->uri, sizeof(filepath));
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
ESP_LOGE(TAG, "Filename is too long");
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If name has trailing '/', respond with directory contents */
|
||||||
|
if (filename[strlen(filename) - 1] == '/') {
|
||||||
|
return http_resp_dir_html(req, filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(filepath, &file_stat) == -1) {
|
||||||
|
/* If file not present on SPIFFS check if URI
|
||||||
|
* corresponds to one of the hardcoded paths */
|
||||||
|
ESP_LOGE(TAG, "Failed to stat file : %s", filepath);
|
||||||
|
/* Respond with 404 Not Found */
|
||||||
|
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = fopen(filepath, "r");
|
||||||
|
if (!fd) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", filename, file_stat.st_size);
|
||||||
|
set_content_type_from_file(req, filename);
|
||||||
|
|
||||||
|
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||||
|
char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
|
||||||
|
size_t chunksize;
|
||||||
|
do {
|
||||||
|
/* Read file in chunks into the scratch buffer */
|
||||||
|
chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);
|
||||||
|
|
||||||
|
/* Send the buffer contents as HTTP response chunk */
|
||||||
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
|
fclose(fd);
|
||||||
|
ESP_LOGE(TAG, "File sending failed!");
|
||||||
|
/* Abort sending file */
|
||||||
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep looping till the whole file is sent */
|
||||||
|
} while (chunksize != 0);
|
||||||
|
|
||||||
|
/* Close file after sending complete */
|
||||||
|
fclose(fd);
|
||||||
|
ESP_LOGI(TAG, "File sending complete");
|
||||||
|
|
||||||
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handler to upload a file onto the server */
|
||||||
|
static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("upload_post_handler");
|
||||||
|
char filepath[FILE_PATH_MAX];
|
||||||
|
FILE *fd = NULL;
|
||||||
|
struct stat file_stat;
|
||||||
|
|
||||||
|
/* Skip leading "/upload" from URI to get filename */
|
||||||
|
/* Note sizeof() counts NULL termination hence the -1 */
|
||||||
|
const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
|
||||||
|
req->uri + sizeof("/upload") - 1, sizeof(filepath));
|
||||||
|
if (!filename) {
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filename cannot have a trailing '/' */
|
||||||
|
if (filename[strlen(filename) - 1] == '/') {
|
||||||
|
ESP_LOGE(TAG, "Invalid filename : %s", filename);
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(filepath, &file_stat) == 0) {
|
||||||
|
ESP_LOGE(TAG, "File already exists : %s", filepath);
|
||||||
|
/* Respond with 400 Bad Request */
|
||||||
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File cannot be larger than a limit */
|
||||||
|
if (req->content_len > MAX_FILE_SIZE) {
|
||||||
|
ESP_LOGE(TAG, "File too large : %d bytes", req->content_len);
|
||||||
|
/* Respond with 400 Bad Request */
|
||||||
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
|
||||||
|
"File size must be less than "
|
||||||
|
MAX_FILE_SIZE_STR "!");
|
||||||
|
/* Return failure to close underlying connection else the
|
||||||
|
* incoming file content will keep the socket busy */
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = fopen(filepath, "w");
|
||||||
|
if (!fd) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create file : %s", filepath);
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Receiving file : %s...", filename);
|
||||||
|
|
||||||
|
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||||
|
char *buf = ((struct file_server_data *)req->user_ctx)->scratch;
|
||||||
|
int received;
|
||||||
|
|
||||||
|
/* Content length of the request gives
|
||||||
|
* the size of the file being uploaded */
|
||||||
|
int remaining = req->content_len;
|
||||||
|
|
||||||
|
while (remaining > 0) {
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Remaining size : %d", remaining);
|
||||||
|
/* Receive the file part by part into a buffer */
|
||||||
|
if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) {
|
||||||
|
if (received == HTTPD_SOCK_ERR_TIMEOUT) {
|
||||||
|
/* Retry if timeout occurred */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In case of unrecoverable error,
|
||||||
|
* close and delete the unfinished file*/
|
||||||
|
fclose(fd);
|
||||||
|
unlink(filepath);
|
||||||
|
|
||||||
|
ESP_LOGE(TAG, "File reception failed!");
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write buffer content to file on storage */
|
||||||
|
if (received && (received != fwrite(buf, 1, received, fd))) {
|
||||||
|
/* Couldn't write everything to file!
|
||||||
|
* Storage may be full? */
|
||||||
|
fclose(fd);
|
||||||
|
unlink(filepath);
|
||||||
|
|
||||||
|
ESP_LOGE(TAG, "File write failed!");
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep track of remaining size of
|
||||||
|
* the file left to be uploaded */
|
||||||
|
remaining -= received;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file upon upload completion */
|
||||||
|
fclose(fd);
|
||||||
|
ESP_LOGI(TAG, "File reception complete");
|
||||||
|
|
||||||
|
std::string directory = std::string(filepath);
|
||||||
|
size_t zw = directory.find("/");
|
||||||
|
size_t found = zw;
|
||||||
|
while (zw != std::string::npos)
|
||||||
|
{
|
||||||
|
zw = directory.find("/", found+1);
|
||||||
|
if (zw != std::string::npos)
|
||||||
|
found = zw;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
||||||
|
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
||||||
|
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||||
|
printf("Directory danach: %s\n", directory.c_str());
|
||||||
|
|
||||||
|
directory = "/fileserver" + directory;
|
||||||
|
printf("Directory danach: %s\n", directory.c_str());
|
||||||
|
|
||||||
|
/* Redirect onto root to see the updated file list */
|
||||||
|
httpd_resp_set_status(req, "303 See Other");
|
||||||
|
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
||||||
|
|
||||||
|
/* Redirect onto root to see the updated file list */
|
||||||
|
httpd_resp_set_status(req, "303 See Other");
|
||||||
|
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
||||||
|
httpd_resp_sendstr(req, "File uploaded successfully");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handler to delete a file from the server */
|
||||||
|
static esp_err_t delete_post_handler(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("delete_post_handler");
|
||||||
|
char filepath[FILE_PATH_MAX];
|
||||||
|
struct stat file_stat;
|
||||||
|
|
||||||
|
|
||||||
|
/* Skip leading "/delete" from URI to get filename */
|
||||||
|
/* Note sizeof() counts NULL termination hence the -1 */
|
||||||
|
const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
|
||||||
|
req->uri + sizeof("/delete") - 1, sizeof(filepath));
|
||||||
|
if (!filename) {
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filename cannot have a trailing '/' */
|
||||||
|
if (filename[strlen(filename) - 1] == '/') {
|
||||||
|
ESP_LOGE(TAG, "Invalid filename : %s", filename);
|
||||||
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(filepath, &file_stat) == -1) {
|
||||||
|
ESP_LOGE(TAG, "File does not exist : %s", filename);
|
||||||
|
/* Respond with 400 Bad Request */
|
||||||
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Deleting file : %s", filename);
|
||||||
|
/* Delete file */
|
||||||
|
unlink(filepath);
|
||||||
|
|
||||||
|
std::string directory = std::string(filepath);
|
||||||
|
size_t zw = directory.find("/");
|
||||||
|
size_t found = zw;
|
||||||
|
while (zw != std::string::npos)
|
||||||
|
{
|
||||||
|
zw = directory.find("/", found+1);
|
||||||
|
if (zw != std::string::npos)
|
||||||
|
found = zw;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
||||||
|
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
||||||
|
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||||
|
printf("Directory danach: %s\n", directory.c_str());
|
||||||
|
|
||||||
|
directory = "/fileserver" + directory;
|
||||||
|
printf("Directory danach: %s\n", directory.c_str());
|
||||||
|
|
||||||
|
/* Redirect onto root to see the updated file list */
|
||||||
|
httpd_resp_set_status(req, "303 See Other");
|
||||||
|
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
||||||
|
httpd_resp_sendstr(req, "File deleted successfully");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||||
|
{
|
||||||
|
static struct file_server_data *server_data = NULL;
|
||||||
|
|
||||||
|
/* Validate file storage base path */
|
||||||
|
if (!base_path) {
|
||||||
|
// if (!base_path || strcmp(base_path, "/spiffs") != 0) {
|
||||||
|
ESP_LOGE(TAG, "File server base_path not set");
|
||||||
|
// return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_data) {
|
||||||
|
ESP_LOGE(TAG, "File server already started");
|
||||||
|
// return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory for server data */
|
||||||
|
server_data = (file_server_data *) calloc(1, sizeof(struct file_server_data));
|
||||||
|
if (!server_data) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for server data");
|
||||||
|
// return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
strlcpy(server_data->base_path, base_path,
|
||||||
|
sizeof(server_data->base_path));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* URI handler for getting uploaded files */
|
||||||
|
// char zw[sizeof(serverprefix)+1];
|
||||||
|
// strcpy(zw, serverprefix);
|
||||||
|
// zw[strlen(serverprefix)] = '*';
|
||||||
|
// zw[strlen(serverprefix)+1] = '\0';
|
||||||
|
// printf("zw: %s\n", zw);
|
||||||
|
httpd_uri_t file_download = {
|
||||||
|
.uri = "/fileserver*", // Match all URIs of type /path/to/file
|
||||||
|
.method = HTTP_GET,
|
||||||
|
.handler = download_get_handler,
|
||||||
|
.user_ctx = server_data // Pass server data as context
|
||||||
|
};
|
||||||
|
httpd_register_uri_handler(server, &file_download);
|
||||||
|
|
||||||
|
/* URI handler for uploading files to server */
|
||||||
|
httpd_uri_t file_upload = {
|
||||||
|
.uri = "/upload/*", // Match all URIs of type /upload/path/to/file
|
||||||
|
.method = HTTP_POST,
|
||||||
|
.handler = upload_post_handler,
|
||||||
|
.user_ctx = server_data // Pass server data as context
|
||||||
|
};
|
||||||
|
httpd_register_uri_handler(server, &file_upload);
|
||||||
|
|
||||||
|
/* URI handler for deleting files from server */
|
||||||
|
httpd_uri_t file_delete = {
|
||||||
|
.uri = "/delete/*", // Match all URIs of type /delete/path/to/file
|
||||||
|
.method = HTTP_POST,
|
||||||
|
.handler = delete_post_handler,
|
||||||
|
.user_ctx = server_data // Pass server data as context
|
||||||
|
};
|
||||||
|
httpd_register_uri_handler(server, &file_delete);
|
||||||
|
|
||||||
|
}
|
||||||
3
code/lib/jomjol_fileserver_ota/server_file.h
Normal file
3
code/lib/jomjol_fileserver_ota/server_file.h
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#include <esp_http_server.h>
|
||||||
|
|
||||||
|
void register_server_file_uri(httpd_handle_t server, const char *base_path);
|
||||||
408
code/lib/jomjol_fileserver_ota/server_ota.cpp
Normal file
408
code/lib/jomjol_fileserver_ota/server_ota.cpp
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
#include "server_ota.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include <esp_int_wdt.h>
|
||||||
|
#include <esp_task_wdt.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_event_loop.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_ota_ops.h"
|
||||||
|
#include "esp_http_client.h"
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
#include "nvs.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "protocol_examples_common.h"
|
||||||
|
#include "errno.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "server_tflite.h"
|
||||||
|
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define BUFFSIZE 1024
|
||||||
|
#define HASH_LEN 32 /* SHA-256 digest length */
|
||||||
|
|
||||||
|
|
||||||
|
/*an ota data write buffer ready to write to the flash*/
|
||||||
|
static char ota_write_data[BUFFSIZE + 1] = { 0 };
|
||||||
|
|
||||||
|
|
||||||
|
#define OTA_URL_SIZE 256
|
||||||
|
|
||||||
|
|
||||||
|
static void infinite_loop(void)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
ESP_LOGI(TAGPARTOTA, "When a new firmware is available on the server, press the reset button to download it");
|
||||||
|
while(1) {
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Waiting for a new firmware ... %d", ++i);
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static bool ota_example_task(std::string fn)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
|
||||||
|
esp_ota_handle_t update_handle = 0 ;
|
||||||
|
const esp_partition_t *update_partition = NULL;
|
||||||
|
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Starting OTA example");
|
||||||
|
|
||||||
|
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
|
||||||
|
if (configured != running) {
|
||||||
|
ESP_LOGW(TAGPARTOTA, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
|
||||||
|
configured->address, running->address);
|
||||||
|
ESP_LOGW(TAGPARTOTA, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Running partition type %d subtype %d (offset 0x%08x)",
|
||||||
|
running->type, running->subtype, running->address);
|
||||||
|
|
||||||
|
|
||||||
|
update_partition = esp_ota_get_next_update_partition(NULL);
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Writing to partition subtype %d at offset 0x%x",
|
||||||
|
update_partition->subtype, update_partition->address);
|
||||||
|
// assert(update_partition != NULL);
|
||||||
|
|
||||||
|
int binary_file_length = 0;
|
||||||
|
|
||||||
|
// deal with all receive packet
|
||||||
|
bool image_header_was_checked = false;
|
||||||
|
|
||||||
|
int data_read;
|
||||||
|
|
||||||
|
FILE* f = fopen(fn.c_str(), "rb"); // vorher nur "r"
|
||||||
|
data_read = fread(ota_write_data, 1, BUFFSIZE, f);
|
||||||
|
|
||||||
|
while (data_read > 0) {
|
||||||
|
if (data_read < 0) {
|
||||||
|
ESP_LOGE(TAGPARTOTA, "Error: SSL data read error");
|
||||||
|
return false;
|
||||||
|
} else if (data_read > 0) {
|
||||||
|
if (image_header_was_checked == false) {
|
||||||
|
esp_app_desc_t new_app_info;
|
||||||
|
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
|
||||||
|
// check current version with downloading
|
||||||
|
memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
|
||||||
|
ESP_LOGI(TAGPARTOTA, "New firmware version: %s", new_app_info.version);
|
||||||
|
|
||||||
|
esp_app_desc_t running_app_info;
|
||||||
|
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Running firmware version: %s", running_app_info.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
|
||||||
|
esp_app_desc_t invalid_app_info;
|
||||||
|
if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Last invalid firmware version: %s", invalid_app_info.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check current version with last invalid partition
|
||||||
|
if (last_invalid_app != NULL) {
|
||||||
|
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||||
|
ESP_LOGW(TAGPARTOTA, "New version is the same as invalid version.");
|
||||||
|
ESP_LOGW(TAGPARTOTA, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
|
||||||
|
ESP_LOGW(TAGPARTOTA, "The firmware has been rolled back to the previous version.");
|
||||||
|
infinite_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||||
|
ESP_LOGW(TAGPARTOTA, "Current running version is the same as a new. We will not continue the update.");
|
||||||
|
infinite_loop();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
image_header_was_checked = true;
|
||||||
|
|
||||||
|
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAGPARTOTA, "esp_ota_begin failed (%s)", esp_err_to_name(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAGPARTOTA, "esp_ota_begin succeeded");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAGPARTOTA, "received package is not fit len");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
binary_file_length += data_read;
|
||||||
|
ESP_LOGD(TAGPARTOTA, "Written image length %d", binary_file_length);
|
||||||
|
} else if (data_read == 0) {
|
||||||
|
//
|
||||||
|
// * As esp_http_client_read never returns negative error code, we rely on
|
||||||
|
// * `errno` to check for underlying transport connectivity closure if any
|
||||||
|
//
|
||||||
|
if (errno == ECONNRESET || errno == ENOTCONN) {
|
||||||
|
ESP_LOGE(TAGPARTOTA, "Connection closed, errno = %d", errno);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data_read = fread(ota_write_data, 1, BUFFSIZE, f);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Total Write binary data length: %d", binary_file_length);
|
||||||
|
|
||||||
|
err = esp_ota_end(update_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||||
|
ESP_LOGE(TAGPARTOTA, "Image validation failed, image is corrupted");
|
||||||
|
}
|
||||||
|
ESP_LOGE(TAGPARTOTA, "esp_ota_end failed (%s)!", esp_err_to_name(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = esp_ota_set_boot_partition(update_partition);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAGPARTOTA, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
|
||||||
|
|
||||||
|
}
|
||||||
|
// ESP_LOGI(TAGPARTOTA, "Prepare to restart system!");
|
||||||
|
// esp_restart();
|
||||||
|
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void print_sha256 (const uint8_t *image_hash, const char *label)
|
||||||
|
{
|
||||||
|
char hash_print[HASH_LEN * 2 + 1];
|
||||||
|
hash_print[HASH_LEN * 2] = 0;
|
||||||
|
for (int i = 0; i < HASH_LEN; ++i) {
|
||||||
|
sprintf(&hash_print[i * 2], "%02x", image_hash[i]);
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAGPARTOTA, "%s: %s", label, hash_print);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool diagnostic(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
gpio_config_t io_conf;
|
||||||
|
io_conf.intr_type = (gpio_int_type_t) GPIO_PIN_INTR_DISABLE;
|
||||||
|
io_conf.mode = GPIO_MODE_INPUT;
|
||||||
|
io_conf.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
|
||||||
|
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||||
|
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||||
|
gpio_config(&io_conf);
|
||||||
|
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Diagnostics (5 sec)...");
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
bool diagnostic_is_ok = gpio_get_level(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
|
||||||
|
|
||||||
|
gpio_reset_pin(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
|
||||||
|
|
||||||
|
return diagnostic_is_ok;
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckOTAUpdate(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Start CheckOTAUpdateCheck ...");
|
||||||
|
printf("Start CheckOTAUpdateCheck ...\n");
|
||||||
|
|
||||||
|
uint8_t sha_256[HASH_LEN] = { 0 };
|
||||||
|
esp_partition_t partition;
|
||||||
|
|
||||||
|
// get sha256 digest for the partition table
|
||||||
|
partition.address = ESP_PARTITION_TABLE_OFFSET;
|
||||||
|
partition.size = ESP_PARTITION_TABLE_MAX_LEN;
|
||||||
|
partition.type = ESP_PARTITION_TYPE_DATA;
|
||||||
|
esp_partition_get_sha256(&partition, sha_256);
|
||||||
|
print_sha256(sha_256, "SHA-256 for the partition table: ");
|
||||||
|
|
||||||
|
// get sha256 digest for bootloader
|
||||||
|
partition.address = ESP_BOOTLOADER_OFFSET;
|
||||||
|
partition.size = ESP_PARTITION_TABLE_OFFSET;
|
||||||
|
partition.type = ESP_PARTITION_TYPE_APP;
|
||||||
|
esp_partition_get_sha256(&partition, sha_256);
|
||||||
|
print_sha256(sha_256, "SHA-256 for bootloader: ");
|
||||||
|
|
||||||
|
// get sha256 digest for running partition
|
||||||
|
esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256);
|
||||||
|
print_sha256(sha_256, "SHA-256 for current firmware: ");
|
||||||
|
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
esp_ota_img_states_t ota_state;
|
||||||
|
esp_err_t res_stat_partition = esp_ota_get_state_partition(running, &ota_state);
|
||||||
|
switch (res_stat_partition)
|
||||||
|
{
|
||||||
|
case ESP_OK:
|
||||||
|
printf("CheckOTAUpdate Partition: ESP_OK\n");
|
||||||
|
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||||
|
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||||
|
// run diagnostic function ...
|
||||||
|
bool diagnostic_is_ok = diagnostic();
|
||||||
|
if (diagnostic_is_ok) {
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Diagnostics completed successfully! Continuing execution ...");
|
||||||
|
printf("Diagnostics completed successfully! Continuing execution ...\n");
|
||||||
|
esp_ota_mark_app_valid_cancel_rollback();
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAGPARTOTA, "Diagnostics failed! Start rollback to the previous version ...");
|
||||||
|
printf("Diagnostics failed! Start rollback to the previous version ...\n");
|
||||||
|
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESP_ERR_INVALID_ARG:
|
||||||
|
printf("CheckOTAUpdate Partition: ESP_ERR_INVALID_ARG\n");
|
||||||
|
break;
|
||||||
|
case ESP_ERR_NOT_SUPPORTED:
|
||||||
|
printf("CheckOTAUpdate Partition: ESP_ERR_NOT_SUPPORTED\n");
|
||||||
|
break;
|
||||||
|
case ESP_ERR_NOT_FOUND:
|
||||||
|
printf("CheckOTAUpdate Partition: ESP_ERR_NOT_FOUND\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||||
|
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||||
|
// run diagnostic function ...
|
||||||
|
bool diagnostic_is_ok = diagnostic();
|
||||||
|
if (diagnostic_is_ok) {
|
||||||
|
ESP_LOGI(TAGPARTOTA, "Diagnostics completed successfully! Continuing execution ...");
|
||||||
|
printf("Diagnostics completed successfully! Continuing execution ...\n");
|
||||||
|
esp_ota_mark_app_valid_cancel_rollback();
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAGPARTOTA, "Diagnostics failed! Start rollback to the previous version ...");
|
||||||
|
printf("Diagnostics failed! Start rollback to the previous version ...\n");
|
||||||
|
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t handler_ota_update(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("handler_ota_update");
|
||||||
|
char _query[100];
|
||||||
|
char _filename[30];
|
||||||
|
std::string fn = "/sdcard/firmware/";
|
||||||
|
bool _file_del = false;
|
||||||
|
|
||||||
|
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Query: "); printf(_query); printf("\n");
|
||||||
|
if (httpd_query_key_value(_query, "file", _filename, 30) == ESP_OK)
|
||||||
|
{
|
||||||
|
fn.append(_filename);
|
||||||
|
printf("File: "); printf(fn.c_str()); printf("\n");
|
||||||
|
}
|
||||||
|
if (httpd_query_key_value(_query, "delete", _filename, 30) == ESP_OK)
|
||||||
|
{
|
||||||
|
fn.append(_filename);
|
||||||
|
_file_del = true;
|
||||||
|
printf("Delete Default File: "); printf(fn.c_str()); printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_file_del)
|
||||||
|
{
|
||||||
|
struct stat file_stat;
|
||||||
|
if (stat(fn.c_str(), &file_stat) != -1) {
|
||||||
|
printf("Deleting file : %s", fn.c_str());
|
||||||
|
/* Delete file */
|
||||||
|
unlink(fn.c_str());
|
||||||
|
}
|
||||||
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* resp_str;
|
||||||
|
|
||||||
|
if (ota_example_task(fn))
|
||||||
|
{
|
||||||
|
resp_str = "Firmware Update Successfull!<br><br>You can restart now.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resp_str = "Error during Firmware Update!!!<br><br>Please check output of console.";
|
||||||
|
}
|
||||||
|
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
void hard_restart() {
|
||||||
|
esp_task_wdt_init(1,true);
|
||||||
|
esp_task_wdt_add(NULL);
|
||||||
|
while(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void task_reboot(void *pvParameter)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
esp_restart();
|
||||||
|
hard_restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelete(NULL); //Delete this task if it exits from the loop above
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t handler_reboot(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile("handler_reboot");
|
||||||
|
ESP_LOGI(TAGPARTOTA, "!!! System will restart within 5 sec!!!");
|
||||||
|
|
||||||
|
const char* resp_str = "!!! System will restart within 5 sec!!!";
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
|
||||||
|
KillTFliteTasks();
|
||||||
|
|
||||||
|
xTaskCreate(&task_reboot, "reboot", configMINIMAL_STACK_SIZE * 64, NULL, 10, NULL);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_server_ota_sdcard_uri(httpd_handle_t server)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAGPARTOTA, "server_ota - Registering URI handlers");
|
||||||
|
|
||||||
|
httpd_uri_t camuri = { };
|
||||||
|
camuri.method = HTTP_GET;
|
||||||
|
camuri.uri = "/ota";
|
||||||
|
camuri.handler = handler_ota_update;
|
||||||
|
camuri.user_ctx = (void*) "Do OTA";
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
|
camuri.method = HTTP_GET;
|
||||||
|
camuri.uri = "/reboot";
|
||||||
|
camuri.handler = handler_reboot;
|
||||||
|
camuri.user_ctx = (void*) "Reboot";
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
|
}
|
||||||
10
code/lib/jomjol_fileserver_ota/server_ota.h
Normal file
10
code/lib/jomjol_fileserver_ota/server_ota.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#include <esp_http_server.h>
|
||||||
|
|
||||||
|
//#include "ClassControllCamera.h"
|
||||||
|
|
||||||
|
static const char *TAGPARTOTA = "server_ota";
|
||||||
|
|
||||||
|
void register_server_ota_sdcard_uri(httpd_handle_t server);
|
||||||
|
void CheckOTAUpdate();
|
||||||
110
code/lib/jomjol_flowcontroll/ClassFlow.cpp
Normal file
110
code/lib/jomjol_flowcontroll/ClassFlow.cpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#include "ClassFlow.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ClassFlow::SetInitialParameter(void)
|
||||||
|
{
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<string> ClassFlow::ZerlegeZeile(std::string input)
|
||||||
|
{
|
||||||
|
std::vector<string> Output;
|
||||||
|
std::string delimiter = " =,";
|
||||||
|
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
size_t pos = findDelimiterPos(input, delimiter);
|
||||||
|
std::string token;
|
||||||
|
while (pos != std::string::npos) {
|
||||||
|
token = input.substr(0, pos);
|
||||||
|
token = trim(token, delimiter);
|
||||||
|
Output.push_back(token);
|
||||||
|
input.erase(0, pos + 1);
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
pos = findDelimiterPos(input, delimiter);
|
||||||
|
}
|
||||||
|
Output.push_back(input);
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlow::isNewParagraph(string input)
|
||||||
|
{
|
||||||
|
if (input[0] == '[')
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlow::GetNextParagraph(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph));
|
||||||
|
|
||||||
|
if (this->isNewParagraph(aktparamgraph))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ClassFlow::ClassFlow(void)
|
||||||
|
{
|
||||||
|
SetInitialParameter();
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlow::ClassFlow(std::vector<ClassFlow*> * lfc)
|
||||||
|
{
|
||||||
|
SetInitialParameter();
|
||||||
|
ListFlowControll = lfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlow::ReadParameter(FILE* pfile, string &aktparamgraph)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlow::doFlow(string time)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ClassFlow::getReadout()
|
||||||
|
{
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlow::getNextLine(FILE* pfile, string *rt)
|
||||||
|
{
|
||||||
|
char zw[1024];
|
||||||
|
if (pfile == NULL)
|
||||||
|
{
|
||||||
|
*rt = "";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fgets(zw, 1024, pfile);
|
||||||
|
printf("%s", zw);
|
||||||
|
if ((strlen(zw) == 0) && feof(pfile))
|
||||||
|
{
|
||||||
|
*rt = "";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*rt = zw;
|
||||||
|
*rt = trim(*rt);
|
||||||
|
while (zw[0] == '#' || (rt->size() == 0)) // Kommentarzeilen und Leerzeilen überspringen
|
||||||
|
{
|
||||||
|
fgets(zw, 1024, pfile);
|
||||||
|
printf("%s", zw);
|
||||||
|
if (feof(pfile))
|
||||||
|
{
|
||||||
|
*rt = "";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*rt = zw;
|
||||||
|
*rt = trim(*rt);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
40
code/lib/jomjol_flowcontroll/ClassFlow.h
Normal file
40
code/lib/jomjol_flowcontroll/ClassFlow.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
#include "CFindTemplate.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
struct HTMLInfo
|
||||||
|
{
|
||||||
|
float val;
|
||||||
|
std::string filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ClassFlow
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::vector<string> ZerlegeZeile(string input);
|
||||||
|
bool isNewParagraph(string input);
|
||||||
|
bool GetNextParagraph(FILE* pfile, string& aktparamgraph);
|
||||||
|
bool getNextLine(FILE* pfile, string* rt);
|
||||||
|
|
||||||
|
std::vector<ClassFlow*>* ListFlowControll;
|
||||||
|
|
||||||
|
virtual void SetInitialParameter(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClassFlow(void);
|
||||||
|
ClassFlow(std::vector<ClassFlow*> * lfc);
|
||||||
|
virtual bool ReadParameter(FILE* pfile, string &aktparamgraph);
|
||||||
|
virtual bool doFlow(string time);
|
||||||
|
virtual string getReadout();
|
||||||
|
virtual string name(){return "ClassFlow";};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
108
code/lib/jomjol_flowcontroll/ClassFlowAlignment.cpp
Normal file
108
code/lib/jomjol_flowcontroll/ClassFlowAlignment.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include "ClassFlowAlignment.h"
|
||||||
|
|
||||||
|
ClassFlowAlignment::ClassFlowAlignment()
|
||||||
|
{
|
||||||
|
initalrotate = 0;
|
||||||
|
anz_ref = 0;
|
||||||
|
suchex = 40;
|
||||||
|
suchey = 40;
|
||||||
|
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow*>* lfc)
|
||||||
|
{
|
||||||
|
initalrotate = 0;
|
||||||
|
anz_ref = 0;
|
||||||
|
suchex = 40;
|
||||||
|
suchey = 40;
|
||||||
|
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||||
|
ListFlowControll = lfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (aktparamgraph.compare("[Alignment]") != 0) // Paragraph passt nich zu MakeImage
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((zerlegt[0] == "InitalRotate") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->initalrotate = std::stod(zerlegt[1]);
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "SearchFieldX") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->suchex = std::stod(zerlegt[1]);
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "SearchFieldY") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->suchey = std::stod(zerlegt[1]);
|
||||||
|
}
|
||||||
|
if ((zerlegt.size() == 3) && (anz_ref < 2))
|
||||||
|
{
|
||||||
|
this->reffilename[anz_ref] = FormatFileName("/sdcard" + zerlegt[0]);
|
||||||
|
this->ref_x[anz_ref] = std::stod(zerlegt[1]);
|
||||||
|
this->ref_y[anz_ref] = std::stod(zerlegt[2]);
|
||||||
|
anz_ref++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowAlignment::doFlow(string time)
|
||||||
|
{
|
||||||
|
string input = namerawimage;
|
||||||
|
string output = "/sdcard/img_tmp/rot.jpg";
|
||||||
|
string output3 = "/sdcard/img_tmp/rot_roi.jpg";
|
||||||
|
string output2 = "/sdcard/img_tmp/alg.jpg";
|
||||||
|
string output4 = "/sdcard/img_tmp/alg_roi.jpg";
|
||||||
|
|
||||||
|
input = FormatFileName(input);
|
||||||
|
output = FormatFileName(output);
|
||||||
|
output2 = FormatFileName(output2);
|
||||||
|
|
||||||
|
CRotate *rt;
|
||||||
|
|
||||||
|
if (this->initalrotate != 0)
|
||||||
|
{
|
||||||
|
rt = new CRotate(input);
|
||||||
|
rt->Rotate(this->initalrotate);
|
||||||
|
rt->SaveToFile(output);
|
||||||
|
delete rt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CopyFile(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAlignAndCutImage *caic;
|
||||||
|
caic = new CAlignAndCutImage(output);
|
||||||
|
caic->Align(this->reffilename[0], this->ref_x[0], this->ref_y[0], this->reffilename[1], this->ref_x[1], this->ref_y[1], suchex, suchey, output3);
|
||||||
|
caic->SaveToFile(output2);
|
||||||
|
|
||||||
|
printf("Startwriting Output4:%s\n", output4.c_str());
|
||||||
|
if (output4.length() > 0)
|
||||||
|
{
|
||||||
|
caic->drawRect(ref_x[0], ref_y[0], caic->t0_dx, caic->t0_dy, 255, 0, 0, 2);
|
||||||
|
caic->drawRect(ref_x[1], ref_y[1], caic->t1_dx, caic->t1_dy, 255, 0, 0, 2);
|
||||||
|
caic->SaveToFile(output4);
|
||||||
|
printf("Write output4: %s\n", output4.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
delete caic;
|
||||||
|
|
||||||
|
// Align mit Templates
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
28
code/lib/jomjol_flowcontroll/ClassFlowAlignment.h
Normal file
28
code/lib/jomjol_flowcontroll/ClassFlowAlignment.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ClassFlow.h"
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class ClassFlowAlignment :
|
||||||
|
public ClassFlow
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
float initalrotate;
|
||||||
|
string reffilename[2];
|
||||||
|
int ref_x[2], ref_y[2];
|
||||||
|
int anz_ref;
|
||||||
|
int suchex, suchey;
|
||||||
|
string namerawimage;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClassFlowAlignment();
|
||||||
|
ClassFlowAlignment(std::vector<ClassFlow*>* lfc);
|
||||||
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
|
string name(){return "ClassFlowAlignment";};
|
||||||
|
};
|
||||||
|
|
||||||
257
code/lib/jomjol_flowcontroll/ClassFlowAnalog.cpp
Normal file
257
code/lib/jomjol_flowcontroll/ClassFlowAnalog.cpp
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
#include "ClassFlowAnalog.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
// #define OHNETFLITE
|
||||||
|
|
||||||
|
#ifndef OHNETFLITE
|
||||||
|
#include "CTfLiteClass.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ClassFlowAnalog::ClassFlowAnalog()
|
||||||
|
{
|
||||||
|
isLogImage = false;
|
||||||
|
string cnnmodelfile = "";
|
||||||
|
modelxsize = 1;
|
||||||
|
modelysize = 1;
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowAnalog::ClassFlowAnalog(std::vector<ClassFlow*>* lfc)
|
||||||
|
{
|
||||||
|
isLogImage = false;
|
||||||
|
string cnnmodelfile = "";
|
||||||
|
modelxsize = 1;
|
||||||
|
modelysize = 1;
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
ListFlowControll = lfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
string ClassFlowAnalog::getReadout()
|
||||||
|
{
|
||||||
|
int prev = -1;
|
||||||
|
string result = "";
|
||||||
|
for (int i = ROI.size() - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
prev = ZeigerEval(ROI[i]->result, prev);
|
||||||
|
result = std::to_string(prev) + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClassFlowAnalog::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||||
|
{
|
||||||
|
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||||
|
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||||
|
int ergebnis, ergebnis_rating;
|
||||||
|
|
||||||
|
if (ziffer_vorgaenger == -1)
|
||||||
|
return ergebnis_vorkomma % 10;
|
||||||
|
|
||||||
|
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
||||||
|
if (ergebnis_nachkomma >= 5)
|
||||||
|
ergebnis_rating-=5;
|
||||||
|
else
|
||||||
|
ergebnis_rating+=5;
|
||||||
|
ergebnis = (int) round(zahl);
|
||||||
|
if (ergebnis_rating < 0)
|
||||||
|
ergebnis-=1;
|
||||||
|
if (ergebnis == -1)
|
||||||
|
ergebnis+=10;
|
||||||
|
|
||||||
|
ergebnis = ergebnis % 10;
|
||||||
|
return ergebnis;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowAnalog::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
if (aktparamgraph.compare("[Analog]") != 0) // Paragraph passt nich zu MakeImage
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->isLogImage = true;
|
||||||
|
this->LogImageLocation = zerlegt[1];
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->cnnmodelfile = zerlegt[1];
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||||
|
{
|
||||||
|
this->modelxsize = std::stoi(zerlegt[1]);
|
||||||
|
this->modelysize = std::stoi(zerlegt[2]);
|
||||||
|
}
|
||||||
|
if (zerlegt.size() >= 5)
|
||||||
|
{
|
||||||
|
roianalog* neuroi = new roianalog;
|
||||||
|
neuroi->name = zerlegt[0];
|
||||||
|
neuroi->posx = std::stoi(zerlegt[1]);
|
||||||
|
neuroi->posy = std::stoi(zerlegt[2]);
|
||||||
|
neuroi->deltax = std::stoi(zerlegt[3]);
|
||||||
|
neuroi->deltay = std::stoi(zerlegt[4]);
|
||||||
|
ROI.push_back(neuroi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ClassFlowAnalog::doFlow(string time)
|
||||||
|
{
|
||||||
|
doAlignAndCut(time);
|
||||||
|
doNeuralNetwork(time);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowAnalog::doAlignAndCut(string time)
|
||||||
|
{
|
||||||
|
string input = "/sdcard/img_tmp/alg.jpg";
|
||||||
|
string input_roi = "/sdcard/img_tmp/alg_roi.jpg";
|
||||||
|
string ioresize = "/sdcard/img_tmp/resize.bmp";
|
||||||
|
string output;
|
||||||
|
string nm;
|
||||||
|
input = FormatFileName(input);
|
||||||
|
input_roi = FormatFileName(input_roi);
|
||||||
|
|
||||||
|
CResizeImage *rs;
|
||||||
|
CImageBasis *img_roi = NULL;
|
||||||
|
CAlignAndCutImage *caic = new CAlignAndCutImage(input);
|
||||||
|
|
||||||
|
if (input_roi.length() > 0)
|
||||||
|
img_roi = new CImageBasis(input_roi);
|
||||||
|
|
||||||
|
for (int i = 0; i < ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
printf("Analog %d - Align&Cut\n", i);
|
||||||
|
output = "/sdcard/img_tmp/" + ROI[i]->name + ".jpg";
|
||||||
|
output = FormatFileName(output);
|
||||||
|
caic->CutAndSave(output, ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay);
|
||||||
|
|
||||||
|
rs = new CResizeImage(output);
|
||||||
|
rs->Resize(modelxsize, modelysize);
|
||||||
|
ioresize = "/sdcard/img_tmp/ra" + std::to_string(i) + ".bmp";
|
||||||
|
ioresize = FormatFileName(ioresize);
|
||||||
|
rs->SaveToFile(ioresize);
|
||||||
|
delete rs;
|
||||||
|
|
||||||
|
if (img_roi)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
int g = 255;
|
||||||
|
int b = 0;
|
||||||
|
img_roi->drawRect(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, r, g, b, 1);
|
||||||
|
img_roi->drawCircle((int) (ROI[i]->posx + ROI[i]->deltax/2), (int) (ROI[i]->posy + ROI[i]->deltay/2), (int) (ROI[i]->deltax/2), r, g, b, 2);
|
||||||
|
img_roi->drawLine((int) (ROI[i]->posx + ROI[i]->deltax/2), (int) ROI[i]->posy, (int) (ROI[i]->posx + ROI[i]->deltax/2), (int) (ROI[i]->posy + ROI[i]->deltay), r, g, b, 2);
|
||||||
|
img_roi->drawLine((int) ROI[i]->posx, (int) (ROI[i]->posy + ROI[i]->deltay/2), (int) ROI[i]->posx + ROI[i]->deltax, (int) (ROI[i]->posy + ROI[i]->deltay/2), r, g, b, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete caic;
|
||||||
|
|
||||||
|
|
||||||
|
if (img_roi)
|
||||||
|
{
|
||||||
|
img_roi->SaveToFile(input_roi);
|
||||||
|
delete img_roi;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowAnalog::doNeuralNetwork(string time)
|
||||||
|
{
|
||||||
|
string input = "/sdcard/img_tmp/alg.jpg";
|
||||||
|
string ioresize = "/sdcard/img_tmp/resize.bmp";
|
||||||
|
string output;
|
||||||
|
string nm;
|
||||||
|
input = FormatFileName(input);
|
||||||
|
|
||||||
|
#ifndef OHNETFLITE
|
||||||
|
CTfLiteClass *tflite = new CTfLiteClass;
|
||||||
|
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||||
|
zwcnn = FormatFileName(zwcnn);
|
||||||
|
printf(zwcnn.c_str());printf("\n");
|
||||||
|
tflite->LoadModel(zwcnn);
|
||||||
|
tflite->MakeAllocate();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
printf("Analog %d - TfLite\n", i);
|
||||||
|
ioresize = "/sdcard/img_tmp/ra" + std::to_string(i) + ".bmp";
|
||||||
|
ioresize = FormatFileName(ioresize);
|
||||||
|
|
||||||
|
|
||||||
|
float f1, f2;
|
||||||
|
f1 = 0; f2 = 0;
|
||||||
|
|
||||||
|
#ifndef OHNETFLITE
|
||||||
|
tflite->LoadInputImage(ioresize);
|
||||||
|
tflite->Invoke();
|
||||||
|
|
||||||
|
f1 = tflite->GetOutputValue(0);
|
||||||
|
f2 = tflite->GetOutputValue(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
|
||||||
|
// printf("Result sin, cos, ziffer: %f, %f, %f\n", f1, f2, result);
|
||||||
|
ROI[i]->result = result * 10;
|
||||||
|
|
||||||
|
printf("Result Analog%i: %f\n", i, ROI[i]->result);
|
||||||
|
|
||||||
|
if (isLogImage)
|
||||||
|
{
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << std::fixed << std::setprecision(1) << ROI[i]->result;
|
||||||
|
std::string s = stream.str();
|
||||||
|
// std::snprintf(&s[0], s.size(), "%.2f", pi);
|
||||||
|
nm = "/sdcard" + LogImageLocation + "/" + s + "_" + ROI[i]->name + "_" + time + ".jpg";
|
||||||
|
nm = FormatFileName(nm);
|
||||||
|
output = "/sdcard/img_tmp/" + ROI[i]->name + ".jpg";
|
||||||
|
output = FormatFileName(output);
|
||||||
|
printf("Analog - save to file: %s\n", nm.c_str());
|
||||||
|
CopyFile(output, nm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef OHNETFLITE
|
||||||
|
delete tflite;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> ClassFlowAnalog::GetHTMLInfo()
|
||||||
|
{
|
||||||
|
std::vector<HTMLInfo*> result;
|
||||||
|
|
||||||
|
for (int i = 0; i < ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
HTMLInfo *zw = new HTMLInfo;
|
||||||
|
zw->filename = ROI[i]->name + ".jpg";
|
||||||
|
zw->val = ROI[i]->result;
|
||||||
|
result.push_back(zw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
36
code/lib/jomjol_flowcontroll/ClassFlowAnalog.h
Normal file
36
code/lib/jomjol_flowcontroll/ClassFlowAnalog.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ClassFlow.h"
|
||||||
|
// #include "CTfLiteClass.h"
|
||||||
|
|
||||||
|
struct roianalog {
|
||||||
|
int posx, posy, deltax, deltay;
|
||||||
|
float result;
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ClassFlowAnalog :
|
||||||
|
public ClassFlow
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
string LogImageLocation;
|
||||||
|
bool isLogImage;
|
||||||
|
std::vector<roianalog*> ROI;
|
||||||
|
string cnnmodelfile;
|
||||||
|
int modelxsize, modelysize;
|
||||||
|
int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClassFlowAnalog();
|
||||||
|
ClassFlowAnalog(std::vector<ClassFlow*>* lfc);
|
||||||
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
|
string getReadout();
|
||||||
|
|
||||||
|
bool doNeuralNetwork(string time);
|
||||||
|
bool doAlignAndCut(string time);
|
||||||
|
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||||
|
|
||||||
|
string name(){return "ClassFlowAnalog";};
|
||||||
|
};
|
||||||
|
|
||||||
212
code/lib/jomjol_flowcontroll/ClassFlowControll.cpp
Normal file
212
code/lib/jomjol_flowcontroll/ClassFlowControll.cpp
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
#include "ClassFlowControll.h"
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
|
if (FlowControll[i]->name().compare("ClassFlowDigit") == 0)
|
||||||
|
return ((ClassFlowDigit*) (FlowControll[i]))->GetHTMLInfo();
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
|
if (FlowControll[i]->name().compare("ClassFlowAnalog") == 0)
|
||||||
|
return ((ClassFlowAnalog*) (FlowControll[i]))->GetHTMLInfo();
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ClassFlowControll::SetInitialParameter(void)
|
||||||
|
{
|
||||||
|
AutoStart = false;
|
||||||
|
AutoIntervall = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowControll::isAutoStart(long &_intervall)
|
||||||
|
{
|
||||||
|
_intervall = AutoIntervall * 60 * 1000; // AutoIntervall: Minuten -> ms
|
||||||
|
return AutoStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||||
|
{
|
||||||
|
ClassFlow* cfc = NULL;
|
||||||
|
|
||||||
|
_type = trim(_type);
|
||||||
|
|
||||||
|
if (_type.compare("[MakeImage]") == 0)
|
||||||
|
cfc = new ClassFlowMakeImage(&FlowControll);
|
||||||
|
if (_type.compare("[Alignment]") == 0)
|
||||||
|
cfc = new ClassFlowAlignment(&FlowControll);
|
||||||
|
if (_type.compare("[Analog]") == 0)
|
||||||
|
cfc = new ClassFlowAnalog(&FlowControll);
|
||||||
|
if (_type.compare("[Digits]") == 0)
|
||||||
|
cfc = new ClassFlowDigit(&FlowControll);
|
||||||
|
if (_type.compare("[PostProcessing]") == 0)
|
||||||
|
{
|
||||||
|
cfc = new ClassFlowPostProcessing(&FlowControll);
|
||||||
|
flowpostprocessing = (ClassFlowPostProcessing*) cfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfc) // Wird nur angehangen, falls es nicht [AutoTimer] ist, denn dieses ist für FlowControll
|
||||||
|
FlowControll.push_back(cfc);
|
||||||
|
|
||||||
|
if (_type.compare("[AutoTimer]") == 0)
|
||||||
|
cfc = this;
|
||||||
|
|
||||||
|
return cfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassFlowControll::InitFlow(std::string config)
|
||||||
|
{
|
||||||
|
int aktFlow;
|
||||||
|
bool handeled;
|
||||||
|
string line;
|
||||||
|
|
||||||
|
flowpostprocessing = NULL;
|
||||||
|
|
||||||
|
ClassFlow* cfc;
|
||||||
|
FILE* pFile;
|
||||||
|
config = FormatFileName(config);
|
||||||
|
pFile = fopen(config.c_str(), "r");
|
||||||
|
|
||||||
|
line = "";
|
||||||
|
handeled = true;
|
||||||
|
|
||||||
|
|
||||||
|
char zw[1024];
|
||||||
|
if (pFile != NULL)
|
||||||
|
{
|
||||||
|
fgets(zw, 1024, pFile);
|
||||||
|
printf("%s", zw);
|
||||||
|
line = std::string(zw);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((line.size() > 0) && !(feof(pFile)))
|
||||||
|
{
|
||||||
|
cfc = CreateClassFlow(line);
|
||||||
|
if (cfc)
|
||||||
|
{
|
||||||
|
cfc->ReadParameter(pFile, line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fgets(zw, 1024, pFile);
|
||||||
|
printf("%s", zw);
|
||||||
|
line = std::string(zw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(pFile);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowControll::doFlow(string time)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
|
result = result && FlowControll[i]->doFlow(time);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ClassFlowControll::getReadout(bool _rawvalue = false)
|
||||||
|
{
|
||||||
|
if (flowpostprocessing)
|
||||||
|
return flowpostprocessing->getReadout();
|
||||||
|
|
||||||
|
string zw = "";
|
||||||
|
string result = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
|
{
|
||||||
|
zw = FlowControll[i]->getReadout();
|
||||||
|
if (zw.length() > 0)
|
||||||
|
{
|
||||||
|
if (result.length() == 0)
|
||||||
|
result = zw;
|
||||||
|
else
|
||||||
|
result = result + "\t" + zw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ClassFlowControll::GetPrevalue()
|
||||||
|
{
|
||||||
|
if (flowpostprocessing)
|
||||||
|
{
|
||||||
|
return flowpostprocessing->GetPreValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue)
|
||||||
|
{
|
||||||
|
float zw;
|
||||||
|
char* p;
|
||||||
|
|
||||||
|
_newvalue = trim(_newvalue);
|
||||||
|
// printf("Input UpdatePreValue: %s\n", _newvalue.c_str());
|
||||||
|
|
||||||
|
if (_newvalue.compare("0.0") == 0)
|
||||||
|
{
|
||||||
|
zw = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zw = strtof(_newvalue.c_str(), &p);
|
||||||
|
if (zw == 0)
|
||||||
|
return "- Error in String to Value Conversion!!! Must be of format value=123.456";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (flowpostprocessing)
|
||||||
|
{
|
||||||
|
flowpostprocessing->SavePreValue(zw);
|
||||||
|
return _newvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
if (aktparamgraph.compare("[AutoTimer]") != 0) // Paragraph passt nich zu MakeImage
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((zerlegt[0] == "AutoStart") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(zerlegt[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
AutoStart = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "Intervall") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
AutoIntervall = std::stof(zerlegt[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
41
code/lib/jomjol_flowcontroll/ClassFlowControll.h
Normal file
41
code/lib/jomjol_flowcontroll/ClassFlowControll.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "ClassFlow.h"
|
||||||
|
#include "ClassFlowMakeImage.h"
|
||||||
|
#include "ClassFlowAlignment.h"
|
||||||
|
#include "ClassFlowDigit.h"
|
||||||
|
#include "ClassFlowAnalog.h"
|
||||||
|
#include "ClassFlowPostProcessing.h"
|
||||||
|
|
||||||
|
|
||||||
|
class ClassFlowControll :
|
||||||
|
public ClassFlow
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::vector<ClassFlow*> FlowControll;
|
||||||
|
ClassFlowPostProcessing* flowpostprocessing;
|
||||||
|
ClassFlow* CreateClassFlow(std::string _type);
|
||||||
|
|
||||||
|
bool AutoStart;
|
||||||
|
float AutoIntervall;
|
||||||
|
void SetInitialParameter(void);
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
void InitFlow(std::string config);
|
||||||
|
bool doFlow(string time);
|
||||||
|
string getReadout(bool _rawvalue);
|
||||||
|
string UpdatePrevalue(std::string _newvalue);
|
||||||
|
string GetPrevalue();
|
||||||
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
|
||||||
|
bool isAutoStart(long &_intervall);
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> GetAllDigital();
|
||||||
|
std::vector<HTMLInfo*> GetAllAnalog();
|
||||||
|
|
||||||
|
string name(){return "ClassFlowControll";};
|
||||||
|
};
|
||||||
|
|
||||||
210
code/lib/jomjol_flowcontroll/ClassFlowDigit.cpp
Normal file
210
code/lib/jomjol_flowcontroll/ClassFlowDigit.cpp
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
#include "ClassFlowDigit.h"
|
||||||
|
|
||||||
|
//#include "CFindTemplate.h"
|
||||||
|
//#include "CTfLiteClass.h"
|
||||||
|
|
||||||
|
// #define OHNETFLITE
|
||||||
|
|
||||||
|
#ifndef OHNETFLITE
|
||||||
|
#include "CTfLiteClass.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// #include "bitmap_image.hpp"
|
||||||
|
|
||||||
|
ClassFlowDigit::ClassFlowDigit()
|
||||||
|
{
|
||||||
|
isLogImage = false;
|
||||||
|
string cnnmodelfile = "";
|
||||||
|
modelxsize = 1;
|
||||||
|
modelysize = 1;
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowDigit::ClassFlowDigit(std::vector<ClassFlow*>* lfc)
|
||||||
|
{
|
||||||
|
isLogImage = false;
|
||||||
|
string cnnmodelfile = "";
|
||||||
|
modelxsize = 1;
|
||||||
|
modelysize = 1;
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
ListFlowControll = lfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ClassFlowDigit::getReadout()
|
||||||
|
{
|
||||||
|
string rst = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
if (ROI[i]->resultklasse == 10)
|
||||||
|
rst = rst + "N";
|
||||||
|
else
|
||||||
|
rst = rst + std::to_string(ROI[i]->resultklasse);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rst;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowDigit::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
if (aktparamgraph.compare("[Digits]") != 0) // Paragraph passt nich zu MakeImage
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
isLogImage = true;
|
||||||
|
LogImageLocation = zerlegt[1];
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
cnnmodelfile = zerlegt[1];
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||||
|
{
|
||||||
|
modelxsize = std::stoi(zerlegt[1]);
|
||||||
|
modelysize = std::stoi(zerlegt[2]);
|
||||||
|
}
|
||||||
|
if (zerlegt.size() >= 5)
|
||||||
|
{
|
||||||
|
roi* neuroi = new roi;
|
||||||
|
neuroi->name = zerlegt[0];
|
||||||
|
neuroi->posx = std::stoi(zerlegt[1]);
|
||||||
|
neuroi->posy = std::stoi(zerlegt[2]);
|
||||||
|
neuroi->deltax = std::stoi(zerlegt[3]);
|
||||||
|
neuroi->deltay = std::stoi(zerlegt[4]);
|
||||||
|
ROI.push_back(neuroi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ClassFlowDigit::doFlow(string time)
|
||||||
|
{
|
||||||
|
doAlignAndCut(time);
|
||||||
|
doNeuralNetwork(time);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowDigit::doAlignAndCut(string time)
|
||||||
|
{
|
||||||
|
string input = "/sdcard/img_tmp/alg.jpg";
|
||||||
|
string input_roi = "/sdcard/img_tmp/alg_roi.jpg";
|
||||||
|
string ioresize = "/sdcard/img_tmp/resize.bmp";
|
||||||
|
string output;
|
||||||
|
string nm;
|
||||||
|
input = FormatFileName(input);
|
||||||
|
input_roi = FormatFileName(input_roi);
|
||||||
|
|
||||||
|
CResizeImage *rs;
|
||||||
|
CImageBasis *img_roi = NULL;
|
||||||
|
CAlignAndCutImage *caic = new CAlignAndCutImage(input);
|
||||||
|
|
||||||
|
if (input_roi.length() > 0)
|
||||||
|
img_roi = new CImageBasis(input_roi);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
printf("DigitalDigit %d - Align&Cut\n", i);
|
||||||
|
output = "/sdcard/img_tmp/" + ROI[i]->name + ".jpg";
|
||||||
|
output = FormatFileName(output);
|
||||||
|
caic->CutAndSave(output, ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay);
|
||||||
|
|
||||||
|
rs = new CResizeImage(output);
|
||||||
|
rs->Resize(modelxsize, modelysize);
|
||||||
|
ioresize = "/sdcard/img_tmp/rd" + std::to_string(i) + ".bmp";
|
||||||
|
ioresize = FormatFileName(ioresize);
|
||||||
|
rs->SaveToFile(ioresize);
|
||||||
|
delete rs;
|
||||||
|
|
||||||
|
if (img_roi)
|
||||||
|
{
|
||||||
|
img_roi->drawRect(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, 0, 0, 255, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete caic;
|
||||||
|
|
||||||
|
if (img_roi)
|
||||||
|
{
|
||||||
|
img_roi->SaveToFile(input_roi);
|
||||||
|
delete img_roi;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowDigit::doNeuralNetwork(string time)
|
||||||
|
{
|
||||||
|
string input = "/sdcard/img_tmp/alg.jpg";
|
||||||
|
string ioresize = "/sdcard/img_tmp/resize.bmp";
|
||||||
|
string output;
|
||||||
|
string nm;
|
||||||
|
input = FormatFileName(input);
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef OHNETFLITE
|
||||||
|
CTfLiteClass *tflite = new CTfLiteClass;
|
||||||
|
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||||
|
zwcnn = FormatFileName(zwcnn);
|
||||||
|
printf(zwcnn.c_str());printf("\n");
|
||||||
|
tflite->LoadModel(zwcnn);
|
||||||
|
tflite->MakeAllocate();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
printf("DigitalDigit %d - TfLite\n", i);
|
||||||
|
ioresize = "/sdcard/img_tmp/rd" + std::to_string(i) + ".bmp";
|
||||||
|
ioresize = FormatFileName(ioresize);
|
||||||
|
// printf("output: %s, ioresize: %s\n", output.c_str(), ioresize.c_str());
|
||||||
|
|
||||||
|
ROI[i]->resultklasse = 0;
|
||||||
|
#ifndef OHNETFLITE
|
||||||
|
ROI[i]->resultklasse = tflite->GetClassFromImage(ioresize);
|
||||||
|
#endif
|
||||||
|
printf("Result Digit%i: %d\n", i, ROI[i]->resultklasse);
|
||||||
|
|
||||||
|
if (isLogImage)
|
||||||
|
{
|
||||||
|
nm = "/sdcard" + LogImageLocation + "/" + std::to_string(ROI[i]->resultklasse) + "/" + time + "_" + ROI[i]->name + ".jpg";
|
||||||
|
output = "/sdcard/img_tmp/" + ROI[i]->name + ".jpg";
|
||||||
|
output = FormatFileName(output);
|
||||||
|
nm = FormatFileName(nm);
|
||||||
|
CopyFile(output, nm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef OHNETFLITE
|
||||||
|
delete tflite;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> ClassFlowDigit::GetHTMLInfo()
|
||||||
|
{
|
||||||
|
std::vector<HTMLInfo*> result;
|
||||||
|
|
||||||
|
for (int i = 0; i < ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
HTMLInfo *zw = new HTMLInfo;
|
||||||
|
zw->filename = ROI[i]->name + ".jpg";
|
||||||
|
zw->val = ROI[i]->resultklasse;
|
||||||
|
result.push_back(zw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
37
code/lib/jomjol_flowcontroll/ClassFlowDigit.h
Normal file
37
code/lib/jomjol_flowcontroll/ClassFlowDigit.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ClassFlow.h"
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct roi {
|
||||||
|
int posx, posy, deltax, deltay;
|
||||||
|
int resultklasse;
|
||||||
|
string name;
|
||||||
|
roi* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ClassFlowDigit :
|
||||||
|
public ClassFlow
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
string LogImageLocation;
|
||||||
|
bool isLogImage;
|
||||||
|
std::vector<roi*> ROI;
|
||||||
|
string cnnmodelfile;
|
||||||
|
int modelxsize, modelysize;
|
||||||
|
|
||||||
|
bool doNeuralNetwork(string time);
|
||||||
|
bool doAlignAndCut(string time);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClassFlowDigit();
|
||||||
|
ClassFlowDigit(std::vector<ClassFlow*>* lfc);
|
||||||
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
|
string getReadout();
|
||||||
|
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||||
|
|
||||||
|
string name(){return "ClassFlowDigit";};
|
||||||
|
};
|
||||||
|
|
||||||
154
code/lib/jomjol_flowcontroll/ClassFlowMakeImage.cpp
Normal file
154
code/lib/jomjol_flowcontroll/ClassFlowMakeImage.cpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#include "ClassFlowMakeImage.h"
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "CFindTemplate.h"
|
||||||
|
#include "ClassControllCamera.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t ClassFlowMakeImage::camera_capture(){
|
||||||
|
string nm = namerawimage;
|
||||||
|
Camera.CaptureToFile(nm);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassFlowMakeImage::takePictureWithFlash(int flashdauer)
|
||||||
|
{
|
||||||
|
string nm = namerawimage;
|
||||||
|
if (isImageSize && (ImageQuality > 0))
|
||||||
|
Camera.SetQualitySize(ImageQuality, ImageSize);
|
||||||
|
printf("Start CaptureFile\n");
|
||||||
|
Camera.CaptureToFile(nm, flashdauer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ClassFlowMakeImage::ClassFlowMakeImage()
|
||||||
|
{
|
||||||
|
isLogImage = false;
|
||||||
|
waitbeforepicture = 5;
|
||||||
|
isImageSize = false;
|
||||||
|
ImageQuality = -1;
|
||||||
|
TimeImageTaken = 0;
|
||||||
|
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowMakeImage::ClassFlowMakeImage(std::vector<ClassFlow*>* lfc)
|
||||||
|
{
|
||||||
|
isLogImage = false;
|
||||||
|
waitbeforepicture = 5;
|
||||||
|
isImageSize = false;
|
||||||
|
ImageQuality = -1;
|
||||||
|
TimeImageTaken = 0;
|
||||||
|
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||||
|
|
||||||
|
ListFlowControll = lfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (aktparamgraph.compare("[MakeImage]") != 0) // Paragraph passt nich zu MakeImage
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->isLogImage = true;
|
||||||
|
this->LogImageLocation = zerlegt[1];
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "ImageQuality") && (zerlegt.size() > 1))
|
||||||
|
this->ImageQuality = std::stod(zerlegt[1]);
|
||||||
|
if ((zerlegt[0] == "ImageSize") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
ImageSize = Camera.TextToFramesize(zerlegt[1].c_str());
|
||||||
|
isImageSize = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ClassFlowMakeImage::CopyFile(string input, string output)
|
||||||
|
{
|
||||||
|
input = FormatFileName(input);
|
||||||
|
output = FormatFileName(output);
|
||||||
|
input = namerawimage;
|
||||||
|
|
||||||
|
|
||||||
|
printf("Copy Input : %s\n", input.c_str());
|
||||||
|
printf("Copy Output: %s\n", output.c_str());
|
||||||
|
|
||||||
|
char cTemp;
|
||||||
|
FILE* fpSourceFile = fopen(input.c_str(), "rb");
|
||||||
|
FILE* fpTargetFile = fopen(output.c_str(), "wb");
|
||||||
|
|
||||||
|
if (fpSourceFile == NULL)
|
||||||
|
{
|
||||||
|
printf("fpSourceFile == NULL\n");
|
||||||
|
perror("Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fpTargetFile == NULL)
|
||||||
|
{
|
||||||
|
printf("fpTargetFile == NULL\n");
|
||||||
|
perror("Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
while (fread(&cTemp, 1, 1, fpSourceFile) == 1)
|
||||||
|
{
|
||||||
|
fwrite(&cTemp, 1, 1, fpTargetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close The Files
|
||||||
|
fclose(fpSourceFile);
|
||||||
|
fclose(fpTargetFile);
|
||||||
|
printf("Copy done\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ClassFlowMakeImage::doFlow(string zwtime)
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// TakeImage and Store into /image_tmp/raw.jpg TO BE DONE
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int flashdauer = (int) waitbeforepicture * 1000;
|
||||||
|
|
||||||
|
|
||||||
|
takePictureWithFlash(flashdauer);
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
|
|
||||||
|
if (this->isLogImage)
|
||||||
|
{
|
||||||
|
string nm = "/sdcard" + this->LogImageLocation + "/" + zwtime + ".jpg";
|
||||||
|
string input = "/sdcard/image_tmp/raw.jgp";
|
||||||
|
printf("loginput from: %s to: %s\n", input.c_str(), nm.c_str());
|
||||||
|
nm = FormatFileName(nm);
|
||||||
|
input = FormatFileName(input);
|
||||||
|
CopyFile(input, nm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t ClassFlowMakeImage::getTimeImageTaken()
|
||||||
|
{
|
||||||
|
return TimeImageTaken;
|
||||||
|
}
|
||||||
41
code/lib/jomjol_flowcontroll/ClassFlowMakeImage.h
Normal file
41
code/lib/jomjol_flowcontroll/ClassFlowMakeImage.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ClassFlow.h"
|
||||||
|
#include "ClassControllCamera.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
static const char* TAG2 = "example";
|
||||||
|
|
||||||
|
#define BLINK_GPIO GPIO_NUM_4
|
||||||
|
|
||||||
|
#define CAMERA_MODEL_AI_THINKER
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ClassFlowMakeImage :
|
||||||
|
public ClassFlow
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
string LogImageLocation;
|
||||||
|
bool isLogImage;
|
||||||
|
float waitbeforepicture;
|
||||||
|
framesize_t ImageSize;
|
||||||
|
bool isImageSize;
|
||||||
|
int ImageQuality;
|
||||||
|
time_t TimeImageTaken;
|
||||||
|
string namerawimage;
|
||||||
|
|
||||||
|
void CopyFile(string input, string output);
|
||||||
|
|
||||||
|
esp_err_t camera_capture();
|
||||||
|
void takePictureWithFlash(int flashdauer);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClassFlowMakeImage();
|
||||||
|
ClassFlowMakeImage(std::vector<ClassFlow*>* lfc);
|
||||||
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
|
time_t getTimeImageTaken();
|
||||||
|
string name(){return "ClassFlowMakeImage";};
|
||||||
|
};
|
||||||
|
|
||||||
302
code/lib/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
Normal file
302
code/lib/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
#include "ClassFlowPostProcessing.h"
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
#include "ClassFlowAnalog.h"
|
||||||
|
#include "ClassFlowDigit.h"
|
||||||
|
#include "ClassFlowMakeImage.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
string ClassFlowPostProcessing::GetPreValue()
|
||||||
|
{
|
||||||
|
return to_string(PreValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowPostProcessing::LoadPreValue(void)
|
||||||
|
{
|
||||||
|
FILE* pFile;
|
||||||
|
char zw[1024];
|
||||||
|
string zwtime, zwvalue;
|
||||||
|
|
||||||
|
pFile = fopen(FilePreValue.c_str(), "r");
|
||||||
|
if (pFile == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fgets(zw, 1024, pFile);
|
||||||
|
printf("%s", zw);
|
||||||
|
zwtime = trim(std::string(zw));
|
||||||
|
|
||||||
|
fgets(zw, 1024, pFile);
|
||||||
|
printf("%s", zw);
|
||||||
|
zwvalue = trim(std::string(zw));
|
||||||
|
PreValue = stof(zwvalue.c_str());
|
||||||
|
|
||||||
|
time_t tStart;
|
||||||
|
int yy, month, dd, hh, mm, ss;
|
||||||
|
struct tm whenStart;
|
||||||
|
|
||||||
|
sscanf(zwtime.c_str(), "%d-%d-%d_%d-%d-%d", &yy, &month, &dd, &hh, &mm, &ss);
|
||||||
|
whenStart.tm_year = yy - 1900;
|
||||||
|
whenStart.tm_mon = month - 1;
|
||||||
|
whenStart.tm_mday = dd;
|
||||||
|
whenStart.tm_hour = hh;
|
||||||
|
whenStart.tm_min = mm;
|
||||||
|
whenStart.tm_sec = ss;
|
||||||
|
whenStart.tm_isdst = -1;
|
||||||
|
|
||||||
|
tStart = mktime(&whenStart);
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
localtime(&now);
|
||||||
|
double difference = difftime(now, tStart);
|
||||||
|
difference /= 60;
|
||||||
|
if (difference > PreValueAgeStartup)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassFlowPostProcessing::SavePreValue(float value, string zwtime)
|
||||||
|
{
|
||||||
|
FILE* pFile;
|
||||||
|
PreValue = value;
|
||||||
|
|
||||||
|
pFile = fopen(FilePreValue.c_str(), "w");
|
||||||
|
|
||||||
|
if (strlen(zwtime.c_str()) == 0)
|
||||||
|
{
|
||||||
|
time_t rawtime;
|
||||||
|
struct tm* timeinfo;
|
||||||
|
char buffer[80];
|
||||||
|
|
||||||
|
time(&rawtime);
|
||||||
|
timeinfo = localtime(&rawtime);
|
||||||
|
|
||||||
|
strftime(buffer, 80, "%Y-%m-%d_%H-%M-%S", timeinfo);
|
||||||
|
|
||||||
|
zwtime = std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs(zwtime.c_str(), pFile);
|
||||||
|
fputs("\n", pFile);
|
||||||
|
|
||||||
|
fputs(to_string(value).c_str(), pFile);
|
||||||
|
fputs("\n", pFile);
|
||||||
|
|
||||||
|
fclose(pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ClassFlowPostProcessing::ClassFlowPostProcessing()
|
||||||
|
{
|
||||||
|
PreValueUse = false;
|
||||||
|
PreValueAgeStartup = 30;
|
||||||
|
AllowNegativeRates = false;
|
||||||
|
MaxRateValue = 0.1;
|
||||||
|
ErrorMessage = false;
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
PreValueOkay = false;
|
||||||
|
useMaxRateValue = false;
|
||||||
|
FilePreValue = FormatFileName("/sdcard/config/prevalue.ini");
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc)
|
||||||
|
{
|
||||||
|
PreValueUse = false;
|
||||||
|
PreValueAgeStartup = 30;
|
||||||
|
AllowNegativeRates = false;
|
||||||
|
MaxRateValue = 0.1;
|
||||||
|
ErrorMessage = false;
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
PreValueOkay = false;
|
||||||
|
useMaxRateValue = false;
|
||||||
|
FilePreValue = FormatFileName("/sdcard/config/prevalue.ini");
|
||||||
|
ListFlowControll = lfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
if (aktparamgraph.compare("[PostProcessing]") != 0) // Paragraph passt nich zu MakeImage
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((zerlegt[0] == "PreValueUse") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
if ((zerlegt[1] == "True") || (zerlegt[1] == "true"))
|
||||||
|
{
|
||||||
|
PreValueUse = true;
|
||||||
|
PreValueOkay = LoadPreValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "AllowNegativeRates") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
if ((zerlegt[1] == "True") || (zerlegt[1] == "true"))
|
||||||
|
AllowNegativeRates = true;
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "ErrorMessage") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
if ((zerlegt[1] == "True") || (zerlegt[1] == "true"))
|
||||||
|
ErrorMessage = true;
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "PreValueAgeStartup") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
PreValueAgeStartup = std::stoi(zerlegt[1]);
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "MaxRateValue") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
useMaxRateValue = true;
|
||||||
|
MaxRateValue = std::stof(zerlegt[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||||
|
{
|
||||||
|
string result = "";
|
||||||
|
string digit = "";
|
||||||
|
string analog = "";
|
||||||
|
bool isdigit = false;
|
||||||
|
bool isanalog = false;
|
||||||
|
string zw;
|
||||||
|
string error = "";
|
||||||
|
time_t imagetime = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||||
|
{
|
||||||
|
if (((*ListFlowControll)[i])->name().compare("ClassFlowMakeImage") == 0)
|
||||||
|
{
|
||||||
|
imagetime = ((ClassFlowMakeImage*)(*ListFlowControll)[i])->getTimeImageTaken();
|
||||||
|
}
|
||||||
|
if (((*ListFlowControll)[i])->name().compare("ClassFlowDigit") == 0)
|
||||||
|
{
|
||||||
|
isdigit = true;
|
||||||
|
digit = (*ListFlowControll)[i]->getReadout();
|
||||||
|
}
|
||||||
|
if (((*ListFlowControll)[i])->name().compare("ClassFlowAnalog") == 0)
|
||||||
|
{
|
||||||
|
isanalog = true;
|
||||||
|
analog = (*ListFlowControll)[i]->getReadout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imagetime == 0)
|
||||||
|
time(&imagetime);
|
||||||
|
|
||||||
|
struct tm* timeinfo;
|
||||||
|
timeinfo = localtime(&imagetime);
|
||||||
|
|
||||||
|
char strftime_buf[64];
|
||||||
|
strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d_%H-%M-%S", timeinfo);
|
||||||
|
zwtime = std::string(strftime_buf);
|
||||||
|
|
||||||
|
|
||||||
|
// // TESTING ONLY////////////////////
|
||||||
|
// isdigit = true; digit = "12N";
|
||||||
|
// isanalog = true; analog = "456";
|
||||||
|
|
||||||
|
|
||||||
|
if (!PreValueUse || !PreValueOkay)
|
||||||
|
{
|
||||||
|
if (isdigit)
|
||||||
|
ReturnValue = digit;
|
||||||
|
if (isdigit && isanalog)
|
||||||
|
ReturnValue = ReturnValue + ".";
|
||||||
|
if (isanalog)
|
||||||
|
ReturnValue = ReturnValue + analog;
|
||||||
|
|
||||||
|
if ((findDelimiterPos(ReturnValue, "N") == std::string::npos) && (ReturnValue.length() > 0))
|
||||||
|
{
|
||||||
|
while ((ReturnValue.length() > 1) && (ReturnValue[0] == '0'))
|
||||||
|
{
|
||||||
|
ReturnValue.erase(0, 1);
|
||||||
|
}
|
||||||
|
ReturnRawValue = ReturnValue;
|
||||||
|
Value = std::stof(ReturnValue);
|
||||||
|
SavePreValue(Value, zwtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isdigit)
|
||||||
|
{
|
||||||
|
digit = ErsetzteN(digit);
|
||||||
|
zw = zw + digit;
|
||||||
|
}
|
||||||
|
if (isdigit && isanalog)
|
||||||
|
zw = zw + ".";
|
||||||
|
if (isanalog)
|
||||||
|
zw = zw + analog;
|
||||||
|
|
||||||
|
ReturnRawValue = zw;
|
||||||
|
|
||||||
|
Value = std::stof(zw);
|
||||||
|
|
||||||
|
if ((!AllowNegativeRates) && (Value < PreValue))
|
||||||
|
{
|
||||||
|
error = "Negative Rate - Return old value - " + std::to_string(Value);
|
||||||
|
Value = PreValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useMaxRateValue && ((Value - PreValue) > MaxRateValue))
|
||||||
|
{
|
||||||
|
error = "Negative Rate - Return old value - " + std::to_string(Value);
|
||||||
|
Value = PreValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue = std::to_string(Value);
|
||||||
|
if (ErrorMessage && (error.length() > 0))
|
||||||
|
ReturnValue = ReturnValue + "\t" + error;
|
||||||
|
|
||||||
|
if (error.length() == 0)
|
||||||
|
SavePreValue(Value, zwtime);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ClassFlowPostProcessing::getReadout()
|
||||||
|
{
|
||||||
|
return ReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ClassFlowPostProcessing::getReadoutParam(bool _rawValue)
|
||||||
|
{
|
||||||
|
if (_rawValue)
|
||||||
|
return ReturnRawValue;
|
||||||
|
return ReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string ClassFlowPostProcessing::ErsetzteN(string input)
|
||||||
|
{
|
||||||
|
int posN, posPunkt;
|
||||||
|
int pot, ziffer;
|
||||||
|
float zw;
|
||||||
|
|
||||||
|
posN = findDelimiterPos(input, "N");
|
||||||
|
posPunkt = input.length();
|
||||||
|
|
||||||
|
while (posN != std::string::npos)
|
||||||
|
{
|
||||||
|
pot = posPunkt - posN - 1;
|
||||||
|
zw = PreValue / pow(10, pot);
|
||||||
|
ziffer = ((int) zw) % 10;
|
||||||
|
input[posN] = ziffer + 48;
|
||||||
|
|
||||||
|
posN = findDelimiterPos(input, "N");
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
42
code/lib/jomjol_flowcontroll/ClassFlowPostProcessing.h
Normal file
42
code/lib/jomjol_flowcontroll/ClassFlowPostProcessing.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ClassFlow.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
class ClassFlowPostProcessing :
|
||||||
|
public ClassFlow
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
bool PreValueUse;
|
||||||
|
int PreValueAgeStartup;
|
||||||
|
bool AllowNegativeRates;
|
||||||
|
float MaxRateValue;
|
||||||
|
bool useMaxRateValue;
|
||||||
|
bool ErrorMessage;
|
||||||
|
bool PreValueOkay;
|
||||||
|
|
||||||
|
string FilePreValue;
|
||||||
|
float PreValue;
|
||||||
|
float Value;
|
||||||
|
string ReturnValue;
|
||||||
|
string ReturnRawValue;
|
||||||
|
|
||||||
|
bool LoadPreValue(void);
|
||||||
|
|
||||||
|
|
||||||
|
string ErsetzteN(string);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClassFlowPostProcessing();
|
||||||
|
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc);
|
||||||
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
|
string getReadout();
|
||||||
|
string getReadoutParam(bool _rawValue);
|
||||||
|
void SavePreValue(float value, string time = "");
|
||||||
|
string GetPreValue();
|
||||||
|
|
||||||
|
string name(){return "ClassFlowPostProcessing";};
|
||||||
|
};
|
||||||
|
|
||||||
157
code/lib/jomjol_flowcontroll/Helper.cpp
Normal file
157
code/lib/jomjol_flowcontroll/Helper.cpp
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
//#pragma warning(disable : 4996)
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
//#define ISWINDOWS_TRUE
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
std::string FormatFileName(std::string input)
|
||||||
|
{
|
||||||
|
#ifdef ISWINDOWS_TRUE
|
||||||
|
input.erase(0, 1);
|
||||||
|
std::string os = "/";
|
||||||
|
std::string ns = "\\";
|
||||||
|
FindReplace(input, os, ns);
|
||||||
|
#endif
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void FindReplace(std::string& line, std::string& oldString, std::string& newString) {
|
||||||
|
const size_t oldSize = oldString.length();
|
||||||
|
|
||||||
|
// do nothing if line is shorter than the string to find
|
||||||
|
if (oldSize > line.length()) return;
|
||||||
|
|
||||||
|
const size_t newSize = newString.length();
|
||||||
|
for (size_t pos = 0; ; pos += newSize) {
|
||||||
|
// Locate the substring to replace
|
||||||
|
pos = line.find(oldString, pos);
|
||||||
|
if (pos == std::string::npos) return;
|
||||||
|
if (oldSize == newSize) {
|
||||||
|
// if they're same size, use std::string::replace
|
||||||
|
line.replace(pos, oldSize, newString);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if not same size, replace by erasing and inserting
|
||||||
|
line.erase(pos, oldSize);
|
||||||
|
line.insert(pos, newString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool ctype_space(const char c, string adddelimiter)
|
||||||
|
{
|
||||||
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 11)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (adddelimiter.find(c) != string::npos)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string trim(string istring, string adddelimiter)
|
||||||
|
{
|
||||||
|
bool trimmed = false;
|
||||||
|
|
||||||
|
if (ctype_space(istring[istring.length() - 1], adddelimiter))
|
||||||
|
{
|
||||||
|
istring.erase(istring.length() - 1);
|
||||||
|
trimmed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctype_space(istring[0], adddelimiter))
|
||||||
|
{
|
||||||
|
istring.erase(0, 1);
|
||||||
|
trimmed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((trimmed == false) || (istring.size() == 0))
|
||||||
|
{
|
||||||
|
return istring;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return trim(istring, adddelimiter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t findDelimiterPos(string input, string delimiter)
|
||||||
|
{
|
||||||
|
size_t pos = std::string::npos;
|
||||||
|
size_t zw;
|
||||||
|
string akt_del;
|
||||||
|
|
||||||
|
for (int anz = 0; anz < delimiter.length(); ++anz)
|
||||||
|
{
|
||||||
|
akt_del = delimiter[anz];
|
||||||
|
if ((zw = input.find(akt_del)) != std::string::npos)
|
||||||
|
{
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
if (zw < pos)
|
||||||
|
pos = zw;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pos = zw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CopyFile(string input, string output)
|
||||||
|
{
|
||||||
|
input = FormatFileName(input);
|
||||||
|
output = FormatFileName(output);
|
||||||
|
|
||||||
|
char cTemp;
|
||||||
|
FILE* fpSourceFile = fopen(input.c_str(), "rb");
|
||||||
|
FILE* fpTargetFile = fopen(output.c_str(), "wb");
|
||||||
|
|
||||||
|
// Code Section
|
||||||
|
|
||||||
|
// Read From The Source File - "Copy"
|
||||||
|
while (fread(&cTemp, 1, 1, fpSourceFile) == 1)
|
||||||
|
{
|
||||||
|
// Write To The Target File - "Paste"
|
||||||
|
fwrite(&cTemp, 1, 1, fpTargetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close The Files
|
||||||
|
fclose(fpSourceFile);
|
||||||
|
fclose(fpTargetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string getFileType(string filename)
|
||||||
|
{
|
||||||
|
int lastpos = filename.find(".", 0);
|
||||||
|
int neu_pos;
|
||||||
|
while ((neu_pos = filename.find(".", lastpos + 1)) > -1)
|
||||||
|
{
|
||||||
|
lastpos = neu_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
string zw = filename.substr(lastpos + 1, filename.size() - lastpos);
|
||||||
|
|
||||||
|
return zw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string toUpper(string in)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < in.length(); ++i)
|
||||||
|
in[i] = toupper(in[i]);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
22
code/lib/jomjol_flowcontroll/Helper.h
Normal file
22
code/lib/jomjol_flowcontroll/Helper.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
std::string FormatFileName(std::string input);
|
||||||
|
void FindReplace(std::string& line, std::string& oldString, std::string& newString);
|
||||||
|
|
||||||
|
void CopyFile(string input, string output);
|
||||||
|
|
||||||
|
size_t findDelimiterPos(string input, string delimiter);
|
||||||
|
//string trim(string istring);
|
||||||
|
string trim(string istring, string adddelimiter = "");
|
||||||
|
bool ctype_space(const char c, string adddelimiter);
|
||||||
|
|
||||||
|
string getFileType(string filename);
|
||||||
|
|
||||||
|
string toUpper(string in);
|
||||||
|
|
||||||
|
|
||||||
101
code/lib/jomjol_flowcontroll/camera_define.h
Normal file
101
code/lib/jomjol_flowcontroll/camera_define.h
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#ifndef CAMERADEFINED
|
||||||
|
#define CAMERADEFINED
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(CAMERA_MODEL_WROVER_KIT)
|
||||||
|
#define PWDN_GPIO_NUM -1
|
||||||
|
#define RESET_GPIO_NUM -1
|
||||||
|
#define XCLK_GPIO_NUM 21
|
||||||
|
#define SIOD_GPIO_NUM 26
|
||||||
|
#define SIOC_GPIO_NUM 27
|
||||||
|
|
||||||
|
#define Y9_GPIO_NUM 35
|
||||||
|
#define Y8_GPIO_NUM 34
|
||||||
|
#define Y7_GPIO_NUM 39
|
||||||
|
#define Y6_GPIO_NUM 36
|
||||||
|
#define Y5_GPIO_NUM 19
|
||||||
|
#define Y4_GPIO_NUM 18
|
||||||
|
#define Y3_GPIO_NUM 5
|
||||||
|
#define Y2_GPIO_NUM 4
|
||||||
|
#define VSYNC_GPIO_NUM 25
|
||||||
|
#define HREF_GPIO_NUM 23
|
||||||
|
#define PCLK_GPIO_NUM 22
|
||||||
|
|
||||||
|
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
|
||||||
|
#define PWDN_GPIO_NUM -1
|
||||||
|
#define RESET_GPIO_NUM 15
|
||||||
|
#define XCLK_GPIO_NUM 27
|
||||||
|
#define SIOD_GPIO_NUM 25
|
||||||
|
#define SIOC_GPIO_NUM 23
|
||||||
|
|
||||||
|
#define Y9_GPIO_NUM 19
|
||||||
|
#define Y8_GPIO_NUM 36
|
||||||
|
#define Y7_GPIO_NUM 18
|
||||||
|
#define Y6_GPIO_NUM 39
|
||||||
|
#define Y5_GPIO_NUM 5
|
||||||
|
#define Y4_GPIO_NUM 34
|
||||||
|
#define Y3_GPIO_NUM 35
|
||||||
|
#define Y2_GPIO_NUM 32
|
||||||
|
#define VSYNC_GPIO_NUM 22
|
||||||
|
#define HREF_GPIO_NUM 26
|
||||||
|
#define PCLK_GPIO_NUM 21
|
||||||
|
|
||||||
|
#elif defined(CAMERA_MODEL_AI_THINKER)
|
||||||
|
#define PWDN_GPIO_NUM GPIO_NUM_32
|
||||||
|
#define RESET_GPIO_NUM -1
|
||||||
|
#define XCLK_GPIO_NUM GPIO_NUM_0
|
||||||
|
#define SIOD_GPIO_NUM GPIO_NUM_26
|
||||||
|
#define SIOC_GPIO_NUM GPIO_NUM_27
|
||||||
|
|
||||||
|
#define Y9_GPIO_NUM GPIO_NUM_35
|
||||||
|
#define Y8_GPIO_NUM GPIO_NUM_34
|
||||||
|
#define Y7_GPIO_NUM GPIO_NUM_39
|
||||||
|
#define Y6_GPIO_NUM GPIO_NUM_36
|
||||||
|
#define Y5_GPIO_NUM GPIO_NUM_21
|
||||||
|
#define Y4_GPIO_NUM GPIO_NUM_19
|
||||||
|
#define Y3_GPIO_NUM GPIO_NUM_18
|
||||||
|
#define Y2_GPIO_NUM GPIO_NUM_5
|
||||||
|
#define VSYNC_GPIO_NUM GPIO_NUM_25
|
||||||
|
#define HREF_GPIO_NUM GPIO_NUM_23
|
||||||
|
#define PCLK_GPIO_NUM GPIO_NUM_22
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Camera model not selected"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static camera_config_t camera_config = {
|
||||||
|
.pin_pwdn = PWDN_GPIO_NUM,
|
||||||
|
.pin_reset = RESET_GPIO_NUM,
|
||||||
|
.pin_xclk = XCLK_GPIO_NUM,
|
||||||
|
.pin_sscb_sda = SIOD_GPIO_NUM,
|
||||||
|
.pin_sscb_scl = SIOC_GPIO_NUM,
|
||||||
|
|
||||||
|
.pin_d7 = Y9_GPIO_NUM,
|
||||||
|
.pin_d6 = Y8_GPIO_NUM,
|
||||||
|
.pin_d5 = Y7_GPIO_NUM,
|
||||||
|
.pin_d4 = Y6_GPIO_NUM,
|
||||||
|
.pin_d3 = Y5_GPIO_NUM,
|
||||||
|
.pin_d2 = Y4_GPIO_NUM,
|
||||||
|
.pin_d1 = Y3_GPIO_NUM,
|
||||||
|
.pin_d0 = Y2_GPIO_NUM,
|
||||||
|
.pin_vsync = VSYNC_GPIO_NUM,
|
||||||
|
.pin_href = HREF_GPIO_NUM,
|
||||||
|
.pin_pclk = PCLK_GPIO_NUM,
|
||||||
|
|
||||||
|
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
|
||||||
|
.xclk_freq_hz = 20000000,
|
||||||
|
.ledc_timer = LEDC_TIMER_0,
|
||||||
|
.ledc_channel = LEDC_CHANNEL_0,
|
||||||
|
|
||||||
|
.pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG
|
||||||
|
// .pixel_format = PIXFORMAT_RGB888,//YUV422,GRAYSCALE,RGB565,JPEG
|
||||||
|
// .frame_size = FRAMESIZE_QVGA,//QQVGA-QXGA Do not use sizes above QVGA when not JPEG
|
||||||
|
.frame_size = FRAMESIZE_SVGA,//QQVGA-QXGA Do not use sizes above QVGA when not JPEG
|
||||||
|
|
||||||
|
.jpeg_quality = 12, //0-63 lower number means higher quality
|
||||||
|
.fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
87
code/lib/jomjol_image_proc/CFindTemplate._h_
Normal file
87
code/lib/jomjol_image_proc/CFindTemplate._h_
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __CFINDTEMPLATE
|
||||||
|
#define __CFINGTEMPLATE
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <esp_heap_caps.h>
|
||||||
|
|
||||||
|
#include "stb_image.h"
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
#include "stb_image_resize.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CImageBasis
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
uint8_t* rgb_image;
|
||||||
|
int channels;
|
||||||
|
int width, height, bpp;
|
||||||
|
bool externalImage;
|
||||||
|
std::string filename;
|
||||||
|
|
||||||
|
void memCopy(uint8_t* _source, uint8_t* _target, int _size);
|
||||||
|
public:
|
||||||
|
int getWidth(){return this->width;};
|
||||||
|
int getHeight(){return this->width;};
|
||||||
|
int getChannels(){return this->channels;};
|
||||||
|
|
||||||
|
CImageBasis();
|
||||||
|
CImageBasis(std::string _image);
|
||||||
|
CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||||
|
uint8_t GetPixelColor(int x, int y, int ch);
|
||||||
|
|
||||||
|
~CImageBasis();
|
||||||
|
|
||||||
|
void SaveToFile(std::string _imageout);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CFindTemplate : public CImageBasis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CFindTemplate(std::string _image);
|
||||||
|
|
||||||
|
void FindTemplate(std::string _template, int* found_x, int* found_y, std::string _imageout);
|
||||||
|
void FindTemplate(std::string _template, int* found_x, int* found_y, int _dx, int _dy, std::string _imageout);
|
||||||
|
void FindTemplate(std::string _template, int* found_x, int* found_y);
|
||||||
|
void FindTemplate(std::string _template, int* found_x, int* found_y, int _dx, int _dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CRotate: public CImageBasis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CRotate(std::string _image) : CImageBasis(_image) {};
|
||||||
|
CRotate(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(_rgb_image, _channels, _width, _height, _bpp) {};
|
||||||
|
|
||||||
|
void Rotate(float _angle);
|
||||||
|
void Rotate(float _angle, int _centerx, int _centery);
|
||||||
|
void Translate(int _dx, int _dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CAlignAndCutImage : public CImageBasis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CAlignAndCutImage(std::string _image) : CImageBasis(_image) {};
|
||||||
|
|
||||||
|
void Align(std::string _template0, int ref0_x, int ref0_y, std::string _template1, int ref1_x, int ref1_y, int deltax, int deltay);
|
||||||
|
void CutAndSave(std::string _template1, int x1, int y1, int dx, int dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CResizeImage : public CImageBasis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CResizeImage(std::string _image) : CImageBasis(_image) {};
|
||||||
|
void Resize(int _new_dx, int _new_dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
366
code/lib/jomjol_image_proc/CFindTemplate.cp__p
Normal file
366
code/lib/jomjol_image_proc/CFindTemplate.cp__p
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
#include "CFindTemplate.h"
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <math.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#define _ESP32_PSRAM
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define GET_MEMORY malloc
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t CImageBasis::GetPixelColor(int x, int y, int ch)
|
||||||
|
{
|
||||||
|
stbi_uc* p_source;
|
||||||
|
p_source = this->rgb_image + (this->channels * (y * this->width + x));
|
||||||
|
return p_source[channels];
|
||||||
|
}
|
||||||
|
|
||||||
|
void CResizeImage::Resize(int _new_dx, int _new_dy)
|
||||||
|
{
|
||||||
|
int memsize = _new_dx * _new_dy * this->channels;
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
stbir_resize_uint8(this->rgb_image, this->width, this->height, 0, odata, _new_dx, _new_dy, 0, this->channels);
|
||||||
|
|
||||||
|
stbi_image_free(this->rgb_image);
|
||||||
|
this->rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
this->memCopy(odata, this->rgb_image, memsize);
|
||||||
|
this->width = _new_dx;
|
||||||
|
this->height = _new_dy;
|
||||||
|
stbi_image_free(odata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRotate::Rotate(float _angle, int _centerx, int _centery)
|
||||||
|
{
|
||||||
|
float m[2][3];
|
||||||
|
|
||||||
|
float x_center = _centerx;
|
||||||
|
float y_center = _centery;
|
||||||
|
_angle = _angle / 180 * M_PI;
|
||||||
|
|
||||||
|
m[0][0] = cos(_angle);
|
||||||
|
m[0][1] = sin(_angle);
|
||||||
|
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
||||||
|
|
||||||
|
m[1][0] = -m[0][1];
|
||||||
|
m[1][1] = m[0][0];
|
||||||
|
m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center;
|
||||||
|
|
||||||
|
int memsize = this->width * this->height * this->channels;
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
int x_source, y_source;
|
||||||
|
stbi_uc* p_target;
|
||||||
|
stbi_uc* p_source;
|
||||||
|
|
||||||
|
for (int x = 0; x < this->width; ++x)
|
||||||
|
for (int y = 0; y < this->height; ++y)
|
||||||
|
{
|
||||||
|
p_target = odata + (this->channels * (y * this->width + x));
|
||||||
|
|
||||||
|
x_source = int(m[0][0] * x + m[0][1] * y);
|
||||||
|
y_source = int(m[1][0] * x + m[1][1] * y);
|
||||||
|
|
||||||
|
x_source += int(m[0][2]);
|
||||||
|
y_source += int(m[1][2]);
|
||||||
|
|
||||||
|
if ((x_source >= 0) && (x_source < this->width) && (y_source >= 0) && (y_source < this->height))
|
||||||
|
{
|
||||||
|
p_source = this->rgb_image + (this->channels * (y_source * this->width + x_source));
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = p_source[channels];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// memcpy(this->rgb_image, odata, memsize);
|
||||||
|
this->memCopy(odata, this->rgb_image, memsize);
|
||||||
|
stbi_image_free(odata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRotate::Rotate(float _angle)
|
||||||
|
{
|
||||||
|
this->Rotate(_angle, this->width / 2, this->height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRotate::Translate(int _dx, int _dy)
|
||||||
|
{
|
||||||
|
int memsize = this->width * this->height * this->channels;
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
|
||||||
|
int x_source, y_source;
|
||||||
|
stbi_uc* p_target;
|
||||||
|
stbi_uc* p_source;
|
||||||
|
|
||||||
|
for (int x = 0; x < this->width; ++x)
|
||||||
|
for (int y = 0; y < this->height; ++y)
|
||||||
|
{
|
||||||
|
p_target = odata + (this->channels * (y * this->width + x));
|
||||||
|
|
||||||
|
x_source = x - _dx;
|
||||||
|
y_source = y - _dy;
|
||||||
|
|
||||||
|
if ((x_source >= 0) && (x_source < this->width) && (y_source >= 0) && (y_source < this->height))
|
||||||
|
{
|
||||||
|
p_source = this->rgb_image + (this->channels * (y_source * this->width + x_source));
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = p_source[channels];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// memcpy(this->rgb_image, odata, memsize);
|
||||||
|
this->memCopy(odata, this->rgb_image, memsize);
|
||||||
|
stbi_image_free(odata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CFindTemplate::CFindTemplate(std::string _image)
|
||||||
|
{
|
||||||
|
this->channels = 1;
|
||||||
|
this->rgb_image = stbi_load(_image.c_str(), &(this->width), &(this->height), &(this->bpp), this->channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFindTemplate::FindTemplate(std::string _template, int* found_x, int* found_y)
|
||||||
|
{
|
||||||
|
this->FindTemplate(_template, found_x, found_y, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFindTemplate::FindTemplate(std::string _template, int* found_x, int* found_y, int _dx, int _dy)
|
||||||
|
{
|
||||||
|
int tpl_width, tpl_height, tpl_bpp;
|
||||||
|
uint8_t* rgb_template = stbi_load(_template.c_str(), &tpl_width, &tpl_height, &tpl_bpp, this->channels);
|
||||||
|
|
||||||
|
int ow, ow_start, ow_stop;
|
||||||
|
int oh, oh_start, oh_stop;
|
||||||
|
|
||||||
|
if (_dx == 0)
|
||||||
|
{
|
||||||
|
_dx = this->width;
|
||||||
|
*found_x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_dy == 0)
|
||||||
|
{
|
||||||
|
_dy = this->height;
|
||||||
|
*found_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ow_start = *found_x - _dx;
|
||||||
|
ow_start = std::max(ow_start, 0);
|
||||||
|
ow_stop = *found_x + _dx;
|
||||||
|
if ((ow_stop + tpl_width) > this->width)
|
||||||
|
ow_stop = this->width - tpl_width;
|
||||||
|
ow = ow_stop - ow_start + 1;
|
||||||
|
|
||||||
|
oh_start = *found_y - _dy;
|
||||||
|
oh_start = std::max(oh_start, 0);
|
||||||
|
oh_stop = *found_y + _dy;
|
||||||
|
if ((oh_stop + tpl_height) > this->height)
|
||||||
|
oh_stop = this->height - tpl_height;
|
||||||
|
oh = oh_stop - oh_start + 1;
|
||||||
|
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(ow * oh * this->channels);
|
||||||
|
|
||||||
|
double aktSAD;
|
||||||
|
double minSAD = pow(tpl_width * tpl_height * 255, 2);
|
||||||
|
|
||||||
|
for (int xouter = ow_start; xouter <= ow_stop; xouter++)
|
||||||
|
for (int youter = oh_start; youter <= oh_stop; ++youter)
|
||||||
|
{
|
||||||
|
aktSAD = 0;
|
||||||
|
for (int tpl_x = 0; tpl_x < tpl_width; tpl_x++)
|
||||||
|
for (int tpl_y = 0; tpl_y < tpl_height; tpl_y++)
|
||||||
|
{
|
||||||
|
stbi_uc* p_org = this->rgb_image + (this->channels * ((youter + tpl_y) * this->width + (xouter + tpl_x)));
|
||||||
|
stbi_uc* p_tpl = rgb_template + (this->channels * (tpl_y * tpl_width + tpl_x));
|
||||||
|
aktSAD += pow(p_tpl[0] - p_org[0], 2);
|
||||||
|
}
|
||||||
|
stbi_uc* p_out = odata + (this->channels * ((youter - oh_start) * ow + (xouter - ow_start)));
|
||||||
|
|
||||||
|
p_out[0] = int(sqrt(aktSAD / (tpl_width * tpl_height)));
|
||||||
|
if (aktSAD < minSAD)
|
||||||
|
{
|
||||||
|
minSAD = aktSAD;
|
||||||
|
*found_x = xouter;
|
||||||
|
*found_y = youter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_image_free(odata);
|
||||||
|
stbi_image_free(rgb_template);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFindTemplate::FindTemplate(std::string _template, int* found_x, int* found_y, std::string _imageout)
|
||||||
|
{
|
||||||
|
this->FindTemplate(_template, found_x, found_y);
|
||||||
|
this->SaveToFile(_imageout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFindTemplate::FindTemplate(std::string _template, int* found_x, int* found_y, int _dx, int _dy, std::string _imageout)
|
||||||
|
{
|
||||||
|
this->FindTemplate(_template, found_x, found_y, _dx, _dy);
|
||||||
|
this->SaveToFile(_imageout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void CImageBasis::memCopy(uint8_t* _source, uint8_t* _target, int _size)
|
||||||
|
{
|
||||||
|
#ifdef _ESP32_PSRAM
|
||||||
|
for (int i = 0; i < _size; ++i)
|
||||||
|
*(_target + i) = *(_source + i);
|
||||||
|
#else
|
||||||
|
memcpy(_target, _source, _size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageBasis::CImageBasis()
|
||||||
|
{
|
||||||
|
this->externalImage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageBasis::CImageBasis(std::string _image)
|
||||||
|
{
|
||||||
|
// printf("Start CImageBasis\n");
|
||||||
|
channels = 3;
|
||||||
|
externalImage = false;
|
||||||
|
filename = _image;
|
||||||
|
// printf("CImageBasis before load\n");
|
||||||
|
// printf(_image.c_str()); printf("\n");
|
||||||
|
rgb_image = stbi_load(_image.c_str(), &width, &height, &bpp, channels);
|
||||||
|
if (!rgb_image)
|
||||||
|
{
|
||||||
|
printf("Datei konnte nicht geoeffnet werden\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// printf("CImageBasis after load\n");
|
||||||
|
// printf("w %d, h %d, b %d, c %d\n", width, height, bpp, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageBasis::CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp)
|
||||||
|
{
|
||||||
|
rgb_image = _rgb_image;
|
||||||
|
channels = _channels;
|
||||||
|
width = _width;
|
||||||
|
height = _height;
|
||||||
|
bpp = _bpp;
|
||||||
|
externalImage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageBasis::~CImageBasis()
|
||||||
|
{
|
||||||
|
if (!externalImage)
|
||||||
|
stbi_image_free(rgb_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImageBasis::SaveToFile(std::string _imageout)
|
||||||
|
{
|
||||||
|
string typ = getFileType(_imageout);
|
||||||
|
|
||||||
|
if ((typ == "jpg") || (typ == "JPG")) // ACHTUNG PROBLEMATISCH IM ESP32
|
||||||
|
{
|
||||||
|
stbi_write_jpg(_imageout.c_str(), this->width, this->height, this->channels, this->rgb_image, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((typ == "bmp") || (typ == "BMP"))
|
||||||
|
{
|
||||||
|
stbi_write_bmp(_imageout.c_str(), this->width, this->height, this->channels, this->rgb_image);
|
||||||
|
}
|
||||||
|
// stbi_write_jpg(_imageout.c_str(), this->width, this->height, this->channels, this->rgb_image, 0);
|
||||||
|
// stbi_write_bmp(_imageout.c_str(), this->width, this->height, this->channels, this->rgb_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void CAlignAndCutImage::Align(std::string _template0, int ref0_x, int ref0_y, std::string _template1, int ref1_x, int ref1_y, int deltax = 40, int deltay = 40)
|
||||||
|
{
|
||||||
|
int dx, dy;
|
||||||
|
int r0_x, r0_y, r1_x, r1_y;
|
||||||
|
|
||||||
|
CFindTemplate* ft = new CFindTemplate(this->filename);
|
||||||
|
|
||||||
|
r0_x = ref0_x;
|
||||||
|
r0_y = ref0_y;
|
||||||
|
ft->FindTemplate(_template0, &r0_x, &r0_y, deltax, deltay);
|
||||||
|
|
||||||
|
r1_x = ref1_x;
|
||||||
|
r1_y = ref1_y;
|
||||||
|
ft->FindTemplate(_template1, &r1_x, &r1_y, deltax, deltay);
|
||||||
|
|
||||||
|
delete ft;
|
||||||
|
|
||||||
|
|
||||||
|
dx = ref0_x - r0_x;
|
||||||
|
dy = ref0_y - r0_y;
|
||||||
|
|
||||||
|
r0_x += dx;
|
||||||
|
r0_y += dy;
|
||||||
|
|
||||||
|
r1_x += dx;
|
||||||
|
r1_y += dy;
|
||||||
|
|
||||||
|
float w_org, w_ist, d_winkel;
|
||||||
|
|
||||||
|
w_org = atan2(ref1_y - ref0_y, ref1_x - ref0_x);
|
||||||
|
w_ist = atan2(r1_y - r0_y, r1_x - r0_x);
|
||||||
|
|
||||||
|
d_winkel = -(w_org - w_ist) * 180 / M_PI;
|
||||||
|
|
||||||
|
CRotate rt(this->rgb_image, this->channels, this->width, this->height, this->bpp);
|
||||||
|
rt.Translate(dx, dy);
|
||||||
|
rt.Rotate(d_winkel, ref0_x, ref0_y);
|
||||||
|
printf("Alignment: dx %d - dy %d - rot %f\n", dx, dy, d_winkel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAlignAndCutImage::CutAndSave(std::string _template1, int x1, int y1, int dx, int dy)
|
||||||
|
{
|
||||||
|
|
||||||
|
int x2, y2;
|
||||||
|
|
||||||
|
x2 = x1 + dx;
|
||||||
|
y2 = y1 + dy;
|
||||||
|
x2 = min(x2, this->width - 1);
|
||||||
|
y2 = min(y2, this->height - 1);
|
||||||
|
|
||||||
|
dx = x2 - x1;
|
||||||
|
dy = y2 - y1;
|
||||||
|
|
||||||
|
int memsize = dx * dy * this->channels;
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
|
||||||
|
int x_source, y_source;
|
||||||
|
stbi_uc* p_target;
|
||||||
|
stbi_uc* p_source;
|
||||||
|
|
||||||
|
for (int x = x1; x < x2; ++x)
|
||||||
|
for (int y = y1; y < y2; ++y)
|
||||||
|
{
|
||||||
|
p_target = odata + (this->channels * ((y - y1) * dx + (x - x1)));
|
||||||
|
p_source = this->rgb_image + (this->channels * (y * this->width + x));
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = p_source[channels];
|
||||||
|
}
|
||||||
|
|
||||||
|
// stbi_write_jpg(_template1.c_str(), dx, dy, this->channels, odata, 0);
|
||||||
|
stbi_write_bmp(_template1.c_str(), dx, dy, this->channels, odata);
|
||||||
|
|
||||||
|
stbi_image_free(odata);
|
||||||
|
}
|
||||||
496
code/lib/jomjol_image_proc/CFindTemplate.cpp
Normal file
496
code/lib/jomjol_image_proc/CFindTemplate.cpp
Normal file
@@ -0,0 +1,496 @@
|
|||||||
|
#include "CFindTemplate.h"
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <math.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#define _ESP32_PSRAM
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define GET_MEMORY malloc
|
||||||
|
|
||||||
|
/*
|
||||||
|
CResizeImage::CResizeImage(std::string _image, int _new_dx, int _new_dy)
|
||||||
|
{
|
||||||
|
CImageBasis::CImageBasis(_image);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t CImageBasis::GetPixelColor(int x, int y, int ch)
|
||||||
|
{
|
||||||
|
stbi_uc* p_source;
|
||||||
|
p_source = this->rgb_image + (this->channels * (y * this->width + x));
|
||||||
|
return p_source[ch];
|
||||||
|
}
|
||||||
|
|
||||||
|
void CResizeImage::Resize(int _new_dx, int _new_dy)
|
||||||
|
{
|
||||||
|
int memsize = _new_dx * _new_dy * this->channels;
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
stbir_resize_uint8(this->rgb_image, this->width, this->height, 0, odata, _new_dx, _new_dy, 0, this->channels);
|
||||||
|
|
||||||
|
stbi_image_free(this->rgb_image);
|
||||||
|
this->rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
this->memCopy(odata, this->rgb_image, memsize);
|
||||||
|
this->width = _new_dx;
|
||||||
|
this->height = _new_dy;
|
||||||
|
stbi_image_free(odata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRotate::Rotate(float _angle, int _centerx, int _centery)
|
||||||
|
{
|
||||||
|
float m[2][3];
|
||||||
|
|
||||||
|
float x_center = _centerx;
|
||||||
|
float y_center = _centery;
|
||||||
|
_angle = _angle / 180 * M_PI;
|
||||||
|
|
||||||
|
m[0][0] = cos(_angle);
|
||||||
|
m[0][1] = sin(_angle);
|
||||||
|
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
||||||
|
|
||||||
|
m[1][0] = -m[0][1];
|
||||||
|
m[1][1] = m[0][0];
|
||||||
|
m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center;
|
||||||
|
|
||||||
|
int memsize = this->width * this->height * this->channels;
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
int x_source, y_source;
|
||||||
|
stbi_uc* p_target;
|
||||||
|
stbi_uc* p_source;
|
||||||
|
|
||||||
|
for (int x = 0; x < this->width; ++x)
|
||||||
|
for (int y = 0; y < this->height; ++y)
|
||||||
|
{
|
||||||
|
p_target = odata + (this->channels * (y * this->width + x));
|
||||||
|
|
||||||
|
x_source = int(m[0][0] * x + m[0][1] * y);
|
||||||
|
y_source = int(m[1][0] * x + m[1][1] * y);
|
||||||
|
|
||||||
|
x_source += int(m[0][2]);
|
||||||
|
y_source += int(m[1][2]);
|
||||||
|
|
||||||
|
if ((x_source >= 0) && (x_source < this->width) && (y_source >= 0) && (y_source < this->height))
|
||||||
|
{
|
||||||
|
p_source = this->rgb_image + (this->channels * (y_source * this->width + x_source));
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = p_source[channels];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// memcpy(this->rgb_image, odata, memsize);
|
||||||
|
this->memCopy(odata, this->rgb_image, memsize);
|
||||||
|
stbi_image_free(odata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRotate::Rotate(float _angle)
|
||||||
|
{
|
||||||
|
this->Rotate(_angle, this->width / 2, this->height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRotate::Translate(int _dx, int _dy)
|
||||||
|
{
|
||||||
|
int memsize = this->width * this->height * this->channels;
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
|
||||||
|
int x_source, y_source;
|
||||||
|
stbi_uc* p_target;
|
||||||
|
stbi_uc* p_source;
|
||||||
|
|
||||||
|
for (int x = 0; x < this->width; ++x)
|
||||||
|
for (int y = 0; y < this->height; ++y)
|
||||||
|
{
|
||||||
|
p_target = odata + (this->channels * (y * this->width + x));
|
||||||
|
|
||||||
|
x_source = x - _dx;
|
||||||
|
y_source = y - _dy;
|
||||||
|
|
||||||
|
if ((x_source >= 0) && (x_source < this->width) && (y_source >= 0) && (y_source < this->height))
|
||||||
|
{
|
||||||
|
p_source = this->rgb_image + (this->channels * (y_source * this->width + x_source));
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = p_source[channels];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// memcpy(this->rgb_image, odata, memsize);
|
||||||
|
this->memCopy(odata, this->rgb_image, memsize);
|
||||||
|
stbi_image_free(odata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CFindTemplate::CFindTemplate(std::string _image)
|
||||||
|
{
|
||||||
|
this->channels = 1;
|
||||||
|
this->rgb_image = stbi_load(_image.c_str(), &(this->width), &(this->height), &(this->bpp), this->channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFindTemplate::FindTemplate(std::string _template, int* found_x, int* found_y)
|
||||||
|
{
|
||||||
|
this->FindTemplate(_template, found_x, found_y, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFindTemplate::FindTemplate(std::string _template, int* found_x, int* found_y, int _dx, int _dy)
|
||||||
|
{
|
||||||
|
uint8_t* rgb_template = stbi_load(_template.c_str(), &tpl_width, &tpl_height, &tpl_bpp, this->channels);
|
||||||
|
|
||||||
|
int ow, ow_start, ow_stop;
|
||||||
|
int oh, oh_start, oh_stop;
|
||||||
|
|
||||||
|
if (_dx == 0)
|
||||||
|
{
|
||||||
|
_dx = this->width;
|
||||||
|
*found_x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_dy == 0)
|
||||||
|
{
|
||||||
|
_dy = this->height;
|
||||||
|
*found_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ow_start = *found_x - _dx;
|
||||||
|
ow_start = std::max(ow_start, 0);
|
||||||
|
ow_stop = *found_x + _dx;
|
||||||
|
if ((ow_stop + tpl_width) > this->width)
|
||||||
|
ow_stop = this->width - tpl_width;
|
||||||
|
ow = ow_stop - ow_start + 1;
|
||||||
|
|
||||||
|
oh_start = *found_y - _dy;
|
||||||
|
oh_start = std::max(oh_start, 0);
|
||||||
|
oh_stop = *found_y + _dy;
|
||||||
|
if ((oh_stop + tpl_height) > this->height)
|
||||||
|
oh_stop = this->height - tpl_height;
|
||||||
|
oh = oh_stop - oh_start + 1;
|
||||||
|
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(ow * oh * this->channels);
|
||||||
|
|
||||||
|
double aktSAD;
|
||||||
|
double minSAD = pow(tpl_width * tpl_height * 255, 2);
|
||||||
|
|
||||||
|
for (int xouter = ow_start; xouter <= ow_stop; xouter++)
|
||||||
|
for (int youter = oh_start; youter <= oh_stop; ++youter)
|
||||||
|
{
|
||||||
|
aktSAD = 0;
|
||||||
|
for (int tpl_x = 0; tpl_x < tpl_width; tpl_x++)
|
||||||
|
for (int tpl_y = 0; tpl_y < tpl_height; tpl_y++)
|
||||||
|
{
|
||||||
|
stbi_uc* p_org = this->rgb_image + (this->channels * ((youter + tpl_y) * this->width + (xouter + tpl_x)));
|
||||||
|
stbi_uc* p_tpl = rgb_template + (this->channels * (tpl_y * tpl_width + tpl_x));
|
||||||
|
aktSAD += pow(p_tpl[0] - p_org[0], 2);
|
||||||
|
}
|
||||||
|
stbi_uc* p_out = odata + (this->channels * ((youter - oh_start) * ow + (xouter - ow_start)));
|
||||||
|
|
||||||
|
p_out[0] = int(sqrt(aktSAD / (tpl_width * tpl_height)));
|
||||||
|
if (aktSAD < minSAD)
|
||||||
|
{
|
||||||
|
minSAD = aktSAD;
|
||||||
|
*found_x = xouter;
|
||||||
|
*found_y = youter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_write_bmp("sdcard\\find.bmp", ow, oh, this->channels, odata);
|
||||||
|
|
||||||
|
stbi_image_free(odata);
|
||||||
|
stbi_image_free(rgb_template);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFindTemplate::FindTemplate(std::string _template, int* found_x, int* found_y, std::string _imageout)
|
||||||
|
{
|
||||||
|
this->FindTemplate(_template, found_x, found_y);
|
||||||
|
this->SaveToFile(_imageout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFindTemplate::FindTemplate(std::string _template, int* found_x, int* found_y, int _dx, int _dy, std::string _imageout)
|
||||||
|
{
|
||||||
|
this->FindTemplate(_template, found_x, found_y, _dx, _dy);
|
||||||
|
this->SaveToFile(_imageout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void CImageBasis::memCopy(uint8_t* _source, uint8_t* _target, int _size)
|
||||||
|
{
|
||||||
|
#ifdef _ESP32_PSRAM
|
||||||
|
for (int i = 0; i < _size; ++i)
|
||||||
|
*(_target + i) = *(_source + i);
|
||||||
|
#else
|
||||||
|
memcpy(_target, _source, _size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CImageBasis::isInImage(int x, int y)
|
||||||
|
{
|
||||||
|
if ((x < 0) || (x > this->width - 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((y < 0) || (y > this->height- 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImageBasis::setPixelColor(int x, int y, int r, int g, int b)
|
||||||
|
{
|
||||||
|
stbi_uc* p_source;
|
||||||
|
|
||||||
|
p_source = this->rgb_image + (this->channels * (y * this->width + x));
|
||||||
|
p_source[0] = r;
|
||||||
|
if (this-> channels > 2)
|
||||||
|
{
|
||||||
|
p_source[1] = g;
|
||||||
|
p_source[2] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImageBasis::drawRect(int x, int y, int dx, int dy, int r, int g, int b, int thickness)
|
||||||
|
{
|
||||||
|
int zwx1, zwx2, zwy1, zwy2;
|
||||||
|
int _x, _y, _thick;
|
||||||
|
|
||||||
|
zwx1 = x - thickness + 1;
|
||||||
|
zwx2 = x + dx + thickness - 1;
|
||||||
|
zwy1 = y;
|
||||||
|
zwy2 = y;
|
||||||
|
for (_thick = 0; _thick < thickness; _thick++)
|
||||||
|
for (_x = zwx1; _x <= zwx2; ++_x)
|
||||||
|
for (_y = zwy1; _y <= zwy2; _y++)
|
||||||
|
if (isInImage(_x, _y))
|
||||||
|
setPixelColor(_x, _y - _thick, r, g, b);
|
||||||
|
|
||||||
|
zwx1 = x - thickness + 1;
|
||||||
|
zwx2 = x + dx + thickness - 1;
|
||||||
|
zwy1 = y + dy;
|
||||||
|
zwy2 = y + dy;
|
||||||
|
for (_thick = 0; _thick < thickness; _thick++)
|
||||||
|
for (_x = zwx1; _x <= zwx2; ++_x)
|
||||||
|
for (_y = zwy1; _y <= zwy2; _y++)
|
||||||
|
if (isInImage(_x, _y))
|
||||||
|
setPixelColor(_x, _y + _thick, r, g, b);
|
||||||
|
|
||||||
|
zwx1 = x;
|
||||||
|
zwx2 = x;
|
||||||
|
zwy1 = y;
|
||||||
|
zwy2 = y + dy;
|
||||||
|
for (_thick = 0; _thick < thickness; _thick++)
|
||||||
|
for (_x = zwx1; _x <= zwx2; ++_x)
|
||||||
|
for (_y = zwy1; _y <= zwy2; _y++)
|
||||||
|
if (isInImage(_x, _y))
|
||||||
|
setPixelColor(_x - _thick, _y, r, g, b);
|
||||||
|
|
||||||
|
zwx1 = x + dx;
|
||||||
|
zwx2 = x + dx;
|
||||||
|
zwy1 = y;
|
||||||
|
zwy2 = y + dy;
|
||||||
|
for (_thick = 0; _thick < thickness; _thick++)
|
||||||
|
for (_x = zwx1; _x <= zwx2; ++_x)
|
||||||
|
for (_y = zwy1; _y <= zwy2; _y++)
|
||||||
|
if (isInImage(_x, _y))
|
||||||
|
setPixelColor(_x + _thick, _y, r, g, b);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImageBasis::drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness)
|
||||||
|
{
|
||||||
|
int _x, _y, _thick;
|
||||||
|
int _zwy1, _zwy2;
|
||||||
|
thickness = (thickness-1) / 2;
|
||||||
|
|
||||||
|
for (_thick = 0; _thick <= thickness; ++_thick)
|
||||||
|
for (_x = x1 - _thick; _x <= x2 + _thick; ++_x)
|
||||||
|
{
|
||||||
|
if (x2 == x1)
|
||||||
|
{
|
||||||
|
_zwy1 = y1;
|
||||||
|
_zwy2 = y2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_zwy1 = (y2 - y1) * (float)(_x - x1) / (float)(x2 - x1) + y1;
|
||||||
|
_zwy2 = (y2 - y1) * (float)(_x + 1 - x1) / (float)(x2 - x1) + y1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_y = _zwy1 - _thick; _y <= _zwy2 + _thick; _y++)
|
||||||
|
if (isInImage(_x, _y))
|
||||||
|
setPixelColor(_x, _y, r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness)
|
||||||
|
{
|
||||||
|
float deltarad, aktrad;
|
||||||
|
int _thick, _x, _y;
|
||||||
|
|
||||||
|
deltarad = 1 / (4 * M_PI * (rad + thickness - 1));
|
||||||
|
|
||||||
|
for (aktrad = 0; aktrad <= (2 * M_PI); aktrad += deltarad)
|
||||||
|
for (_thick = 0; _thick < thickness; ++_thick)
|
||||||
|
{
|
||||||
|
_x = sin(aktrad) * (rad + _thick) + x1;
|
||||||
|
_y = cos(aktrad) * (rad + _thick) + y1;
|
||||||
|
if (isInImage(_x, _y))
|
||||||
|
setPixelColor(_x, _y, r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageBasis::CImageBasis()
|
||||||
|
{
|
||||||
|
this->externalImage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageBasis::CImageBasis(std::string _image)
|
||||||
|
{
|
||||||
|
// printf("Start CImageBasis\n");
|
||||||
|
this->channels = 3;
|
||||||
|
this->externalImage = false;
|
||||||
|
this->filename = _image;
|
||||||
|
// printf("CImageBasis before load\n");
|
||||||
|
// printf(_image.c_str()); printf("\n");
|
||||||
|
this->rgb_image = stbi_load(_image.c_str(), &(this->width), &(this->height), &(this->bpp), this->channels);
|
||||||
|
// printf("CImageBasis after load\n");
|
||||||
|
// printf("w %d, h %d, b %d, c %d", this->width, this->height, this->bpp, this->channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageBasis::CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp)
|
||||||
|
{
|
||||||
|
this->rgb_image = _rgb_image;
|
||||||
|
this->channels = _channels;
|
||||||
|
this->width = _width;
|
||||||
|
this->height = _height;
|
||||||
|
this->bpp = _bpp;
|
||||||
|
this->externalImage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageBasis::~CImageBasis()
|
||||||
|
{
|
||||||
|
if (!this->externalImage)
|
||||||
|
stbi_image_free(this->rgb_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImageBasis::SaveToFile(std::string _imageout)
|
||||||
|
{
|
||||||
|
string typ = getFileType(_imageout);
|
||||||
|
|
||||||
|
if ((typ == "jpg") || (typ == "JPG")) // ACHTUNG PROBLEMATISCH IM ESP32
|
||||||
|
{
|
||||||
|
stbi_write_jpg(_imageout.c_str(), this->width, this->height, this->channels, this->rgb_image, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((typ == "bmp") || (typ == "BMP"))
|
||||||
|
{
|
||||||
|
stbi_write_bmp(_imageout.c_str(), this->width, this->height, this->channels, this->rgb_image);
|
||||||
|
}
|
||||||
|
// stbi_write_jpg(_imageout.c_str(), this->width, this->height, this->channels, this->rgb_image, 0);
|
||||||
|
// stbi_write_bmp(_imageout.c_str(), this->width, this->height, this->channels, this->rgb_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void CAlignAndCutImage::Align(std::string _template0, int ref0_x, int ref0_y, std::string _template1, int ref1_x, int ref1_y, int deltax, int deltay, std::string imageROI)
|
||||||
|
{
|
||||||
|
int dx, dy;
|
||||||
|
int r0_x, r0_y, r1_x, r1_y;
|
||||||
|
|
||||||
|
CFindTemplate* ft = new CFindTemplate(this->filename);
|
||||||
|
|
||||||
|
r0_x = ref0_x;
|
||||||
|
r0_y = ref0_y;
|
||||||
|
ft->FindTemplate(_template0, &r0_x, &r0_y, deltax, deltay);
|
||||||
|
t0_dx = ft->tpl_width;
|
||||||
|
t0_dy = ft->tpl_height;
|
||||||
|
|
||||||
|
r1_x = ref1_x;
|
||||||
|
r1_y = ref1_y;
|
||||||
|
ft->FindTemplate(_template1, &r1_x, &r1_y, deltax, deltay);
|
||||||
|
t1_dx = ft->tpl_width;
|
||||||
|
t1_dy = ft->tpl_height;
|
||||||
|
|
||||||
|
delete ft;
|
||||||
|
|
||||||
|
|
||||||
|
dx = ref0_x - r0_x;
|
||||||
|
dy = ref0_y - r0_y;
|
||||||
|
|
||||||
|
r0_x += dx;
|
||||||
|
r0_y += dy;
|
||||||
|
|
||||||
|
r1_x += dx;
|
||||||
|
r1_y += dy;
|
||||||
|
|
||||||
|
float w_org, w_ist, d_winkel;
|
||||||
|
|
||||||
|
w_org = atan2(ref1_y - ref0_y, ref1_x - ref0_x);
|
||||||
|
w_ist = atan2(r1_y - r0_y, r1_x - r0_x);
|
||||||
|
|
||||||
|
d_winkel = (w_org - w_ist) * 180 / M_PI;
|
||||||
|
|
||||||
|
if (imageROI.length() > 0)
|
||||||
|
{
|
||||||
|
CImageBasis* imgzw = new CImageBasis(this->filename);
|
||||||
|
imgzw->drawRect(r0_x, r0_y, t0_dx, t0_dy, 255, 0, 0, 2);
|
||||||
|
imgzw->drawRect(r1_x, r1_y, t1_dx, t1_dy, 255, 0, 0, 2);
|
||||||
|
imgzw->SaveToFile(imageROI);
|
||||||
|
printf("Alignment: alignment ROI created: %s\n", imageROI.c_str());
|
||||||
|
delete imgzw;
|
||||||
|
}
|
||||||
|
|
||||||
|
CRotate rt(this->rgb_image, this->channels, this->width, this->height, this->bpp);
|
||||||
|
rt.Translate(dx, dy);
|
||||||
|
rt.Rotate(d_winkel, ref0_x, ref0_y);
|
||||||
|
printf("Alignment: dx %d - dy %d - rot %f\n", dx, dy, d_winkel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAlignAndCutImage::CutAndSave(std::string _template1, int x1, int y1, int dx, int dy)
|
||||||
|
{
|
||||||
|
|
||||||
|
int x2, y2;
|
||||||
|
|
||||||
|
x2 = x1 + dx;
|
||||||
|
y2 = y1 + dy;
|
||||||
|
x2 = min(x2, this->width - 1);
|
||||||
|
y2 = min(y2, this->height - 1);
|
||||||
|
|
||||||
|
dx = x2 - x1;
|
||||||
|
dy = y2 - y1;
|
||||||
|
|
||||||
|
int memsize = dx * dy * this->channels;
|
||||||
|
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
|
|
||||||
|
int x_source, y_source;
|
||||||
|
stbi_uc* p_target;
|
||||||
|
stbi_uc* p_source;
|
||||||
|
|
||||||
|
for (int x = x1; x < x2; ++x)
|
||||||
|
for (int y = y1; y < y2; ++y)
|
||||||
|
{
|
||||||
|
p_target = odata + (this->channels * ((y - y1) * dx + (x - x1)));
|
||||||
|
p_source = this->rgb_image + (this->channels * (y * this->width + x));
|
||||||
|
for (int channels = 0; channels < this->channels; ++channels)
|
||||||
|
p_target[channels] = p_source[channels];
|
||||||
|
}
|
||||||
|
|
||||||
|
// stbi_write_jpg(_template1.c_str(), dx, dy, this->channels, odata, 0);
|
||||||
|
stbi_write_bmp(_template1.c_str(), dx, dy, this->channels, odata);
|
||||||
|
|
||||||
|
stbi_image_free(odata);
|
||||||
|
}
|
||||||
95
code/lib/jomjol_image_proc/CFindTemplate.h
Normal file
95
code/lib/jomjol_image_proc/CFindTemplate.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __CFINDTEMPLATE
|
||||||
|
#define __CFINGTEMPLATE
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "stb_image.h"
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
#include "stb_image_resize.h"
|
||||||
|
|
||||||
|
|
||||||
|
class CImageBasis
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
uint8_t* rgb_image;
|
||||||
|
int channels;
|
||||||
|
int width, height, bpp;
|
||||||
|
bool externalImage;
|
||||||
|
std::string filename;
|
||||||
|
|
||||||
|
void memCopy(uint8_t* _source, uint8_t* _target, int _size);
|
||||||
|
bool isInImage(int x, int y);
|
||||||
|
|
||||||
|
public:
|
||||||
|
int getWidth(){return this->width;};
|
||||||
|
int getHeight(){return this->height;};
|
||||||
|
int getChannels(){return this->channels;};
|
||||||
|
void drawRect(int x, int y, int dx, int dy, int r = 255, int g = 255, int b = 255, int thickness = 1);
|
||||||
|
void drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness = 1);
|
||||||
|
void drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness = 1);
|
||||||
|
void setPixelColor(int x, int y, int r, int g, int b);
|
||||||
|
|
||||||
|
|
||||||
|
CImageBasis();
|
||||||
|
CImageBasis(std::string _image);
|
||||||
|
CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||||
|
uint8_t GetPixelColor(int x, int y, int ch);
|
||||||
|
|
||||||
|
~CImageBasis();
|
||||||
|
|
||||||
|
void SaveToFile(std::string _imageout);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CFindTemplate : public CImageBasis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int tpl_width, tpl_height, tpl_bpp;
|
||||||
|
CFindTemplate(std::string _image);
|
||||||
|
|
||||||
|
void FindTemplate(std::string _template, int* found_x, int* found_y, std::string _imageout);
|
||||||
|
void FindTemplate(std::string _template, int* found_x, int* found_y, int _dx, int _dy, std::string _imageout);
|
||||||
|
void FindTemplate(std::string _template, int* found_x, int* found_y);
|
||||||
|
void FindTemplate(std::string _template, int* found_x, int* found_y, int _dx, int _dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CRotate: public CImageBasis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CRotate(std::string _image) : CImageBasis(_image) {};
|
||||||
|
CRotate(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(_rgb_image, _channels, _width, _height, _bpp) {};
|
||||||
|
|
||||||
|
void Rotate(float _angle);
|
||||||
|
void Rotate(float _angle, int _centerx, int _centery);
|
||||||
|
void Translate(int _dx, int _dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CAlignAndCutImage : public CImageBasis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int t0_dx, t0_dy, t1_dx, t1_dy;
|
||||||
|
CAlignAndCutImage(std::string _image) : CImageBasis(_image) {};
|
||||||
|
|
||||||
|
void Align(std::string _template1, int x1, int y1, std::string _template2, int x2, int y2, int deltax = 40, int deltay = 40, std::string imageROI = "");
|
||||||
|
void CutAndSave(std::string _template1, int x1, int y1, int dx, int dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CResizeImage : public CImageBasis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CResizeImage(std::string _image) : CImageBasis(_image) {};
|
||||||
|
// CResizeImage(std::string _image, int _new_dx, int _new_dy);
|
||||||
|
void Resize(int _new_dx, int _new_dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
157
code/lib/jomjol_image_proc/Helper.cpp
Normal file
157
code/lib/jomjol_image_proc/Helper.cpp
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
//#pragma warning(disable : 4996)
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
//#define ISWINDOWS_TRUE
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
std::string FormatFileName(std::string input)
|
||||||
|
{
|
||||||
|
#ifdef ISWINDOWS_TRUE
|
||||||
|
input.erase(0, 1);
|
||||||
|
std::string os = "/";
|
||||||
|
std::string ns = "\\";
|
||||||
|
FindReplace(input, os, ns);
|
||||||
|
#endif
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void FindReplace(std::string& line, std::string& oldString, std::string& newString) {
|
||||||
|
const size_t oldSize = oldString.length();
|
||||||
|
|
||||||
|
// do nothing if line is shorter than the string to find
|
||||||
|
if (oldSize > line.length()) return;
|
||||||
|
|
||||||
|
const size_t newSize = newString.length();
|
||||||
|
for (size_t pos = 0; ; pos += newSize) {
|
||||||
|
// Locate the substring to replace
|
||||||
|
pos = line.find(oldString, pos);
|
||||||
|
if (pos == std::string::npos) return;
|
||||||
|
if (oldSize == newSize) {
|
||||||
|
// if they're same size, use std::string::replace
|
||||||
|
line.replace(pos, oldSize, newString);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if not same size, replace by erasing and inserting
|
||||||
|
line.erase(pos, oldSize);
|
||||||
|
line.insert(pos, newString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool ctype_space(const char c, string adddelimiter)
|
||||||
|
{
|
||||||
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 11)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (adddelimiter.find(c) != string::npos)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string trim(string istring, string adddelimiter)
|
||||||
|
{
|
||||||
|
bool trimmed = false;
|
||||||
|
|
||||||
|
if (ctype_space(istring[istring.length() - 1], adddelimiter))
|
||||||
|
{
|
||||||
|
istring.erase(istring.length() - 1);
|
||||||
|
trimmed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctype_space(istring[0], adddelimiter))
|
||||||
|
{
|
||||||
|
istring.erase(0, 1);
|
||||||
|
trimmed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((trimmed == false) || (istring.size() == 0))
|
||||||
|
{
|
||||||
|
return istring;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return trim(istring, adddelimiter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t findDelimiterPos(string input, string delimiter)
|
||||||
|
{
|
||||||
|
size_t pos = std::string::npos;
|
||||||
|
size_t zw;
|
||||||
|
string akt_del;
|
||||||
|
|
||||||
|
for (int anz = 0; anz < delimiter.length(); ++anz)
|
||||||
|
{
|
||||||
|
akt_del = delimiter[anz];
|
||||||
|
if ((zw = input.find(akt_del)) != std::string::npos)
|
||||||
|
{
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
if (zw < pos)
|
||||||
|
pos = zw;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pos = zw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CopyFile(string input, string output)
|
||||||
|
{
|
||||||
|
input = FormatFileName(input);
|
||||||
|
output = FormatFileName(output);
|
||||||
|
|
||||||
|
char cTemp;
|
||||||
|
FILE* fpSourceFile = fopen(input.c_str(), "rb");
|
||||||
|
FILE* fpTargetFile = fopen(output.c_str(), "wb");
|
||||||
|
|
||||||
|
// Code Section
|
||||||
|
|
||||||
|
// Read From The Source File - "Copy"
|
||||||
|
while (fread(&cTemp, 1, 1, fpSourceFile) == 1)
|
||||||
|
{
|
||||||
|
// Write To The Target File - "Paste"
|
||||||
|
fwrite(&cTemp, 1, 1, fpTargetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close The Files
|
||||||
|
fclose(fpSourceFile);
|
||||||
|
fclose(fpTargetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string getFileType(string filename)
|
||||||
|
{
|
||||||
|
int lastpos = filename.find(".", 0);
|
||||||
|
int neu_pos;
|
||||||
|
while ((neu_pos = filename.find(".", lastpos + 1)) > -1)
|
||||||
|
{
|
||||||
|
lastpos = neu_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
string zw = filename.substr(lastpos + 1, filename.size() - lastpos);
|
||||||
|
zw = toUpper(zw);
|
||||||
|
|
||||||
|
return zw;
|
||||||
|
}
|
||||||
|
|
||||||
|
string toUpper(string in)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < in.length(); ++i)
|
||||||
|
in[i] = toupper(in[i]);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
22
code/lib/jomjol_image_proc/Helper.h
Normal file
22
code/lib/jomjol_image_proc/Helper.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
std::string FormatFileName(std::string input);
|
||||||
|
void FindReplace(std::string& line, std::string& oldString, std::string& newString);
|
||||||
|
|
||||||
|
void CopyFile(string input, string output);
|
||||||
|
|
||||||
|
size_t findDelimiterPos(string input, string delimiter);
|
||||||
|
//string trim(string istring);
|
||||||
|
string trim(string istring, string adddelimiter = "");
|
||||||
|
bool ctype_space(const char c, string adddelimiter);
|
||||||
|
|
||||||
|
string getFileType(string filename);
|
||||||
|
|
||||||
|
string toUpper(string in);
|
||||||
|
|
||||||
|
|
||||||
5002
code/lib/jomjol_image_proc/bitmap_image.hpp
Normal file
5002
code/lib/jomjol_image_proc/bitmap_image.hpp
Normal file
File diff suppressed because it is too large
Load Diff
11
code/lib/jomjol_image_proc/make_stb.cpp
Normal file
11
code/lib/jomjol_image_proc/make_stb.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
|
#include "stb_image_resize.h"
|
||||||
7657
code/lib/jomjol_image_proc/stb_image.h
Normal file
7657
code/lib/jomjol_image_proc/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
2598
code/lib/jomjol_image_proc/stb_image_resize.h
Normal file
2598
code/lib/jomjol_image_proc/stb_image_resize.h
Normal file
File diff suppressed because it is too large
Load Diff
1671
code/lib/jomjol_image_proc/stb_image_write.h
Normal file
1671
code/lib/jomjol_image_proc/stb_image_write.h
Normal file
File diff suppressed because it is too large
Load Diff
41
code/lib/jomjol_logfile/ClassLogFile.cpp
Normal file
41
code/lib/jomjol_logfile/ClassLogFile.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "ClassLogFile.h"
|
||||||
|
#include "time_sntp.h"
|
||||||
|
|
||||||
|
ClassLogFile LogFile("/sdcard/log.txt");
|
||||||
|
|
||||||
|
void ClassLogFile::WriteToFile(std::string info, bool _time)
|
||||||
|
{
|
||||||
|
FILE* pFile;
|
||||||
|
std::string zwtime;
|
||||||
|
|
||||||
|
|
||||||
|
pFile = fopen(logfile.c_str(), "a+");
|
||||||
|
|
||||||
|
if (_time)
|
||||||
|
{
|
||||||
|
time_t rawtime;
|
||||||
|
struct tm* timeinfo;
|
||||||
|
char buffer[80];
|
||||||
|
|
||||||
|
time(&rawtime);
|
||||||
|
timeinfo = localtime(&rawtime);
|
||||||
|
|
||||||
|
strftime(buffer, 80, "%Y-%m-%d_%H-%M-%S", timeinfo);
|
||||||
|
|
||||||
|
zwtime = std::string(buffer);
|
||||||
|
info = zwtime + ": " + info;
|
||||||
|
}
|
||||||
|
fputs(info.c_str(), pFile);
|
||||||
|
fputs("\n", pFile);
|
||||||
|
|
||||||
|
fclose(pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassLogFile::ClassLogFile(std::string _logfile)
|
||||||
|
{
|
||||||
|
logfile = _logfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassLogFile::~ClassLogFile()
|
||||||
|
{
|
||||||
|
}
|
||||||
17
code/lib/jomjol_logfile/ClassLogFile.h
Normal file
17
code/lib/jomjol_logfile/ClassLogFile.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
class ClassLogFile
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/* data */
|
||||||
|
std::string logfile;
|
||||||
|
public:
|
||||||
|
ClassLogFile(std::string _logfile);
|
||||||
|
~ClassLogFile();
|
||||||
|
|
||||||
|
void WriteToFile(std::string info, bool _time = true);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern ClassLogFile LogFile;
|
||||||
290
code/lib/jomjol_tfliteclass/CTfLiteClass.cpp
Normal file
290
code/lib/jomjol_tfliteclass/CTfLiteClass.cpp
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
#include "CTfLiteClass.h"
|
||||||
|
|
||||||
|
#include "bitmap_image.hpp"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
float CTfLiteClass::GetOutputValue(int nr)
|
||||||
|
{
|
||||||
|
TfLiteTensor* output2 = this->interpreter->output(0);
|
||||||
|
|
||||||
|
int numeroutput = output2->dims->data[1];
|
||||||
|
if ((nr+1) > numeroutput)
|
||||||
|
return -1000;
|
||||||
|
|
||||||
|
return output2->data.f[nr];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CTfLiteClass::GetClassFromImage(std::string _fn)
|
||||||
|
{
|
||||||
|
if (!LoadInputImage(_fn))
|
||||||
|
return -1000;
|
||||||
|
|
||||||
|
Invoke();
|
||||||
|
return GetOutClassification();
|
||||||
|
// return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CTfLiteClass::GetOutClassification()
|
||||||
|
{
|
||||||
|
TfLiteTensor* output2 = interpreter->output(0);
|
||||||
|
|
||||||
|
float zw_max = 0;
|
||||||
|
float zw;
|
||||||
|
int zw_class = -1;
|
||||||
|
|
||||||
|
if (output2 == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int numeroutput = output2->dims->data[1];
|
||||||
|
for (int i = 0; i < numeroutput; ++i)
|
||||||
|
{
|
||||||
|
zw = output2->data.f[i];
|
||||||
|
if (zw > zw_max)
|
||||||
|
{
|
||||||
|
zw_max = zw;
|
||||||
|
zw_class = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// printf("Result Ziffer: %d\n", zw_class);
|
||||||
|
return zw_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTfLiteClass::GetInputDimension(bool silent = false)
|
||||||
|
{
|
||||||
|
TfLiteTensor* input2 = this->interpreter->input(0);
|
||||||
|
|
||||||
|
int numdim = input2->dims->size;
|
||||||
|
if (!silent) printf("NumDimension: %d\n", numdim);
|
||||||
|
|
||||||
|
int sizeofdim;
|
||||||
|
for (int j = 0; j < numdim; ++j)
|
||||||
|
{
|
||||||
|
sizeofdim = input2->dims->data[j];
|
||||||
|
if (!silent) printf("SizeOfDimension %d: %d\n", j, sizeofdim);
|
||||||
|
if (j == 1) im_height = sizeofdim;
|
||||||
|
if (j == 2) im_width = sizeofdim;
|
||||||
|
if (j == 3) im_channel = sizeofdim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CTfLiteClass::GetOutPut()
|
||||||
|
{
|
||||||
|
TfLiteTensor* output2 = this->interpreter->output(0);
|
||||||
|
|
||||||
|
int numdim = output2->dims->size;
|
||||||
|
printf("NumDimension: %d\n", numdim);
|
||||||
|
|
||||||
|
int sizeofdim;
|
||||||
|
for (int j = 0; j < numdim; ++j)
|
||||||
|
{
|
||||||
|
sizeofdim = output2->dims->data[j];
|
||||||
|
printf("SizeOfDimension %d: %d\n", j, sizeofdim);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float fo;
|
||||||
|
|
||||||
|
// Process the inference results.
|
||||||
|
int numeroutput = output2->dims->data[1];
|
||||||
|
for (int i = 0; i < numeroutput; ++i)
|
||||||
|
{
|
||||||
|
fo = output2->data.f[i];
|
||||||
|
printf("Result %d: %f\n", i, fo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTfLiteClass::Invoke()
|
||||||
|
{
|
||||||
|
interpreter->Invoke();
|
||||||
|
// printf("Invoke Done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mit CImageBasis --> funktioniert nicht, keine Ahnung warum !!!
|
||||||
|
|
||||||
|
bool CTfLiteClass::LoadInputImage(std::string _fn)
|
||||||
|
{
|
||||||
|
|
||||||
|
CImageBasis *cib = new CImageBasis(_fn);
|
||||||
|
|
||||||
|
GetInputDimension(true);
|
||||||
|
|
||||||
|
printf("TFlite load image size: %d x %d x %d\n", cib->getWidth(), cib->getHeight(), cib->getChannels());
|
||||||
|
// printf("TFlite expected image size: %d x %d x %d\n", im_width, im_height, im_channel);
|
||||||
|
|
||||||
|
if ((cib->getWidth() != im_width) || (cib->getHeight() != im_height) || (cib->getChannels() != im_channel))
|
||||||
|
{
|
||||||
|
printf("Input Imagesize Wrong.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_i = 0;
|
||||||
|
float* input_data_ptr = (interpreter->input(0))->data.f;
|
||||||
|
uint8_t ui8;
|
||||||
|
unsigned char zw;
|
||||||
|
|
||||||
|
for (int y = 0; y < cib->getHeight(); ++y)
|
||||||
|
for (int x = 0; x < cib->getWidth(); ++x)
|
||||||
|
{
|
||||||
|
printf("CIB: ");
|
||||||
|
for (int ch = 0; ch < cib->getChannels(); ++ch)
|
||||||
|
{
|
||||||
|
ui8 = cib->GetPixelColor(x, y, ch);
|
||||||
|
printf("%f ", (float) ui8);
|
||||||
|
*(input_data_ptr) = (float) ui8;
|
||||||
|
input_data_ptr++;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
delete cib;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool CTfLiteClass::LoadInputImage(std::string _fn)
|
||||||
|
{
|
||||||
|
bitmap_image image(_fn);
|
||||||
|
unsigned int w = image.width();
|
||||||
|
unsigned int h = image.height();
|
||||||
|
unsigned char red, green, blue;
|
||||||
|
|
||||||
|
input_i = 0;
|
||||||
|
float* input_data_ptr = (interpreter->input(0))->data.f;
|
||||||
|
|
||||||
|
for (int y = 0; y < h; ++y)
|
||||||
|
for (int x = 0; x < w; ++x)
|
||||||
|
{
|
||||||
|
red = image.red_channel(x, y);
|
||||||
|
green = image.green_channel(x, y);
|
||||||
|
blue = image.blue_channel(x, y);
|
||||||
|
*(input_data_ptr) = (float) red;
|
||||||
|
input_data_ptr++;
|
||||||
|
*(input_data_ptr) = (float) green;
|
||||||
|
input_data_ptr++;
|
||||||
|
*(input_data_ptr) = (float) blue;
|
||||||
|
input_data_ptr++;
|
||||||
|
|
||||||
|
printf("BMP: %f %f %f\n", (float) red, (float) green, (float) blue);
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CTfLiteClass::MakeAllocate()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
this->micro_op_resolver.AddBuiltin(
|
||||||
|
tflite::BuiltinOperator_RESHAPE,
|
||||||
|
tflite::ops::micro::Register_RESHAPE());
|
||||||
|
this->micro_op_resolver.AddBuiltin(tflite::BuiltinOperator_CONV_2D,
|
||||||
|
tflite::ops::micro::Register_CONV_2D());
|
||||||
|
this->micro_op_resolver.AddBuiltin(tflite::BuiltinOperator_FULLY_CONNECTED,
|
||||||
|
tflite::ops::micro::Register_FULLY_CONNECTED());
|
||||||
|
this->micro_op_resolver.AddBuiltin(tflite::BuiltinOperator_SOFTMAX,
|
||||||
|
tflite::ops::micro::Register_SOFTMAX());
|
||||||
|
this->micro_op_resolver.AddBuiltin(tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
|
||||||
|
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
|
||||||
|
|
||||||
|
|
||||||
|
this->interpreter = new tflite::MicroInterpreter(this->model, this->micro_op_resolver, this->tensor_arena, this->kTensorArenaSize, this->error_reporter);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static tflite::ops::micro::AllOpsResolver resolver;
|
||||||
|
this->interpreter = new tflite::MicroInterpreter(this->model, resolver, this->tensor_arena, this->kTensorArenaSize, this->error_reporter);
|
||||||
|
|
||||||
|
TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
|
||||||
|
if (allocate_status != kTfLiteOk) {
|
||||||
|
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
|
||||||
|
this->GetInputDimension();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Allocate Done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTfLiteClass::GetInputTensorSize(){
|
||||||
|
float *zw = this->input;
|
||||||
|
int test = sizeof(zw);
|
||||||
|
printf("Input Tensor Dimension: %d\n", test);
|
||||||
|
|
||||||
|
printf("Input Tensor Dimension: %d\n", test);
|
||||||
|
}
|
||||||
|
|
||||||
|
long CTfLiteClass::GetFileSize(std::string filename)
|
||||||
|
{
|
||||||
|
struct stat stat_buf;
|
||||||
|
long rc = stat(filename.c_str(), &stat_buf);
|
||||||
|
return rc == 0 ? stat_buf.st_size : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned char* CTfLiteClass::ReadFileToCharArray(std::string _fn)
|
||||||
|
{
|
||||||
|
long size;
|
||||||
|
|
||||||
|
size = this->GetFileSize(_fn);
|
||||||
|
|
||||||
|
if (size == -1)
|
||||||
|
{
|
||||||
|
printf("\nFile existiert nicht.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned char *result = (unsigned char*) malloc(size);
|
||||||
|
|
||||||
|
if(result != NULL) {
|
||||||
|
// printf("\nSpeicher ist reserviert\n");
|
||||||
|
FILE* f = fopen(_fn.c_str(), "rb"); // vorher nur "r"
|
||||||
|
fread(result, 1, size, f);
|
||||||
|
fclose(f);
|
||||||
|
}else {
|
||||||
|
printf("\nKein freier Speicher vorhanden.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTfLiteClass::LoadModel(std::string _fn){
|
||||||
|
|
||||||
|
|
||||||
|
this->error_reporter = new tflite::MicroErrorReporter;
|
||||||
|
|
||||||
|
unsigned char *rd;
|
||||||
|
rd = this->ReadFileToCharArray(_fn.c_str());
|
||||||
|
// printf("loadedfile: %d", (int) rd);
|
||||||
|
|
||||||
|
this->model = tflite::GetModel(rd);
|
||||||
|
free(rd);
|
||||||
|
TFLITE_MINIMAL_CHECK(model != nullptr);
|
||||||
|
printf("tfile Loaded.\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CTfLiteClass::CTfLiteClass()
|
||||||
|
{
|
||||||
|
// this->accessSD = _accessSD;
|
||||||
|
this->model = nullptr;
|
||||||
|
this->interpreter = nullptr;
|
||||||
|
this->input = nullptr;
|
||||||
|
this->output = nullptr;
|
||||||
|
this->kTensorArenaSize = 600 * 1024;
|
||||||
|
this->tensor_arena = new uint8_t[kTensorArenaSize];
|
||||||
|
|
||||||
|
// micro_op_resolver.AddBuiltin(tflite::BuiltinOperator_CONV_2D,
|
||||||
|
// tflite::ops::micro::Register_CONV_2D());
|
||||||
|
}
|
||||||
|
|
||||||
|
CTfLiteClass::~CTfLiteClass()
|
||||||
|
{
|
||||||
|
delete this->tensor_arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
72
code/lib/jomjol_tfliteclass/CTfLiteClass.h
Normal file
72
code/lib/jomjol_tfliteclass/CTfLiteClass.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef __CFINDTEMPLATE
|
||||||
|
#define __CFINGTEMPLATE
|
||||||
|
|
||||||
|
#define TFLITE_MINIMAL_CHECK(x) \
|
||||||
|
if (!(x)) { \
|
||||||
|
fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
|
||||||
|
exit(1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
//#include "CAccessSD.h"
|
||||||
|
#include "CFindTemplate.h"
|
||||||
|
|
||||||
|
#include "tensorflow/lite/micro/kernels/all_ops_resolver.h"
|
||||||
|
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||||
|
#include "tensorflow/lite/micro/micro_interpreter.h"
|
||||||
|
#include "tensorflow/lite/schema/schema_generated.h"
|
||||||
|
#include "tensorflow/lite/version.h"
|
||||||
|
#include "tensorflow/lite/micro/kernels/micro_ops.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
//extern CAccessSDClass accessSD;
|
||||||
|
|
||||||
|
class CTfLiteClass
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
// CAccessSDClass *accessSD;
|
||||||
|
|
||||||
|
tflite::ErrorReporter* error_reporter;
|
||||||
|
|
||||||
|
const tflite::Model* model;
|
||||||
|
tflite::MicroInterpreter* interpreter;
|
||||||
|
// TfLiteTensor* input = nullptr;
|
||||||
|
TfLiteTensor* output = nullptr;
|
||||||
|
static tflite::ops::micro::AllOpsResolver *resolver;
|
||||||
|
|
||||||
|
tflite::MicroOpResolver<5> micro_op_resolver;
|
||||||
|
|
||||||
|
|
||||||
|
int kTensorArenaSize;
|
||||||
|
uint8_t *tensor_arena;
|
||||||
|
|
||||||
|
float* input;
|
||||||
|
int input_i;
|
||||||
|
|
||||||
|
int im_height, im_width, im_channel;
|
||||||
|
|
||||||
|
long GetFileSize(std::string filename);
|
||||||
|
unsigned char* ReadFileToCharArray(std::string _fn);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// CTfLiteClass(CAccessSDClass *_accessSD);
|
||||||
|
CTfLiteClass();
|
||||||
|
~CTfLiteClass();
|
||||||
|
void LoadModel(std::string _fn);
|
||||||
|
void MakeAllocate();
|
||||||
|
void GetInputTensorSize();
|
||||||
|
bool LoadInputImage(std::string _fn);
|
||||||
|
void Invoke();
|
||||||
|
void GetOutPut();
|
||||||
|
int GetOutClassification();
|
||||||
|
int GetClassFromImage(std::string _fn);
|
||||||
|
|
||||||
|
float GetOutputValue(int nr);
|
||||||
|
void GetInputDimension(bool silent);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
137
code/lib/jomjol_time_sntp/time_sntp.cpp
Normal file
137
code/lib/jomjol_time_sntp/time_sntp.cpp
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#include "time_sntp.h"
|
||||||
|
|
||||||
|
/* LwIP SNTP example
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||||
|
Unless required by applicable law or agreed to in writing, this
|
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*/
|
||||||
|
#include <string>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "esp_sleep.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "protocol_examples_common.h"
|
||||||
|
#include "esp_sntp.h"
|
||||||
|
|
||||||
|
static const char *TAG = "sntp";
|
||||||
|
|
||||||
|
RTC_DATA_ATTR int boot_count = 0;
|
||||||
|
|
||||||
|
/* Variable holding number of times ESP32 restarted since first boot.
|
||||||
|
* It is placed into RTC memory using RTC_DATA_ATTR and
|
||||||
|
* maintains its value when ESP32 wakes from deep sleep.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void obtain_time(void);
|
||||||
|
static void initialize_sntp(void);
|
||||||
|
|
||||||
|
|
||||||
|
void time_sync_notification_cb(struct timeval *tv)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Notification of a time synchronization event");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string gettimestring(const char * frm)
|
||||||
|
{
|
||||||
|
time_t now;
|
||||||
|
struct tm timeinfo;
|
||||||
|
time(&now);
|
||||||
|
localtime_r(&now, &timeinfo);
|
||||||
|
// Is time set? If not, tm_year will be (1970 - 1900).
|
||||||
|
if (timeinfo.tm_year < (2016 - 1900)) {
|
||||||
|
ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
|
||||||
|
obtain_time();
|
||||||
|
// update 'now' variable with current time
|
||||||
|
time(&now);
|
||||||
|
}
|
||||||
|
char strftime_buf[64];
|
||||||
|
|
||||||
|
setenv("TZ", "UTC-2", 1);
|
||||||
|
tzset();
|
||||||
|
localtime_r(&now, &timeinfo);
|
||||||
|
strftime(strftime_buf, sizeof(strftime_buf), frm, &timeinfo);
|
||||||
|
|
||||||
|
std::string result(strftime_buf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_time(void)
|
||||||
|
{
|
||||||
|
++boot_count;
|
||||||
|
ESP_LOGI(TAG, "Boot count: %d", boot_count);
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
struct tm timeinfo;
|
||||||
|
time(&now);
|
||||||
|
localtime_r(&now, &timeinfo);
|
||||||
|
// Is time set? If not, tm_year will be (1970 - 1900).
|
||||||
|
if (timeinfo.tm_year < (2016 - 1900)) {
|
||||||
|
ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
|
||||||
|
obtain_time();
|
||||||
|
// update 'now' variable with current time
|
||||||
|
time(&now);
|
||||||
|
}
|
||||||
|
char strftime_buf[64];
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Set timezone to Eastern Standard Time and print local time
|
||||||
|
setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1);
|
||||||
|
tzset();
|
||||||
|
localtime_r(&now, &timeinfo);
|
||||||
|
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
|
||||||
|
ESP_LOGI(TAG, "The current date/time in New York is: %s", strftime_buf);
|
||||||
|
|
||||||
|
// Set timezone to China Standard Time
|
||||||
|
setenv("TZ", "CST-8", 1);
|
||||||
|
tzset();
|
||||||
|
localtime_r(&now, &timeinfo);
|
||||||
|
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
|
||||||
|
ESP_LOGI(TAG, "The current date/time in Shanghai is: %s", strftime_buf);
|
||||||
|
*/
|
||||||
|
// Set timezone to Berlin Standard Time
|
||||||
|
setenv("TZ", "UTC+9", 1);
|
||||||
|
tzset();
|
||||||
|
localtime_r(&now, &timeinfo);
|
||||||
|
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
|
||||||
|
ESP_LOGI(TAG, "The current date/time in Berlin is: %s", strftime_buf);
|
||||||
|
|
||||||
|
strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d_%H:%M", &timeinfo);
|
||||||
|
ESP_LOGI(TAG, "The current date/time in Berlin is: %s", strftime_buf);
|
||||||
|
|
||||||
|
std::string zw = gettimestring("%Y%m%d-%H%M%S");
|
||||||
|
printf("time %s\n", zw.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void obtain_time(void)
|
||||||
|
{
|
||||||
|
initialize_sntp();
|
||||||
|
|
||||||
|
// wait for time to be set
|
||||||
|
time_t now = 0;
|
||||||
|
struct tm timeinfo = {};
|
||||||
|
int retry = 0;
|
||||||
|
const int retry_count = 10;
|
||||||
|
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) {
|
||||||
|
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
time(&now);
|
||||||
|
localtime_r(&now, &timeinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize_sntp(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Initializing SNTP");
|
||||||
|
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||||
|
sntp_setservername(0, "pool.ntp.org");
|
||||||
|
sntp_set_time_sync_notification_cb(time_sync_notification_cb);
|
||||||
|
sntp_init();
|
||||||
|
}
|
||||||
20
code/lib/jomjol_time_sntp/time_sntp.h
Normal file
20
code/lib/jomjol_time_sntp/time_sntp.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "esp_sleep.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "esp_sntp.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern int boot_count;
|
||||||
|
|
||||||
|
void setup_time(void);
|
||||||
|
|
||||||
|
std::string gettimestring(const char * frm);
|
||||||
580
code/lib/sensors/ov2640.c
Normal file
580
code/lib/sensors/ov2640.c
Normal file
@@ -0,0 +1,580 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* OV2640 driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "sccb.h"
|
||||||
|
#include "ov2640.h"
|
||||||
|
#include "ov2640_regs.h"
|
||||||
|
#include "ov2640_settings.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#else
|
||||||
|
#include "esp_log.h"
|
||||||
|
static const char* TAG = "ov2640";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static volatile ov2640_bank_t reg_bank = BANK_MAX;
|
||||||
|
static int set_bank(sensor_t *sensor, ov2640_bank_t bank)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
if (bank != reg_bank) {
|
||||||
|
reg_bank = bank;
|
||||||
|
res = SCCB_Write(sensor->slv_addr, BANK_SEL, bank);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_regs(sensor_t *sensor, const uint8_t (*regs)[2])
|
||||||
|
{
|
||||||
|
int i=0, res = 0;
|
||||||
|
while (regs[i][0]) {
|
||||||
|
if (regs[i][0] == BANK_SEL) {
|
||||||
|
res = set_bank(sensor, regs[i][1]);
|
||||||
|
} else {
|
||||||
|
res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||||
|
}
|
||||||
|
if (res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_reg(sensor_t *sensor, ov2640_bank_t bank, uint8_t reg, uint8_t value)
|
||||||
|
{
|
||||||
|
int ret = set_bank(sensor, bank);
|
||||||
|
if(!ret) {
|
||||||
|
ret = SCCB_Write(sensor->slv_addr, reg, value);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t offset, uint8_t mask, uint8_t value)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t c_value, new_value;
|
||||||
|
|
||||||
|
ret = set_bank(sensor, bank);
|
||||||
|
if(ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
c_value = SCCB_Read(sensor->slv_addr, reg);
|
||||||
|
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
|
||||||
|
ret = SCCB_Write(sensor->slv_addr, reg, new_value);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_reg(sensor_t *sensor, ov2640_bank_t bank, uint8_t reg)
|
||||||
|
{
|
||||||
|
if(set_bank(sensor, bank)){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return SCCB_Read(sensor->slv_addr, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t get_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t offset, uint8_t mask)
|
||||||
|
{
|
||||||
|
return (read_reg(sensor, bank, reg) >> offset) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t mask, int enable)
|
||||||
|
{
|
||||||
|
return set_reg_bits(sensor, bank, reg, 0, mask, enable?mask:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define WRITE_REGS_OR_RETURN(regs) ret = write_regs(sensor, regs); if(ret){return ret;}
|
||||||
|
#define WRITE_REG_OR_RETURN(bank, reg, val) ret = write_reg(sensor, bank, reg, val); if(ret){return ret;}
|
||||||
|
#define SET_REG_BITS_OR_RETURN(bank, reg, offset, mask, val) ret = set_reg_bits(sensor, bank, reg, offset, mask, val); if(ret){return ret;}
|
||||||
|
|
||||||
|
static int reset(sensor_t *sensor)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
WRITE_REG_OR_RETURN(BANK_SENSOR, COM7, COM7_SRST);
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
WRITE_REGS_OR_RETURN(ov2640_settings_cif);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
sensor->pixformat = pixformat;
|
||||||
|
switch (pixformat) {
|
||||||
|
case PIXFORMAT_RGB565:
|
||||||
|
case PIXFORMAT_RGB888:
|
||||||
|
WRITE_REGS_OR_RETURN(ov2640_settings_rgb565);
|
||||||
|
break;
|
||||||
|
case PIXFORMAT_YUV422:
|
||||||
|
case PIXFORMAT_GRAYSCALE:
|
||||||
|
WRITE_REGS_OR_RETURN(ov2640_settings_yuv422);
|
||||||
|
break;
|
||||||
|
case PIXFORMAT_JPEG:
|
||||||
|
WRITE_REGS_OR_RETURN(ov2640_settings_jpeg3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!ret) {
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x, int offset_y, int max_x, int max_y, int w, int h){
|
||||||
|
int ret = 0;
|
||||||
|
const uint8_t (*regs)[2];
|
||||||
|
ov2640_clk_t c;
|
||||||
|
c.reserved = 0;
|
||||||
|
|
||||||
|
max_x /= 4;
|
||||||
|
max_y /= 4;
|
||||||
|
w /= 4;
|
||||||
|
h /= 4;
|
||||||
|
uint8_t win_regs[][2] = {
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{HSIZE, max_x & 0xFF},
|
||||||
|
{VSIZE, max_y & 0xFF},
|
||||||
|
{XOFFL, offset_x & 0xFF},
|
||||||
|
{YOFFL, offset_y & 0xFF},
|
||||||
|
{VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_y >> 8) & 0X07)},
|
||||||
|
{TEST, (max_x >> 2) & 0X80},
|
||||||
|
{ZMOW, (w)&0xFF},
|
||||||
|
{ZMOH, (h)&0xFF},
|
||||||
|
{ZMHH, ((h>>6)&0x04)|((w>>8)&0x03)},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
c.pclk_auto = 0;
|
||||||
|
c.pclk_div = 8;
|
||||||
|
c.clk_2x = 0;
|
||||||
|
c.clk_div = 0;
|
||||||
|
|
||||||
|
if(sensor->pixformat != PIXFORMAT_JPEG){
|
||||||
|
c.pclk_auto = 1;
|
||||||
|
c.clk_div = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == OV2640_MODE_CIF) {
|
||||||
|
regs = ov2640_settings_to_cif;
|
||||||
|
if(sensor->pixformat != PIXFORMAT_JPEG){
|
||||||
|
c.clk_div = 3;
|
||||||
|
}
|
||||||
|
} else if (mode == OV2640_MODE_SVGA) {
|
||||||
|
regs = ov2640_settings_to_svga;
|
||||||
|
} else {
|
||||||
|
regs = ov2640_settings_to_uxga;
|
||||||
|
c.pclk_div = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_BYPAS);
|
||||||
|
WRITE_REGS_OR_RETURN(regs);
|
||||||
|
WRITE_REGS_OR_RETURN(win_regs);
|
||||||
|
WRITE_REG_OR_RETURN(BANK_SENSOR, CLKRC, c.clk);
|
||||||
|
WRITE_REG_OR_RETURN(BANK_DSP, R_DVP_SP, c.pclk);
|
||||||
|
WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_EN);
|
||||||
|
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
//required when changing resolution
|
||||||
|
set_pixformat(sensor, sensor->pixformat);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
uint16_t w = resolution[framesize].width;
|
||||||
|
uint16_t h = resolution[framesize].height;
|
||||||
|
aspect_ratio_t ratio = resolution[framesize].aspect_ratio;
|
||||||
|
uint16_t max_x = ratio_table[ratio].max_x;
|
||||||
|
uint16_t max_y = ratio_table[ratio].max_y;
|
||||||
|
uint16_t offset_x = ratio_table[ratio].offset_x;
|
||||||
|
uint16_t offset_y = ratio_table[ratio].offset_y;
|
||||||
|
ov2640_sensor_mode_t mode = OV2640_MODE_UXGA;
|
||||||
|
|
||||||
|
sensor->status.framesize = framesize;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (framesize <= FRAMESIZE_CIF) {
|
||||||
|
mode = OV2640_MODE_CIF;
|
||||||
|
max_x /= 4;
|
||||||
|
max_y /= 4;
|
||||||
|
offset_x /= 4;
|
||||||
|
offset_y /= 4;
|
||||||
|
if(max_y > 296){
|
||||||
|
max_y = 296;
|
||||||
|
}
|
||||||
|
} else if (framesize <= FRAMESIZE_SVGA) {
|
||||||
|
mode = OV2640_MODE_SVGA;
|
||||||
|
max_x /= 2;
|
||||||
|
max_y /= 2;
|
||||||
|
offset_x /= 2;
|
||||||
|
offset_y /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = set_window(sensor, mode, offset_x, offset_y, max_x, max_y, w, h);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_contrast(sensor_t *sensor, int level)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
level += 3;
|
||||||
|
if (level <= 0 || level > NUM_CONTRAST_LEVELS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sensor->status.contrast = level-3;
|
||||||
|
for (int i=0; i<7; i++) {
|
||||||
|
WRITE_REG_OR_RETURN(BANK_DSP, contrast_regs[0][i], contrast_regs[level][i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_brightness(sensor_t *sensor, int level)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
level += 3;
|
||||||
|
if (level <= 0 || level > NUM_BRIGHTNESS_LEVELS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sensor->status.brightness = level-3;
|
||||||
|
for (int i=0; i<5; i++) {
|
||||||
|
WRITE_REG_OR_RETURN(BANK_DSP, brightness_regs[0][i], brightness_regs[level][i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_saturation(sensor_t *sensor, int level)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
level += 3;
|
||||||
|
if (level <= 0 || level > NUM_SATURATION_LEVELS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sensor->status.saturation = level-3;
|
||||||
|
for (int i=0; i<5; i++) {
|
||||||
|
WRITE_REG_OR_RETURN(BANK_DSP, saturation_regs[0][i], saturation_regs[level][i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_special_effect(sensor_t *sensor, int effect)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
effect++;
|
||||||
|
if (effect <= 0 || effect > NUM_SPECIAL_EFFECTS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sensor->status.special_effect = effect-1;
|
||||||
|
for (int i=0; i<5; i++) {
|
||||||
|
WRITE_REG_OR_RETURN(BANK_DSP, special_effects_regs[0][i], special_effects_regs[effect][i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_wb_mode(sensor_t *sensor, int mode)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
if (mode < 0 || mode > NUM_WB_MODES) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sensor->status.wb_mode = mode;
|
||||||
|
SET_REG_BITS_OR_RETURN(BANK_DSP, 0XC7, 6, 1, mode?1:0);
|
||||||
|
if(mode) {
|
||||||
|
for (int i=0; i<3; i++) {
|
||||||
|
WRITE_REG_OR_RETURN(BANK_DSP, wb_modes_regs[0][i], wb_modes_regs[mode][i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_ae_level(sensor_t *sensor, int level)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
level += 3;
|
||||||
|
if (level <= 0 || level > NUM_AE_LEVELS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sensor->status.ae_level = level-3;
|
||||||
|
for (int i=0; i<3; i++) {
|
||||||
|
WRITE_REG_OR_RETURN(BANK_SENSOR, ae_levels_regs[0][i], ae_levels_regs[level][i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_quality(sensor_t *sensor, int quality)
|
||||||
|
{
|
||||||
|
if(quality < 0) {
|
||||||
|
quality = 0;
|
||||||
|
} else if(quality > 63) {
|
||||||
|
quality = 63;
|
||||||
|
}
|
||||||
|
sensor->status.quality = quality;
|
||||||
|
return write_reg(sensor, BANK_DSP, QS, quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_agc_gain(sensor_t *sensor, int gain)
|
||||||
|
{
|
||||||
|
if(gain < 0) {
|
||||||
|
gain = 0;
|
||||||
|
} else if(gain > 30) {
|
||||||
|
gain = 30;
|
||||||
|
}
|
||||||
|
sensor->status.agc_gain = gain;
|
||||||
|
return write_reg(sensor, BANK_SENSOR, GAIN, agc_gain_tbl[gain]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_gainceiling_sensor(sensor_t *sensor, gainceiling_t gainceiling)
|
||||||
|
{
|
||||||
|
sensor->status.gainceiling = gainceiling;
|
||||||
|
//return write_reg(sensor, BANK_SENSOR, COM9, COM9_AGC_SET(gainceiling));
|
||||||
|
return set_reg_bits(sensor, BANK_SENSOR, COM9, 5, 7, gainceiling);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_aec_value(sensor_t *sensor, int value)
|
||||||
|
{
|
||||||
|
if(value < 0) {
|
||||||
|
value = 0;
|
||||||
|
} else if(value > 1200) {
|
||||||
|
value = 1200;
|
||||||
|
}
|
||||||
|
sensor->status.aec_value = value;
|
||||||
|
return set_reg_bits(sensor, BANK_SENSOR, REG04, 0, 3, value & 0x3)
|
||||||
|
|| write_reg(sensor, BANK_SENSOR, AEC, (value >> 2) & 0xFF)
|
||||||
|
|| set_reg_bits(sensor, BANK_SENSOR, REG45, 0, 0x3F, value >> 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_aec2(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.aec2 = enable;
|
||||||
|
return set_reg_bits(sensor, BANK_DSP, CTRL0, 6, 1, enable?0:1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_colorbar(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.colorbar = enable;
|
||||||
|
return write_reg_bits(sensor, BANK_SENSOR, COM7, COM7_COLOR_BAR, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_agc_sensor(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.agc = enable;
|
||||||
|
return write_reg_bits(sensor, BANK_SENSOR, COM8, COM8_AGC_EN, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_aec_sensor(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.aec = enable;
|
||||||
|
return write_reg_bits(sensor, BANK_SENSOR, COM8, COM8_AEC_EN, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_hmirror_sensor(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.hmirror = enable;
|
||||||
|
return write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_HFLIP_IMG, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_vflip_sensor(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
sensor->status.vflip = enable;
|
||||||
|
ret = write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_VREF_EN, enable?1:0);
|
||||||
|
return ret & write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_VFLIP_IMG, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.raw_gma = enable;
|
||||||
|
return set_reg_bits(sensor, BANK_DSP, CTRL1, 5, 1, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_awb_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.awb = enable;
|
||||||
|
return set_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_awb_gain_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.awb_gain = enable;
|
||||||
|
return set_reg_bits(sensor, BANK_DSP, CTRL1, 2, 1, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_lenc_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.lenc = enable;
|
||||||
|
return set_reg_bits(sensor, BANK_DSP, CTRL1, 1, 1, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_dcw_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.dcw = enable;
|
||||||
|
return set_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_bpc_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.bpc = enable;
|
||||||
|
return set_reg_bits(sensor, BANK_DSP, CTRL3, 7, 1, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_wpc_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
sensor->status.wpc = enable;
|
||||||
|
return set_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1, enable?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//unsupported
|
||||||
|
static int set_sharpness(sensor_t *sensor, int level)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_denoise(sensor_t *sensor, int level)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||||
|
{
|
||||||
|
int ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF);
|
||||||
|
if(ret > 0){
|
||||||
|
ret &= mask;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF);
|
||||||
|
if(ret < 0){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
value = (ret & ~mask) | (value & mask);
|
||||||
|
ret = write_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF, value);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning)
|
||||||
|
{
|
||||||
|
return set_window(sensor, (ov2640_sensor_mode_t)startX, offsetX, offsetY, totalX, totalY, outputX, outputY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||||
|
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||||
|
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_status(sensor_t *sensor){
|
||||||
|
sensor->status.brightness = 0;
|
||||||
|
sensor->status.contrast = 0;
|
||||||
|
sensor->status.saturation = 0;
|
||||||
|
sensor->status.ae_level = 0;
|
||||||
|
sensor->status.special_effect = 0;
|
||||||
|
sensor->status.wb_mode = 0;
|
||||||
|
|
||||||
|
sensor->status.agc_gain = 30;
|
||||||
|
int agc_gain = read_reg(sensor, BANK_SENSOR, GAIN);
|
||||||
|
for (int i=0; i<30; i++){
|
||||||
|
if(agc_gain >= agc_gain_tbl[i] && agc_gain < agc_gain_tbl[i+1]){
|
||||||
|
sensor->status.agc_gain = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sensor->status.aec_value = ((uint16_t)get_reg_bits(sensor, BANK_SENSOR, REG45, 0, 0x3F) << 10)
|
||||||
|
| ((uint16_t)read_reg(sensor, BANK_SENSOR, AEC) << 2)
|
||||||
|
| get_reg_bits(sensor, BANK_SENSOR, REG04, 0, 3);//0 - 1200
|
||||||
|
sensor->status.quality = read_reg(sensor, BANK_DSP, QS);
|
||||||
|
sensor->status.gainceiling = get_reg_bits(sensor, BANK_SENSOR, COM9, 5, 7);
|
||||||
|
|
||||||
|
sensor->status.awb = get_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1);
|
||||||
|
sensor->status.awb_gain = get_reg_bits(sensor, BANK_DSP, CTRL1, 2, 1);
|
||||||
|
sensor->status.aec = get_reg_bits(sensor, BANK_SENSOR, COM8, 0, 1);
|
||||||
|
sensor->status.aec2 = get_reg_bits(sensor, BANK_DSP, CTRL0, 6, 1);
|
||||||
|
sensor->status.agc = get_reg_bits(sensor, BANK_SENSOR, COM8, 2, 1);
|
||||||
|
sensor->status.bpc = get_reg_bits(sensor, BANK_DSP, CTRL3, 7, 1);
|
||||||
|
sensor->status.wpc = get_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1);
|
||||||
|
sensor->status.raw_gma = get_reg_bits(sensor, BANK_DSP, CTRL1, 5, 1);
|
||||||
|
sensor->status.lenc = get_reg_bits(sensor, BANK_DSP, CTRL1, 1, 1);
|
||||||
|
sensor->status.hmirror = get_reg_bits(sensor, BANK_SENSOR, REG04, 7, 1);
|
||||||
|
sensor->status.vflip = get_reg_bits(sensor, BANK_SENSOR, REG04, 6, 1);
|
||||||
|
sensor->status.dcw = get_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1);
|
||||||
|
sensor->status.colorbar = get_reg_bits(sensor, BANK_SENSOR, COM7, 1, 1);
|
||||||
|
|
||||||
|
sensor->status.sharpness = 0;//not supported
|
||||||
|
sensor->status.denoise = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ov2640_init(sensor_t *sensor)
|
||||||
|
{
|
||||||
|
sensor->reset = reset;
|
||||||
|
sensor->init_status = init_status;
|
||||||
|
sensor->set_pixformat = set_pixformat;
|
||||||
|
sensor->set_framesize = set_framesize;
|
||||||
|
sensor->set_contrast = set_contrast;
|
||||||
|
sensor->set_brightness= set_brightness;
|
||||||
|
sensor->set_saturation= set_saturation;
|
||||||
|
|
||||||
|
sensor->set_quality = set_quality;
|
||||||
|
sensor->set_colorbar = set_colorbar;
|
||||||
|
|
||||||
|
sensor->set_gainceiling = set_gainceiling_sensor;
|
||||||
|
sensor->set_gain_ctrl = set_agc_sensor;
|
||||||
|
sensor->set_exposure_ctrl = set_aec_sensor;
|
||||||
|
sensor->set_hmirror = set_hmirror_sensor;
|
||||||
|
sensor->set_vflip = set_vflip_sensor;
|
||||||
|
|
||||||
|
sensor->set_whitebal = set_awb_dsp;
|
||||||
|
sensor->set_aec2 = set_aec2;
|
||||||
|
sensor->set_aec_value = set_aec_value;
|
||||||
|
sensor->set_special_effect = set_special_effect;
|
||||||
|
sensor->set_wb_mode = set_wb_mode;
|
||||||
|
sensor->set_ae_level = set_ae_level;
|
||||||
|
|
||||||
|
sensor->set_dcw = set_dcw_dsp;
|
||||||
|
sensor->set_bpc = set_bpc_dsp;
|
||||||
|
sensor->set_wpc = set_wpc_dsp;
|
||||||
|
sensor->set_awb_gain = set_awb_gain_dsp;
|
||||||
|
sensor->set_agc_gain = set_agc_gain;
|
||||||
|
|
||||||
|
sensor->set_raw_gma = set_raw_gma_dsp;
|
||||||
|
sensor->set_lenc = set_lenc_dsp;
|
||||||
|
|
||||||
|
//not supported
|
||||||
|
sensor->set_sharpness = set_sharpness;
|
||||||
|
sensor->set_denoise = set_denoise;
|
||||||
|
|
||||||
|
sensor->get_reg = get_reg;
|
||||||
|
sensor->set_reg = set_reg;
|
||||||
|
sensor->set_res_raw = set_res_raw;
|
||||||
|
sensor->set_pll = _set_pll;
|
||||||
|
sensor->set_xclk = set_xclk;
|
||||||
|
ESP_LOGD(TAG, "OV2640 Attached");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
13
code/lib/sensors/ov2640.h
Normal file
13
code/lib/sensors/ov2640.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* OV2640 driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef __OV2640_H__
|
||||||
|
#define __OV2640_H__
|
||||||
|
#include "sensor.h"
|
||||||
|
int ov2640_init(sensor_t *sensor);
|
||||||
|
#endif // __OV2640_H__
|
||||||
216
code/lib/sensors/ov2640_regs.h
Normal file
216
code/lib/sensors/ov2640_regs.h
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* OV2640 register definitions.
|
||||||
|
*/
|
||||||
|
#ifndef __REG_REGS_H__
|
||||||
|
#define __REG_REGS_H__
|
||||||
|
/* DSP register bank FF=0x00*/
|
||||||
|
#define R_BYPASS 0x05
|
||||||
|
#define QS 0x44
|
||||||
|
#define CTRLI 0x50
|
||||||
|
#define HSIZE 0x51
|
||||||
|
#define VSIZE 0x52
|
||||||
|
#define XOFFL 0x53
|
||||||
|
#define YOFFL 0x54
|
||||||
|
#define VHYX 0x55
|
||||||
|
#define DPRP 0x56
|
||||||
|
#define TEST 0x57
|
||||||
|
#define ZMOW 0x5A
|
||||||
|
#define ZMOH 0x5B
|
||||||
|
#define ZMHH 0x5C
|
||||||
|
#define BPADDR 0x7C
|
||||||
|
#define BPDATA 0x7D
|
||||||
|
#define CTRL2 0x86
|
||||||
|
#define CTRL3 0x87
|
||||||
|
#define SIZEL 0x8C
|
||||||
|
#define HSIZE8 0xC0
|
||||||
|
#define VSIZE8 0xC1
|
||||||
|
#define CTRL0 0xC2
|
||||||
|
#define CTRL1 0xC3
|
||||||
|
#define R_DVP_SP 0xD3
|
||||||
|
#define IMAGE_MODE 0xDA
|
||||||
|
#define RESET 0xE0
|
||||||
|
#define MS_SP 0xF0
|
||||||
|
#define SS_ID 0xF7
|
||||||
|
#define SS_CTRL 0xF7
|
||||||
|
#define MC_BIST 0xF9
|
||||||
|
#define MC_AL 0xFA
|
||||||
|
#define MC_AH 0xFB
|
||||||
|
#define MC_D 0xFC
|
||||||
|
#define P_CMD 0xFD
|
||||||
|
#define P_STATUS 0xFE
|
||||||
|
#define BANK_SEL 0xFF
|
||||||
|
|
||||||
|
#define CTRLI_LP_DP 0x80
|
||||||
|
#define CTRLI_ROUND 0x40
|
||||||
|
|
||||||
|
#define CTRL0_AEC_EN 0x80
|
||||||
|
#define CTRL0_AEC_SEL 0x40
|
||||||
|
#define CTRL0_STAT_SEL 0x20
|
||||||
|
#define CTRL0_VFIRST 0x10
|
||||||
|
#define CTRL0_YUV422 0x08
|
||||||
|
#define CTRL0_YUV_EN 0x04
|
||||||
|
#define CTRL0_RGB_EN 0x02
|
||||||
|
#define CTRL0_RAW_EN 0x01
|
||||||
|
|
||||||
|
#define CTRL2_DCW_EN 0x20
|
||||||
|
#define CTRL2_SDE_EN 0x10
|
||||||
|
#define CTRL2_UV_ADJ_EN 0x08
|
||||||
|
#define CTRL2_UV_AVG_EN 0x04
|
||||||
|
#define CTRL2_CMX_EN 0x01
|
||||||
|
|
||||||
|
#define CTRL3_BPC_EN 0x80
|
||||||
|
#define CTRL3_WPC_EN 0x40
|
||||||
|
|
||||||
|
#define R_DVP_SP_AUTO_MODE 0x80
|
||||||
|
|
||||||
|
#define R_BYPASS_DSP_EN 0x00
|
||||||
|
#define R_BYPASS_DSP_BYPAS 0x01
|
||||||
|
|
||||||
|
#define IMAGE_MODE_Y8_DVP_EN 0x40
|
||||||
|
#define IMAGE_MODE_JPEG_EN 0x10
|
||||||
|
#define IMAGE_MODE_YUV422 0x00
|
||||||
|
#define IMAGE_MODE_RAW10 0x04
|
||||||
|
#define IMAGE_MODE_RGB565 0x08
|
||||||
|
#define IMAGE_MODE_HREF_VSYNC 0x02
|
||||||
|
#define IMAGE_MODE_LBYTE_FIRST 0x01
|
||||||
|
|
||||||
|
#define RESET_MICROC 0x40
|
||||||
|
#define RESET_SCCB 0x20
|
||||||
|
#define RESET_JPEG 0x10
|
||||||
|
#define RESET_DVP 0x04
|
||||||
|
#define RESET_IPU 0x02
|
||||||
|
#define RESET_CIF 0x01
|
||||||
|
|
||||||
|
#define MC_BIST_RESET 0x80
|
||||||
|
#define MC_BIST_BOOT_ROM_SEL 0x40
|
||||||
|
#define MC_BIST_12KB_SEL 0x20
|
||||||
|
#define MC_BIST_12KB_MASK 0x30
|
||||||
|
#define MC_BIST_512KB_SEL 0x08
|
||||||
|
#define MC_BIST_512KB_MASK 0x0C
|
||||||
|
#define MC_BIST_BUSY_BIT_R 0x02
|
||||||
|
#define MC_BIST_MC_RES_ONE_SH_W 0x02
|
||||||
|
#define MC_BIST_LAUNCH 0x01
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BANK_DSP, BANK_SENSOR, BANK_MAX
|
||||||
|
} ov2640_bank_t;
|
||||||
|
|
||||||
|
/* Sensor register bank FF=0x01*/
|
||||||
|
#define GAIN 0x00
|
||||||
|
#define COM1 0x03
|
||||||
|
#define REG04 0x04
|
||||||
|
#define REG08 0x08
|
||||||
|
#define COM2 0x09
|
||||||
|
#define REG_PID 0x0A
|
||||||
|
#define REG_VER 0x0B
|
||||||
|
#define COM3 0x0C
|
||||||
|
#define COM4 0x0D
|
||||||
|
#define AEC 0x10
|
||||||
|
#define CLKRC 0x11
|
||||||
|
#define COM7 0x12
|
||||||
|
#define COM8 0x13
|
||||||
|
#define COM9 0x14 /* AGC gain ceiling */
|
||||||
|
#define COM10 0x15
|
||||||
|
#define HSTART 0x17
|
||||||
|
#define HSTOP 0x18
|
||||||
|
#define VSTART 0x19
|
||||||
|
#define VSTOP 0x1A
|
||||||
|
#define MIDH 0x1C
|
||||||
|
#define MIDL 0x1D
|
||||||
|
#define AEW 0x24
|
||||||
|
#define AEB 0x25
|
||||||
|
#define VV 0x26
|
||||||
|
#define REG2A 0x2A
|
||||||
|
#define FRARL 0x2B
|
||||||
|
#define ADDVSL 0x2D
|
||||||
|
#define ADDVSH 0x2E
|
||||||
|
#define YAVG 0x2F
|
||||||
|
#define HSDY 0x30
|
||||||
|
#define HEDY 0x31
|
||||||
|
#define REG32 0x32
|
||||||
|
#define ARCOM2 0x34
|
||||||
|
#define REG45 0x45
|
||||||
|
#define FLL 0x46
|
||||||
|
#define FLH 0x47
|
||||||
|
#define COM19 0x48
|
||||||
|
#define ZOOMS 0x49
|
||||||
|
#define COM22 0x4B
|
||||||
|
#define COM25 0x4E
|
||||||
|
#define BD50 0x4F
|
||||||
|
#define BD60 0x50
|
||||||
|
#define REG5D 0x5D
|
||||||
|
#define REG5E 0x5E
|
||||||
|
#define REG5F 0x5F
|
||||||
|
#define REG60 0x60
|
||||||
|
#define HISTO_LOW 0x61
|
||||||
|
#define HISTO_HIGH 0x62
|
||||||
|
|
||||||
|
#define REG04_DEFAULT 0x28
|
||||||
|
#define REG04_HFLIP_IMG 0x80
|
||||||
|
#define REG04_VFLIP_IMG 0x40
|
||||||
|
#define REG04_VREF_EN 0x10
|
||||||
|
#define REG04_HREF_EN 0x08
|
||||||
|
#define REG04_SET(x) (REG04_DEFAULT|x)
|
||||||
|
|
||||||
|
#define COM2_STDBY 0x10
|
||||||
|
#define COM2_OUT_DRIVE_1x 0x00
|
||||||
|
#define COM2_OUT_DRIVE_2x 0x01
|
||||||
|
#define COM2_OUT_DRIVE_3x 0x02
|
||||||
|
#define COM2_OUT_DRIVE_4x 0x03
|
||||||
|
|
||||||
|
#define COM3_DEFAULT 0x38
|
||||||
|
#define COM3_BAND_50Hz 0x04
|
||||||
|
#define COM3_BAND_60Hz 0x00
|
||||||
|
#define COM3_BAND_AUTO 0x02
|
||||||
|
#define COM3_BAND_SET(x) (COM3_DEFAULT|x)
|
||||||
|
|
||||||
|
#define COM7_SRST 0x80
|
||||||
|
#define COM7_RES_UXGA 0x00 /* UXGA */
|
||||||
|
#define COM7_RES_SVGA 0x40 /* SVGA */
|
||||||
|
#define COM7_RES_CIF 0x20 /* CIF */
|
||||||
|
#define COM7_ZOOM_EN 0x04 /* Enable Zoom */
|
||||||
|
#define COM7_COLOR_BAR 0x02 /* Enable Color Bar Test */
|
||||||
|
|
||||||
|
#define COM8_DEFAULT 0xC0
|
||||||
|
#define COM8_BNDF_EN 0x20 /* Enable Banding filter */
|
||||||
|
#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
|
||||||
|
#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
|
||||||
|
#define COM8_SET(x) (COM8_DEFAULT|x)
|
||||||
|
|
||||||
|
#define COM9_DEFAULT 0x08
|
||||||
|
#define COM9_AGC_GAIN_2x 0x00 /* AGC: 2x */
|
||||||
|
#define COM9_AGC_GAIN_4x 0x01 /* AGC: 4x */
|
||||||
|
#define COM9_AGC_GAIN_8x 0x02 /* AGC: 8x */
|
||||||
|
#define COM9_AGC_GAIN_16x 0x03 /* AGC: 16x */
|
||||||
|
#define COM9_AGC_GAIN_32x 0x04 /* AGC: 32x */
|
||||||
|
#define COM9_AGC_GAIN_64x 0x05 /* AGC: 64x */
|
||||||
|
#define COM9_AGC_GAIN_128x 0x06 /* AGC: 128x */
|
||||||
|
#define COM9_AGC_SET(x) (COM9_DEFAULT|(x<<5))
|
||||||
|
|
||||||
|
#define COM10_HREF_EN 0x80 /* HSYNC changes to HREF */
|
||||||
|
#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */
|
||||||
|
#define COM10_PCLK_FREE 0x20 /* PCLK output option: free running PCLK */
|
||||||
|
#define COM10_PCLK_EDGE 0x10 /* Data is updated at the rising edge of PCLK */
|
||||||
|
#define COM10_HREF_NEG 0x08 /* HREF negative */
|
||||||
|
#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */
|
||||||
|
#define COM10_HSYNC_NEG 0x01 /* HSYNC negative */
|
||||||
|
|
||||||
|
#define CTRL1_AWB 0x08 /* Enable AWB */
|
||||||
|
|
||||||
|
#define VV_AGC_TH_SET(h,l) ((h<<4)|(l&0x0F))
|
||||||
|
|
||||||
|
#define REG32_UXGA 0x36
|
||||||
|
#define REG32_SVGA 0x09
|
||||||
|
#define REG32_CIF 0x89
|
||||||
|
|
||||||
|
#define CLKRC_2X 0x80
|
||||||
|
#define CLKRC_2X_UXGA (0x01 | CLKRC_2X)
|
||||||
|
#define CLKRC_2X_SVGA CLKRC_2X
|
||||||
|
#define CLKRC_2X_CIF CLKRC_2X
|
||||||
|
|
||||||
|
#endif //__REG_REGS_H__
|
||||||
485
code/lib/sensors/ov2640_settings.h
Normal file
485
code/lib/sensors/ov2640_settings.h
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef _OV2640_SETTINGS_H_
|
||||||
|
#define _OV2640_SETTINGS_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "ov2640_regs.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OV2640_MODE_UXGA, OV2640_MODE_SVGA, OV2640_MODE_CIF, OV2640_MODE_MAX
|
||||||
|
} ov2640_sensor_mode_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint8_t pclk_div:7;
|
||||||
|
uint8_t pclk_auto:1;
|
||||||
|
};
|
||||||
|
uint8_t pclk;
|
||||||
|
};
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint8_t clk_div:6;
|
||||||
|
uint8_t reserved:1;
|
||||||
|
uint8_t clk_2x:1;
|
||||||
|
};
|
||||||
|
uint8_t clk;
|
||||||
|
};
|
||||||
|
} ov2640_clk_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t offset_x;
|
||||||
|
uint16_t offset_y;
|
||||||
|
uint16_t max_x;
|
||||||
|
uint16_t max_y;
|
||||||
|
} ov2640_ratio_settings_t;
|
||||||
|
|
||||||
|
static const DRAM_ATTR ov2640_ratio_settings_t ratio_table[] = {
|
||||||
|
// ox, oy, mx, my
|
||||||
|
{ 0, 0, 1600, 1200 }, //4x3
|
||||||
|
{ 8, 72, 1584, 1056 }, //3x2
|
||||||
|
{ 0, 100, 1600, 1000 }, //16x10
|
||||||
|
{ 0, 120, 1600, 960 }, //5x3
|
||||||
|
{ 0, 150, 1600, 900 }, //16x9
|
||||||
|
{ 2, 258, 1596, 684 }, //21x9
|
||||||
|
{ 50, 0, 1500, 1200 }, //5x4
|
||||||
|
{ 200, 0, 1200, 1200 }, //1x1
|
||||||
|
{ 462, 0, 676, 1200 } //9x16
|
||||||
|
};
|
||||||
|
|
||||||
|
// 30fps@24MHz
|
||||||
|
const DRAM_ATTR uint8_t ov2640_settings_cif[][2] = {
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{0x2c, 0xff},
|
||||||
|
{0x2e, 0xdf},
|
||||||
|
{BANK_SEL, BANK_SENSOR},
|
||||||
|
{0x3c, 0x32},
|
||||||
|
{CLKRC, 0x01},
|
||||||
|
{COM2, COM2_OUT_DRIVE_3x},
|
||||||
|
{REG04, REG04_DEFAULT},
|
||||||
|
{COM8, COM8_DEFAULT | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN},
|
||||||
|
{COM9, COM9_AGC_SET(COM9_AGC_GAIN_8x)},
|
||||||
|
{0x2c, 0x0c},
|
||||||
|
{0x33, 0x78},
|
||||||
|
{0x3a, 0x33},
|
||||||
|
{0x3b, 0xfB},
|
||||||
|
{0x3e, 0x00},
|
||||||
|
{0x43, 0x11},
|
||||||
|
{0x16, 0x10},
|
||||||
|
{0x39, 0x92},
|
||||||
|
{0x35, 0xda},
|
||||||
|
{0x22, 0x1a},
|
||||||
|
{0x37, 0xc3},
|
||||||
|
{0x23, 0x00},
|
||||||
|
{ARCOM2, 0xc0},
|
||||||
|
{0x06, 0x88},
|
||||||
|
{0x07, 0xc0},
|
||||||
|
{COM4, 0x87},
|
||||||
|
{0x0e, 0x41},
|
||||||
|
{0x4c, 0x00},
|
||||||
|
{0x4a, 0x81},
|
||||||
|
{0x21, 0x99},
|
||||||
|
{AEW, 0x40},
|
||||||
|
{AEB, 0x38},
|
||||||
|
{VV, VV_AGC_TH_SET(8,2)},
|
||||||
|
{0x5c, 0x00},
|
||||||
|
{0x63, 0x00},
|
||||||
|
{HISTO_LOW, 0x70},
|
||||||
|
{HISTO_HIGH, 0x80},
|
||||||
|
{0x7c, 0x05},
|
||||||
|
{0x20, 0x80},
|
||||||
|
{0x28, 0x30},
|
||||||
|
{0x6c, 0x00},
|
||||||
|
{0x6d, 0x80},
|
||||||
|
{0x6e, 0x00},
|
||||||
|
{0x70, 0x02},
|
||||||
|
{0x71, 0x94},
|
||||||
|
{0x73, 0xc1},
|
||||||
|
{0x3d, 0x34},
|
||||||
|
{0x5a, 0x57},
|
||||||
|
{BD50, 0xbb},
|
||||||
|
{BD60, 0x9c},
|
||||||
|
{COM7, COM7_RES_CIF},
|
||||||
|
{HSTART, 0x11},
|
||||||
|
{HSTOP, 0x43},
|
||||||
|
{VSTART, 0x00},
|
||||||
|
{VSTOP, 0x25},
|
||||||
|
{REG32, 0x89},
|
||||||
|
{0x37, 0xc0},
|
||||||
|
{BD50, 0xca},
|
||||||
|
{BD60, 0xa8},
|
||||||
|
{0x6d, 0x00},
|
||||||
|
{0x3d, 0x38},
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{0xe5, 0x7f},
|
||||||
|
{MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL},
|
||||||
|
{0x41, 0x24},
|
||||||
|
{RESET, RESET_JPEG | RESET_DVP},
|
||||||
|
{0x76, 0xff},
|
||||||
|
{0x33, 0xa0},
|
||||||
|
{0x42, 0x20},
|
||||||
|
{0x43, 0x18},
|
||||||
|
{0x4c, 0x00},
|
||||||
|
{CTRL3, CTRL3_WPC_EN | 0x10 },
|
||||||
|
{0x88, 0x3f},
|
||||||
|
{0xd7, 0x03},
|
||||||
|
{0xd9, 0x10},
|
||||||
|
{R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x02},
|
||||||
|
{0xc8, 0x08},
|
||||||
|
{0xc9, 0x80},
|
||||||
|
{BPADDR, 0x00},
|
||||||
|
{BPDATA, 0x00},
|
||||||
|
{BPADDR, 0x03},
|
||||||
|
{BPDATA, 0x48},
|
||||||
|
{BPDATA, 0x48},
|
||||||
|
{BPADDR, 0x08},
|
||||||
|
{BPDATA, 0x20},
|
||||||
|
{BPDATA, 0x10},
|
||||||
|
{BPDATA, 0x0e},
|
||||||
|
{0x90, 0x00},
|
||||||
|
{0x91, 0x0e},
|
||||||
|
{0x91, 0x1a},
|
||||||
|
{0x91, 0x31},
|
||||||
|
{0x91, 0x5a},
|
||||||
|
{0x91, 0x69},
|
||||||
|
{0x91, 0x75},
|
||||||
|
{0x91, 0x7e},
|
||||||
|
{0x91, 0x88},
|
||||||
|
{0x91, 0x8f},
|
||||||
|
{0x91, 0x96},
|
||||||
|
{0x91, 0xa3},
|
||||||
|
{0x91, 0xaf},
|
||||||
|
{0x91, 0xc4},
|
||||||
|
{0x91, 0xd7},
|
||||||
|
{0x91, 0xe8},
|
||||||
|
{0x91, 0x20},
|
||||||
|
{0x92, 0x00},
|
||||||
|
{0x93, 0x06},
|
||||||
|
{0x93, 0xe3},
|
||||||
|
{0x93, 0x05},
|
||||||
|
{0x93, 0x05},
|
||||||
|
{0x93, 0x00},
|
||||||
|
{0x93, 0x04},
|
||||||
|
{0x93, 0x00},
|
||||||
|
{0x93, 0x00},
|
||||||
|
{0x93, 0x00},
|
||||||
|
{0x93, 0x00},
|
||||||
|
{0x93, 0x00},
|
||||||
|
{0x93, 0x00},
|
||||||
|
{0x93, 0x00},
|
||||||
|
{0x96, 0x00},
|
||||||
|
{0x97, 0x08},
|
||||||
|
{0x97, 0x19},
|
||||||
|
{0x97, 0x02},
|
||||||
|
{0x97, 0x0c},
|
||||||
|
{0x97, 0x24},
|
||||||
|
{0x97, 0x30},
|
||||||
|
{0x97, 0x28},
|
||||||
|
{0x97, 0x26},
|
||||||
|
{0x97, 0x02},
|
||||||
|
{0x97, 0x98},
|
||||||
|
{0x97, 0x80},
|
||||||
|
{0x97, 0x00},
|
||||||
|
{0x97, 0x00},
|
||||||
|
{0xa4, 0x00},
|
||||||
|
{0xa8, 0x00},
|
||||||
|
{0xc5, 0x11},
|
||||||
|
{0xc6, 0x51},
|
||||||
|
{0xbf, 0x80},
|
||||||
|
{0xc7, 0x10},
|
||||||
|
{0xb6, 0x66},
|
||||||
|
{0xb8, 0xA5},
|
||||||
|
{0xb7, 0x64},
|
||||||
|
{0xb9, 0x7C},
|
||||||
|
{0xb3, 0xaf},
|
||||||
|
{0xb4, 0x97},
|
||||||
|
{0xb5, 0xFF},
|
||||||
|
{0xb0, 0xC5},
|
||||||
|
{0xb1, 0x94},
|
||||||
|
{0xb2, 0x0f},
|
||||||
|
{0xc4, 0x5c},
|
||||||
|
{CTRL1, 0xfd},
|
||||||
|
{0x7f, 0x00},
|
||||||
|
{0xe5, 0x1f},
|
||||||
|
{0xe1, 0x67},
|
||||||
|
{0xdd, 0x7f},
|
||||||
|
{IMAGE_MODE, 0x00},
|
||||||
|
{RESET, 0x00},
|
||||||
|
{R_BYPASS, R_BYPASS_DSP_EN},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DRAM_ATTR uint8_t ov2640_settings_to_cif[][2] = {
|
||||||
|
{BANK_SEL, BANK_SENSOR},
|
||||||
|
{COM7, COM7_RES_CIF},
|
||||||
|
|
||||||
|
//Set the sensor output window
|
||||||
|
{COM1, 0x0A},
|
||||||
|
{REG32, REG32_CIF},
|
||||||
|
{HSTART, 0x11},
|
||||||
|
{HSTOP, 0x43},
|
||||||
|
{VSTART, 0x00},
|
||||||
|
{VSTOP, 0x25},
|
||||||
|
|
||||||
|
//{CLKRC, 0x00},
|
||||||
|
{BD50, 0xca},
|
||||||
|
{BD60, 0xa8},
|
||||||
|
{0x5a, 0x23},
|
||||||
|
{0x6d, 0x00},
|
||||||
|
{0x3d, 0x38},
|
||||||
|
{0x39, 0x92},
|
||||||
|
{0x35, 0xda},
|
||||||
|
{0x22, 0x1a},
|
||||||
|
{0x37, 0xc3},
|
||||||
|
{0x23, 0x00},
|
||||||
|
{ARCOM2, 0xc0},
|
||||||
|
{0x06, 0x88},
|
||||||
|
{0x07, 0xc0},
|
||||||
|
{COM4, 0x87},
|
||||||
|
{0x0e, 0x41},
|
||||||
|
{0x4c, 0x00},
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{RESET, RESET_DVP},
|
||||||
|
|
||||||
|
//Set the sensor resolution (UXGA, SVGA, CIF)
|
||||||
|
{HSIZE8, 0x32},
|
||||||
|
{VSIZE8, 0x25},
|
||||||
|
{SIZEL, 0x00},
|
||||||
|
|
||||||
|
//Set the image window size >= output size
|
||||||
|
{HSIZE, 0x64},
|
||||||
|
{VSIZE, 0x4a},
|
||||||
|
{XOFFL, 0x00},
|
||||||
|
{YOFFL, 0x00},
|
||||||
|
{VHYX, 0x00},
|
||||||
|
{TEST, 0x00},
|
||||||
|
|
||||||
|
{CTRL2, CTRL2_DCW_EN | 0x1D},
|
||||||
|
{CTRLI, CTRLI_LP_DP | 0x00},
|
||||||
|
//{R_DVP_SP, 0x08},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DRAM_ATTR uint8_t ov2640_settings_to_svga[][2] = {
|
||||||
|
{BANK_SEL, BANK_SENSOR},
|
||||||
|
{COM7, COM7_RES_SVGA},
|
||||||
|
|
||||||
|
//Set the sensor output window
|
||||||
|
{COM1, 0x0A},
|
||||||
|
{REG32, REG32_SVGA},
|
||||||
|
{HSTART, 0x11},
|
||||||
|
{HSTOP, 0x43},
|
||||||
|
{VSTART, 0x00},
|
||||||
|
{VSTOP, 0x4b},
|
||||||
|
|
||||||
|
//{CLKRC, 0x00},
|
||||||
|
{0x37, 0xc0},
|
||||||
|
{BD50, 0xca},
|
||||||
|
{BD60, 0xa8},
|
||||||
|
{0x5a, 0x23},
|
||||||
|
{0x6d, 0x00},
|
||||||
|
{0x3d, 0x38},
|
||||||
|
{0x39, 0x92},
|
||||||
|
{0x35, 0xda},
|
||||||
|
{0x22, 0x1a},
|
||||||
|
{0x37, 0xc3},
|
||||||
|
{0x23, 0x00},
|
||||||
|
{ARCOM2, 0xc0},
|
||||||
|
{0x06, 0x88},
|
||||||
|
{0x07, 0xc0},
|
||||||
|
{COM4, 0x87},
|
||||||
|
{0x0e, 0x41},
|
||||||
|
{0x42, 0x03},
|
||||||
|
{0x4c, 0x00},
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{RESET, RESET_DVP},
|
||||||
|
|
||||||
|
//Set the sensor resolution (UXGA, SVGA, CIF)
|
||||||
|
{HSIZE8, 0x64},
|
||||||
|
{VSIZE8, 0x4B},
|
||||||
|
{SIZEL, 0x00},
|
||||||
|
|
||||||
|
//Set the image window size >= output size
|
||||||
|
{HSIZE, 0xC8},
|
||||||
|
{VSIZE, 0x96},
|
||||||
|
{XOFFL, 0x00},
|
||||||
|
{YOFFL, 0x00},
|
||||||
|
{VHYX, 0x00},
|
||||||
|
{TEST, 0x00},
|
||||||
|
|
||||||
|
{CTRL2, CTRL2_DCW_EN | 0x1D},
|
||||||
|
{CTRLI, CTRLI_LP_DP | 0x00},
|
||||||
|
//{R_DVP_SP, 0x08},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DRAM_ATTR uint8_t ov2640_settings_to_uxga[][2] = {
|
||||||
|
{BANK_SEL, BANK_SENSOR},
|
||||||
|
{COM7, COM7_RES_UXGA},
|
||||||
|
|
||||||
|
//Set the sensor output window
|
||||||
|
{COM1, 0x0F},
|
||||||
|
{REG32, REG32_UXGA},
|
||||||
|
{HSTART, 0x11},
|
||||||
|
{HSTOP, 0x75},
|
||||||
|
{VSTART, 0x01},
|
||||||
|
{VSTOP, 0x97},
|
||||||
|
|
||||||
|
//{CLKRC, 0x00},
|
||||||
|
{0x3d, 0x34},
|
||||||
|
{BD50, 0xbb},
|
||||||
|
{BD60, 0x9c},
|
||||||
|
{0x5a, 0x57},
|
||||||
|
{0x6d, 0x80},
|
||||||
|
{0x39, 0x82},
|
||||||
|
{0x23, 0x00},
|
||||||
|
{0x07, 0xc0},
|
||||||
|
{0x4c, 0x00},
|
||||||
|
{0x35, 0x88},
|
||||||
|
{0x22, 0x0a},
|
||||||
|
{0x37, 0x40},
|
||||||
|
{ARCOM2, 0xa0},
|
||||||
|
{0x06, 0x02},
|
||||||
|
{COM4, 0xb7},
|
||||||
|
{0x0e, 0x01},
|
||||||
|
{0x42, 0x83},
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{RESET, RESET_DVP},
|
||||||
|
|
||||||
|
//Set the sensor resolution (UXGA, SVGA, CIF)
|
||||||
|
{HSIZE8, 0xc8},
|
||||||
|
{VSIZE8, 0x96},
|
||||||
|
{SIZEL, 0x00},
|
||||||
|
|
||||||
|
//Set the image window size >= output size
|
||||||
|
{HSIZE, 0x90},
|
||||||
|
{VSIZE, 0x2c},
|
||||||
|
{XOFFL, 0x00},
|
||||||
|
{YOFFL, 0x00},
|
||||||
|
{VHYX, 0x88},
|
||||||
|
{TEST, 0x00},
|
||||||
|
|
||||||
|
{CTRL2, CTRL2_DCW_EN | 0x1d},
|
||||||
|
{CTRLI, 0x00},
|
||||||
|
//{R_DVP_SP, 0x06},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DRAM_ATTR uint8_t ov2640_settings_jpeg3[][2] = {
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{RESET, RESET_JPEG | RESET_DVP},
|
||||||
|
{IMAGE_MODE, IMAGE_MODE_JPEG_EN | IMAGE_MODE_HREF_VSYNC},
|
||||||
|
{0xD7, 0x03},
|
||||||
|
{0xE1, 0x77},
|
||||||
|
{0xE5, 0x1F},
|
||||||
|
{0xD9, 0x10},
|
||||||
|
{0xDF, 0x80},
|
||||||
|
{0x33, 0x80},
|
||||||
|
{0x3C, 0x10},
|
||||||
|
{0xEB, 0x30},
|
||||||
|
{0xDD, 0x7F},
|
||||||
|
{RESET, 0x00},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t ov2640_settings_yuv422[][2] = {
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{RESET, RESET_DVP},
|
||||||
|
{IMAGE_MODE, IMAGE_MODE_YUV422},
|
||||||
|
{0xD7, 0x01},
|
||||||
|
{0xE1, 0x67},
|
||||||
|
{RESET, 0x00},
|
||||||
|
{0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t ov2640_settings_rgb565[][2] = {
|
||||||
|
{BANK_SEL, BANK_DSP},
|
||||||
|
{RESET, RESET_DVP},
|
||||||
|
{IMAGE_MODE, IMAGE_MODE_RGB565},
|
||||||
|
{0xD7, 0x03},
|
||||||
|
{0xE1, 0x77},
|
||||||
|
{RESET, 0x00},
|
||||||
|
{0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_BRIGHTNESS_LEVELS (5)
|
||||||
|
static const uint8_t brightness_regs[NUM_BRIGHTNESS_LEVELS + 1][5] = {
|
||||||
|
{BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
|
||||||
|
{0x00, 0x04, 0x09, 0x00, 0x00 }, /* -2 */
|
||||||
|
{0x00, 0x04, 0x09, 0x10, 0x00 }, /* -1 */
|
||||||
|
{0x00, 0x04, 0x09, 0x20, 0x00 }, /* 0 */
|
||||||
|
{0x00, 0x04, 0x09, 0x30, 0x00 }, /* +1 */
|
||||||
|
{0x00, 0x04, 0x09, 0x40, 0x00 }, /* +2 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_CONTRAST_LEVELS (5)
|
||||||
|
static const uint8_t contrast_regs[NUM_CONTRAST_LEVELS + 1][7] = {
|
||||||
|
{BPADDR, BPDATA, BPADDR, BPDATA, BPDATA, BPDATA, BPDATA },
|
||||||
|
{0x00, 0x04, 0x07, 0x20, 0x18, 0x34, 0x06 }, /* -2 */
|
||||||
|
{0x00, 0x04, 0x07, 0x20, 0x1c, 0x2a, 0x06 }, /* -1 */
|
||||||
|
{0x00, 0x04, 0x07, 0x20, 0x20, 0x20, 0x06 }, /* 0 */
|
||||||
|
{0x00, 0x04, 0x07, 0x20, 0x24, 0x16, 0x06 }, /* +1 */
|
||||||
|
{0x00, 0x04, 0x07, 0x20, 0x28, 0x0c, 0x06 }, /* +2 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_SATURATION_LEVELS (5)
|
||||||
|
static const uint8_t saturation_regs[NUM_SATURATION_LEVELS + 1][5] = {
|
||||||
|
{BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
|
||||||
|
{0x00, 0x02, 0x03, 0x28, 0x28 }, /* -2 */
|
||||||
|
{0x00, 0x02, 0x03, 0x38, 0x38 }, /* -1 */
|
||||||
|
{0x00, 0x02, 0x03, 0x48, 0x48 }, /* 0 */
|
||||||
|
{0x00, 0x02, 0x03, 0x58, 0x58 }, /* +1 */
|
||||||
|
{0x00, 0x02, 0x03, 0x68, 0x68 }, /* +2 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_SPECIAL_EFFECTS (7)
|
||||||
|
static const uint8_t special_effects_regs[NUM_SPECIAL_EFFECTS + 1][5] = {
|
||||||
|
{BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
|
||||||
|
{0x00, 0X00, 0x05, 0X80, 0X80 }, /* no effect */
|
||||||
|
{0x00, 0X40, 0x05, 0X80, 0X80 }, /* negative */
|
||||||
|
{0x00, 0X18, 0x05, 0X80, 0X80 }, /* black and white */
|
||||||
|
{0x00, 0X18, 0x05, 0X40, 0XC0 }, /* reddish */
|
||||||
|
{0x00, 0X18, 0x05, 0X40, 0X40 }, /* greenish */
|
||||||
|
{0x00, 0X18, 0x05, 0XA0, 0X40 }, /* blue */
|
||||||
|
{0x00, 0X18, 0x05, 0X40, 0XA6 }, /* retro */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_WB_MODES (4)
|
||||||
|
static const uint8_t wb_modes_regs[NUM_WB_MODES + 1][3] = {
|
||||||
|
{0XCC, 0XCD, 0XCE },
|
||||||
|
{0x5E, 0X41, 0x54 }, /* sunny */
|
||||||
|
{0x65, 0X41, 0x4F }, /* cloudy */
|
||||||
|
{0x52, 0X41, 0x66 }, /* office */
|
||||||
|
{0x42, 0X3F, 0x71 }, /* home */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_AE_LEVELS (5)
|
||||||
|
static const uint8_t ae_levels_regs[NUM_AE_LEVELS + 1][3] = {
|
||||||
|
{ AEW, AEB, VV },
|
||||||
|
{0x20, 0X18, 0x60 },
|
||||||
|
{0x34, 0X1C, 0x00 },
|
||||||
|
{0x3E, 0X38, 0x81 },
|
||||||
|
{0x48, 0X40, 0x81 },
|
||||||
|
{0x58, 0X50, 0x92 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t agc_gain_tbl[31] = {
|
||||||
|
0x00, 0x10, 0x18, 0x30, 0x34, 0x38, 0x3C, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 0xF0,
|
||||||
|
0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _OV2640_SETTINGS_H_ */
|
||||||
1033
code/lib/sensors/ov3660.c
Normal file
1033
code/lib/sensors/ov3660.c
Normal file
File diff suppressed because it is too large
Load Diff
16
code/lib/sensors/ov3660.h
Normal file
16
code/lib/sensors/ov3660.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* OV3660 driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef __OV3660_H__
|
||||||
|
#define __OV3660_H__
|
||||||
|
|
||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
int ov3660_init(sensor_t *sensor);
|
||||||
|
|
||||||
|
#endif // __OV3660_H__
|
||||||
211
code/lib/sensors/ov3660_regs.h
Normal file
211
code/lib/sensors/ov3660_regs.h
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* OV3660 register definitions.
|
||||||
|
*/
|
||||||
|
#ifndef __OV3660_REG_REGS_H__
|
||||||
|
#define __OV3660_REG_REGS_H__
|
||||||
|
|
||||||
|
/* system control registers */
|
||||||
|
#define SYSTEM_CTROL0 0x3008 // Bit[7]: Software reset
|
||||||
|
// Bit[6]: Software power down
|
||||||
|
// Bit[5]: Reserved
|
||||||
|
// Bit[4]: SRB clock SYNC enable
|
||||||
|
// Bit[3]: Isolation suspend select
|
||||||
|
// Bit[2:0]: Not used
|
||||||
|
|
||||||
|
/* output format control registers */
|
||||||
|
#define FORMAT_CTRL 0x501F // Format select
|
||||||
|
// Bit[2:0]:
|
||||||
|
// 000: YUV422
|
||||||
|
// 001: RGB
|
||||||
|
// 010: Dither
|
||||||
|
// 011: RAW after DPC
|
||||||
|
// 101: RAW after CIP
|
||||||
|
|
||||||
|
/* format control registers */
|
||||||
|
#define FORMAT_CTRL00 0x4300
|
||||||
|
|
||||||
|
/* frame control registers */
|
||||||
|
#define FRAME_CTRL01 0x4201 // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
|
||||||
|
// Bit[7:4]: Not used
|
||||||
|
// Bit[3:0]: Frame ON number
|
||||||
|
#define FRAME_CTRL02 0x4202 // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
|
||||||
|
// Bit[7:4]: Not used
|
||||||
|
// BIT[3:0]: Frame OFF number
|
||||||
|
|
||||||
|
/* ISP top control registers */
|
||||||
|
#define PRE_ISP_TEST_SETTING_1 0x503D // Bit[7]: Test enable
|
||||||
|
// 0: Test disable
|
||||||
|
// 1: Color bar enable
|
||||||
|
// Bit[6]: Rolling
|
||||||
|
// Bit[5]: Transparent
|
||||||
|
// Bit[4]: Square black and white
|
||||||
|
// Bit[3:2]: Color bar style
|
||||||
|
// 00: Standard 8 color bar
|
||||||
|
// 01: Gradual change at vertical mode 1
|
||||||
|
// 10: Gradual change at horizontal
|
||||||
|
// 11: Gradual change at vertical mode 2
|
||||||
|
// Bit[1:0]: Test select
|
||||||
|
// 00: Color bar
|
||||||
|
// 01: Random data
|
||||||
|
// 10: Square data
|
||||||
|
// 11: Black image
|
||||||
|
|
||||||
|
//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
|
||||||
|
|
||||||
|
/* AEC/AGC control functions */
|
||||||
|
#define AEC_PK_MANUAL 0x3503 // AEC Manual Mode Control
|
||||||
|
// Bit[7:6]: Reserved
|
||||||
|
// Bit[5]: Gain delay option
|
||||||
|
// Valid when 0x3503[4]=1’b0
|
||||||
|
// 0: Delay one frame latch
|
||||||
|
// 1: One frame latch
|
||||||
|
// Bit[4:2]: Reserved
|
||||||
|
// Bit[1]: AGC manual
|
||||||
|
// 0: Auto enable
|
||||||
|
// 1: Manual enable
|
||||||
|
// Bit[0]: AEC manual
|
||||||
|
// 0: Auto enable
|
||||||
|
// 1: Manual enable
|
||||||
|
|
||||||
|
//gain = {0x350A[1:0], 0x350B[7:0]} / 16
|
||||||
|
|
||||||
|
/* mirror and flip registers */
|
||||||
|
#define TIMING_TC_REG20 0x3820 // Timing Control Register
|
||||||
|
// Bit[2:1]: Vertical flip enable
|
||||||
|
// 00: Normal
|
||||||
|
// 11: Vertical flip
|
||||||
|
// Bit[0]: Vertical binning enable
|
||||||
|
#define TIMING_TC_REG21 0x3821 // Timing Control Register
|
||||||
|
// Bit[5]: Compression Enable
|
||||||
|
// Bit[2:1]: Horizontal mirror enable
|
||||||
|
// 00: Normal
|
||||||
|
// 11: Horizontal mirror
|
||||||
|
// Bit[0]: Horizontal binning enable
|
||||||
|
|
||||||
|
#define CLOCK_POL_CONTROL 0x4740// Bit[5]: PCLK polarity 0: active low
|
||||||
|
// 1: active high
|
||||||
|
// Bit[3]: Gate PCLK under VSYNC
|
||||||
|
// Bit[2]: Gate PCLK under HREF
|
||||||
|
// Bit[1]: HREF polarity
|
||||||
|
// 0: active low
|
||||||
|
// 1: active high
|
||||||
|
// Bit[0] VSYNC polarity
|
||||||
|
// 0: active low
|
||||||
|
// 1: active high
|
||||||
|
#define DRIVE_CAPABILITY 0x302c // Bit[7:6]:
|
||||||
|
// 00: 1x
|
||||||
|
// 01: 2x
|
||||||
|
// 10: 3x
|
||||||
|
// 11: 4x
|
||||||
|
|
||||||
|
|
||||||
|
#define X_ADDR_ST_H 0x3800 //Bit[3:0]: X address start[11:8]
|
||||||
|
#define X_ADDR_ST_L 0x3801 //Bit[7:0]: X address start[7:0]
|
||||||
|
#define Y_ADDR_ST_H 0x3802 //Bit[2:0]: Y address start[10:8]
|
||||||
|
#define Y_ADDR_ST_L 0x3803 //Bit[7:0]: Y address start[7:0]
|
||||||
|
#define X_ADDR_END_H 0x3804 //Bit[3:0]: X address end[11:8]
|
||||||
|
#define X_ADDR_END_L 0x3805 //Bit[7:0]:
|
||||||
|
#define Y_ADDR_END_H 0x3806 //Bit[2:0]: Y address end[10:8]
|
||||||
|
#define Y_ADDR_END_L 0x3807 //Bit[7:0]:
|
||||||
|
// Size after scaling
|
||||||
|
#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
|
||||||
|
#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
|
||||||
|
#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
|
||||||
|
#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
|
||||||
|
#define X_TOTAL_SIZE_H 0x380c //Bit[3:0]: Total horizontal size[11:8]
|
||||||
|
#define X_TOTAL_SIZE_L 0x380d //Bit[7:0]:
|
||||||
|
#define Y_TOTAL_SIZE_H 0x380e //Bit[7:0]: Total vertical size[15:8]
|
||||||
|
#define Y_TOTAL_SIZE_L 0x380f //Bit[7:0]:
|
||||||
|
#define X_OFFSET_H 0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
|
||||||
|
#define X_OFFSET_L 0x3811 //Bit[7:0]:
|
||||||
|
#define Y_OFFSET_H 0x3812 //Bit[2:0]: ISP vertical offset[10:8]
|
||||||
|
#define Y_OFFSET_L 0x3813 //Bit[7:0]:
|
||||||
|
#define X_INCREMENT 0x3814 //Bit[7:4]: Horizontal odd subsample increment
|
||||||
|
//Bit[3:0]: Horizontal even subsample increment
|
||||||
|
#define Y_INCREMENT 0x3815 //Bit[7:4]: Vertical odd subsample increment
|
||||||
|
//Bit[3:0]: Vertical even subsample increment
|
||||||
|
// Size before scaling
|
||||||
|
//#define X_INPUT_SIZE (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
|
||||||
|
//#define Y_INPUT_SIZE (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
|
||||||
|
|
||||||
|
#define ISP_CONTROL_01 0x5001 // Bit[5]: Scale enable
|
||||||
|
// 0: Disable
|
||||||
|
// 1: Enable
|
||||||
|
|
||||||
|
#define SCALE_CTRL_1 0x5601 // Bit[6:4]: HDIV RW
|
||||||
|
// DCW scale times
|
||||||
|
// 000: DCW 1 time
|
||||||
|
// 001: DCW 2 times
|
||||||
|
// 010: DCW 4 times
|
||||||
|
// 100: DCW 8 times
|
||||||
|
// 101: DCW 16 times
|
||||||
|
// Others: DCW 16 times
|
||||||
|
// Bit[2:0]: VDIV RW
|
||||||
|
// DCW scale times
|
||||||
|
// 000: DCW 1 time
|
||||||
|
// 001: DCW 2 times
|
||||||
|
// 010: DCW 4 times
|
||||||
|
// 100: DCW 8 times
|
||||||
|
// 101: DCW 16 times
|
||||||
|
// Others: DCW 16 times
|
||||||
|
|
||||||
|
#define SCALE_CTRL_2 0x5602 // X_SCALE High Bits
|
||||||
|
#define SCALE_CTRL_3 0x5603 // X_SCALE Low Bits
|
||||||
|
#define SCALE_CTRL_4 0x5604 // Y_SCALE High Bits
|
||||||
|
#define SCALE_CTRL_5 0x5605 // Y_SCALE Low Bits
|
||||||
|
#define SCALE_CTRL_6 0x5606 // Bit[3:0]: V Offset
|
||||||
|
|
||||||
|
#define PCLK_RATIO 0x3824 // Bit[4:0]: PCLK ratio manual
|
||||||
|
#define VFIFO_CTRL0C 0x460C // Bit[1]: PCLK manual enable
|
||||||
|
// 0: Auto
|
||||||
|
// 1: Manual by PCLK_RATIO
|
||||||
|
|
||||||
|
#define VFIFO_X_SIZE_H 0x4602
|
||||||
|
#define VFIFO_X_SIZE_L 0x4603
|
||||||
|
#define VFIFO_Y_SIZE_H 0x4604
|
||||||
|
#define VFIFO_Y_SIZE_L 0x4605
|
||||||
|
|
||||||
|
#define SC_PLLS_CTRL0 0x303a // Bit[7]: PLLS bypass
|
||||||
|
#define SC_PLLS_CTRL1 0x303b // Bit[4:0]: PLLS multiplier
|
||||||
|
#define SC_PLLS_CTRL2 0x303c // Bit[6:4]: PLLS charge pump control
|
||||||
|
// Bit[3:0]: PLLS system divider
|
||||||
|
#define SC_PLLS_CTRL3 0x303d // Bit[5:4]: PLLS pre-divider
|
||||||
|
// 00: 1
|
||||||
|
// 01: 1.5
|
||||||
|
// 10: 2
|
||||||
|
// 11: 3
|
||||||
|
// Bit[2]: PLLS root-divider - 1
|
||||||
|
// Bit[1:0]: PLLS seld5
|
||||||
|
// 00: 1
|
||||||
|
// 01: 1
|
||||||
|
// 10: 2
|
||||||
|
// 11: 2.5
|
||||||
|
|
||||||
|
#define COMPRESSION_CTRL00 0x4400 //
|
||||||
|
#define COMPRESSION_CTRL01 0x4401 //
|
||||||
|
#define COMPRESSION_CTRL02 0x4402 //
|
||||||
|
#define COMPRESSION_CTRL03 0x4403 //
|
||||||
|
#define COMPRESSION_CTRL04 0x4404 //
|
||||||
|
#define COMPRESSION_CTRL05 0x4405 //
|
||||||
|
#define COMPRESSION_CTRL06 0x4406 //
|
||||||
|
#define COMPRESSION_CTRL07 0x4407 // Bit[5:0]: QS
|
||||||
|
#define COMPRESSION_ISI_CTRL 0x4408 //
|
||||||
|
#define COMPRESSION_CTRL09 0x4409 //
|
||||||
|
#define COMPRESSION_CTRL0a 0x440a //
|
||||||
|
#define COMPRESSION_CTRL0b 0x440b //
|
||||||
|
#define COMPRESSION_CTRL0c 0x440c //
|
||||||
|
#define COMPRESSION_CTRL0d 0x440d //
|
||||||
|
#define COMPRESSION_CTRL0E 0x440e //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief register value
|
||||||
|
*/
|
||||||
|
#define TEST_COLOR_BAR 0xC0 /* Enable Color Bar roling Test */
|
||||||
|
|
||||||
|
#define AEC_PK_MANUAL_AGC_MANUALEN 0x02 /* Enable AGC Manual enable */
|
||||||
|
#define AEC_PK_MANUAL_AEC_MANUALEN 0x01 /* Enable AEC Manual enable */
|
||||||
|
|
||||||
|
#define TIMING_TC_REG20_VFLIP 0x06 /* Vertical flip enable */
|
||||||
|
#define TIMING_TC_REG21_HMIRROR 0x06 /* Horizontal mirror enable */
|
||||||
|
|
||||||
|
#endif // __OV3660_REG_REGS_H__
|
||||||
318
code/lib/sensors/ov3660_settings.h
Normal file
318
code/lib/sensors/ov3660_settings.h
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
#ifndef _OV3660_SETTINGS_H_
|
||||||
|
#define _OV3660_SETTINGS_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "ov3660_regs.h"
|
||||||
|
|
||||||
|
static const ratio_settings_t ratio_table[] = {
|
||||||
|
// mw, mh, sx, sy, ex, ey, ox, oy, tx, ty
|
||||||
|
{ 2048, 1536, 0, 0, 2079, 1547, 16, 6, 2300, 1564 }, //4x3
|
||||||
|
{ 1920, 1280, 64, 128, 2015, 1419, 16, 6, 2172, 1436 }, //3x2
|
||||||
|
{ 2048, 1280, 0, 128, 2079, 1419, 16, 6, 2300, 1436 }, //16x10
|
||||||
|
{ 1920, 1152, 64, 192, 2015, 1355, 16, 6, 2172, 1372 }, //5x3
|
||||||
|
{ 1920, 1080, 64, 242, 2015, 1333, 16, 6, 2172, 1322 }, //16x9
|
||||||
|
{ 2048, 880, 0, 328, 2079, 1219, 16, 6, 2300, 1236 }, //21x9
|
||||||
|
{ 1920, 1536, 64, 0, 2015, 1547, 16, 6, 2172, 1564 }, //5x4
|
||||||
|
{ 1536, 1536, 256, 0, 1823, 1547, 16, 6, 2044, 1564 }, //1x1
|
||||||
|
{ 864, 1536, 592, 0, 1487, 1547, 16, 6, 2044, 1564 } //9x16
|
||||||
|
};
|
||||||
|
|
||||||
|
#define REG_DLY 0xffff
|
||||||
|
#define REGLIST_TAIL 0x0000
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||||
|
{SYSTEM_CTROL0, 0x82}, // software reset
|
||||||
|
{REG_DLY, 10}, // delay 10ms
|
||||||
|
|
||||||
|
{0x3103, 0x13},
|
||||||
|
{SYSTEM_CTROL0, 0x42},
|
||||||
|
{0x3017, 0xff},
|
||||||
|
{0x3018, 0xff},
|
||||||
|
{DRIVE_CAPABILITY, 0xc3},
|
||||||
|
{CLOCK_POL_CONTROL, 0x21},
|
||||||
|
|
||||||
|
{0x3611, 0x01},
|
||||||
|
{0x3612, 0x2d},
|
||||||
|
|
||||||
|
{0x3032, 0x00},
|
||||||
|
{0x3614, 0x80},
|
||||||
|
{0x3618, 0x00},
|
||||||
|
{0x3619, 0x75},
|
||||||
|
{0x3622, 0x80},
|
||||||
|
{0x3623, 0x00},
|
||||||
|
{0x3624, 0x03},
|
||||||
|
{0x3630, 0x52},
|
||||||
|
{0x3632, 0x07},
|
||||||
|
{0x3633, 0xd2},
|
||||||
|
{0x3704, 0x80},
|
||||||
|
{0x3708, 0x66},
|
||||||
|
{0x3709, 0x12},
|
||||||
|
{0x370b, 0x12},
|
||||||
|
{0x3717, 0x00},
|
||||||
|
{0x371b, 0x60},
|
||||||
|
{0x371c, 0x00},
|
||||||
|
{0x3901, 0x13},
|
||||||
|
|
||||||
|
{0x3600, 0x08},
|
||||||
|
{0x3620, 0x43},
|
||||||
|
{0x3702, 0x20},
|
||||||
|
{0x3739, 0x48},
|
||||||
|
{0x3730, 0x20},
|
||||||
|
{0x370c, 0x0c},
|
||||||
|
|
||||||
|
{0x3a18, 0x00},
|
||||||
|
{0x3a19, 0xf8},
|
||||||
|
|
||||||
|
{0x3000, 0x10},
|
||||||
|
{0x3004, 0xef},
|
||||||
|
|
||||||
|
{0x6700, 0x05},
|
||||||
|
{0x6701, 0x19},
|
||||||
|
{0x6702, 0xfd},
|
||||||
|
{0x6703, 0xd1},
|
||||||
|
{0x6704, 0xff},
|
||||||
|
{0x6705, 0xff},
|
||||||
|
|
||||||
|
{0x3c01, 0x80},
|
||||||
|
{0x3c00, 0x04},
|
||||||
|
{0x3a08, 0x00}, {0x3a09, 0x62}, //50Hz Band Width Step (10bit)
|
||||||
|
{0x3a0e, 0x08}, //50Hz Max Bands in One Frame (6 bit)
|
||||||
|
{0x3a0a, 0x00}, {0x3a0b, 0x52}, //60Hz Band Width Step (10bit)
|
||||||
|
{0x3a0d, 0x09}, //60Hz Max Bands in One Frame (6 bit)
|
||||||
|
|
||||||
|
{0x3a00, 0x3a},//night mode off
|
||||||
|
{0x3a14, 0x09},
|
||||||
|
{0x3a15, 0x30},
|
||||||
|
{0x3a02, 0x09},
|
||||||
|
{0x3a03, 0x30},
|
||||||
|
|
||||||
|
{COMPRESSION_CTRL0E, 0x08},
|
||||||
|
{0x4520, 0x0b},
|
||||||
|
{0x460b, 0x37},
|
||||||
|
{0x4713, 0x02},
|
||||||
|
{0x471c, 0xd0},
|
||||||
|
{0x5086, 0x00},
|
||||||
|
|
||||||
|
{0x5002, 0x00},
|
||||||
|
{0x501f, 0x00},
|
||||||
|
|
||||||
|
{SYSTEM_CTROL0, 0x02},
|
||||||
|
|
||||||
|
{0x5180, 0xff},
|
||||||
|
{0x5181, 0xf2},
|
||||||
|
{0x5182, 0x00},
|
||||||
|
{0x5183, 0x14},
|
||||||
|
{0x5184, 0x25},
|
||||||
|
{0x5185, 0x24},
|
||||||
|
{0x5186, 0x16},
|
||||||
|
{0x5187, 0x16},
|
||||||
|
{0x5188, 0x16},
|
||||||
|
{0x5189, 0x68},
|
||||||
|
{0x518a, 0x60},
|
||||||
|
{0x518b, 0xe0},
|
||||||
|
{0x518c, 0xb2},
|
||||||
|
{0x518d, 0x42},
|
||||||
|
{0x518e, 0x35},
|
||||||
|
{0x518f, 0x56},
|
||||||
|
{0x5190, 0x56},
|
||||||
|
{0x5191, 0xf8},
|
||||||
|
{0x5192, 0x04},
|
||||||
|
{0x5193, 0x70},
|
||||||
|
{0x5194, 0xf0},
|
||||||
|
{0x5195, 0xf0},
|
||||||
|
{0x5196, 0x03},
|
||||||
|
{0x5197, 0x01},
|
||||||
|
{0x5198, 0x04},
|
||||||
|
{0x5199, 0x12},
|
||||||
|
{0x519a, 0x04},
|
||||||
|
{0x519b, 0x00},
|
||||||
|
{0x519c, 0x06},
|
||||||
|
{0x519d, 0x82},
|
||||||
|
{0x519e, 0x38},
|
||||||
|
|
||||||
|
{0x5381, 0x1d},
|
||||||
|
{0x5382, 0x60},
|
||||||
|
{0x5383, 0x03},
|
||||||
|
{0x5384, 0x0c},
|
||||||
|
{0x5385, 0x78},
|
||||||
|
{0x5386, 0x84},
|
||||||
|
{0x5387, 0x7d},
|
||||||
|
{0x5388, 0x6b},
|
||||||
|
{0x5389, 0x12},
|
||||||
|
{0x538a, 0x01},
|
||||||
|
{0x538b, 0x98},
|
||||||
|
|
||||||
|
{0x5480, 0x01},
|
||||||
|
// {0x5481, 0x05},
|
||||||
|
// {0x5482, 0x09},
|
||||||
|
// {0x5483, 0x10},
|
||||||
|
// {0x5484, 0x3a},
|
||||||
|
// {0x5485, 0x4c},
|
||||||
|
// {0x5486, 0x5a},
|
||||||
|
// {0x5487, 0x68},
|
||||||
|
// {0x5488, 0x74},
|
||||||
|
// {0x5489, 0x80},
|
||||||
|
// {0x548a, 0x8e},
|
||||||
|
// {0x548b, 0xa4},
|
||||||
|
// {0x548c, 0xb4},
|
||||||
|
// {0x548d, 0xc8},
|
||||||
|
// {0x548e, 0xde},
|
||||||
|
// {0x548f, 0xf0},
|
||||||
|
// {0x5490, 0x15},
|
||||||
|
|
||||||
|
{0x5000, 0xa7},
|
||||||
|
{0x5800, 0x0C},
|
||||||
|
{0x5801, 0x09},
|
||||||
|
{0x5802, 0x0C},
|
||||||
|
{0x5803, 0x0C},
|
||||||
|
{0x5804, 0x0D},
|
||||||
|
{0x5805, 0x17},
|
||||||
|
{0x5806, 0x06},
|
||||||
|
{0x5807, 0x05},
|
||||||
|
{0x5808, 0x04},
|
||||||
|
{0x5809, 0x06},
|
||||||
|
{0x580a, 0x09},
|
||||||
|
{0x580b, 0x0E},
|
||||||
|
{0x580c, 0x05},
|
||||||
|
{0x580d, 0x01},
|
||||||
|
{0x580e, 0x01},
|
||||||
|
{0x580f, 0x01},
|
||||||
|
{0x5810, 0x05},
|
||||||
|
{0x5811, 0x0D},
|
||||||
|
{0x5812, 0x05},
|
||||||
|
{0x5813, 0x01},
|
||||||
|
{0x5814, 0x01},
|
||||||
|
{0x5815, 0x01},
|
||||||
|
{0x5816, 0x05},
|
||||||
|
{0x5817, 0x0D},
|
||||||
|
{0x5818, 0x08},
|
||||||
|
{0x5819, 0x06},
|
||||||
|
{0x581a, 0x05},
|
||||||
|
{0x581b, 0x07},
|
||||||
|
{0x581c, 0x0B},
|
||||||
|
{0x581d, 0x0D},
|
||||||
|
{0x581e, 0x12},
|
||||||
|
{0x581f, 0x0D},
|
||||||
|
{0x5820, 0x0E},
|
||||||
|
{0x5821, 0x10},
|
||||||
|
{0x5822, 0x10},
|
||||||
|
{0x5823, 0x1E},
|
||||||
|
{0x5824, 0x53},
|
||||||
|
{0x5825, 0x15},
|
||||||
|
{0x5826, 0x05},
|
||||||
|
{0x5827, 0x14},
|
||||||
|
{0x5828, 0x54},
|
||||||
|
{0x5829, 0x25},
|
||||||
|
{0x582a, 0x33},
|
||||||
|
{0x582b, 0x33},
|
||||||
|
{0x582c, 0x34},
|
||||||
|
{0x582d, 0x16},
|
||||||
|
{0x582e, 0x24},
|
||||||
|
{0x582f, 0x41},
|
||||||
|
{0x5830, 0x50},
|
||||||
|
{0x5831, 0x42},
|
||||||
|
{0x5832, 0x15},
|
||||||
|
{0x5833, 0x25},
|
||||||
|
{0x5834, 0x34},
|
||||||
|
{0x5835, 0x33},
|
||||||
|
{0x5836, 0x24},
|
||||||
|
{0x5837, 0x26},
|
||||||
|
{0x5838, 0x54},
|
||||||
|
{0x5839, 0x25},
|
||||||
|
{0x583a, 0x15},
|
||||||
|
{0x583b, 0x25},
|
||||||
|
{0x583c, 0x53},
|
||||||
|
{0x583d, 0xCF},
|
||||||
|
|
||||||
|
{0x3a0f, 0x30},
|
||||||
|
{0x3a10, 0x28},
|
||||||
|
{0x3a1b, 0x30},
|
||||||
|
{0x3a1e, 0x28},
|
||||||
|
{0x3a11, 0x60},
|
||||||
|
{0x3a1f, 0x14},
|
||||||
|
|
||||||
|
{0x5302, 0x28},
|
||||||
|
{0x5303, 0x20},
|
||||||
|
|
||||||
|
{0x5306, 0x1c}, //de-noise offset 1
|
||||||
|
{0x5307, 0x28}, //de-noise offset 2
|
||||||
|
|
||||||
|
{0x4002, 0xc5},
|
||||||
|
{0x4003, 0x81},
|
||||||
|
{0x4005, 0x12},
|
||||||
|
|
||||||
|
{0x5688, 0x11},
|
||||||
|
{0x5689, 0x11},
|
||||||
|
{0x568a, 0x11},
|
||||||
|
{0x568b, 0x11},
|
||||||
|
{0x568c, 0x11},
|
||||||
|
{0x568d, 0x11},
|
||||||
|
{0x568e, 0x11},
|
||||||
|
{0x568f, 0x11},
|
||||||
|
|
||||||
|
{0x5580, 0x06},
|
||||||
|
{0x5588, 0x00},
|
||||||
|
{0x5583, 0x40},
|
||||||
|
{0x5584, 0x2c},
|
||||||
|
|
||||||
|
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
|
||||||
|
{REGLIST_TAIL, 0x00}, // tail
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x00}, // YUV422
|
||||||
|
{FORMAT_CTRL00, 0x30}, // YUYV
|
||||||
|
{0x3002, 0x00},//0x1c to 0x00 !!!
|
||||||
|
{0x3006, 0xff},//0xc3 to 0xff !!!
|
||||||
|
{0x471c, 0x50},//0xd0 to 0x50 !!!
|
||||||
|
{REGLIST_TAIL, 0x00}, // tail
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = {
|
||||||
|
{FORMAT_CTRL00, 0x00}, // RAW
|
||||||
|
{REGLIST_TAIL, 0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x00}, // YUV422
|
||||||
|
{FORMAT_CTRL00, 0x10}, // Y8
|
||||||
|
{REGLIST_TAIL, 0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x00}, // YUV422
|
||||||
|
{FORMAT_CTRL00, 0x30}, // YUYV
|
||||||
|
{REGLIST_TAIL, 0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x01}, // RGB
|
||||||
|
{FORMAT_CTRL00, 0x61}, // RGB565 (BGR)
|
||||||
|
{REGLIST_TAIL, 0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint8_t sensor_saturation_levels[9][11] = {
|
||||||
|
{0x1d, 0x60, 0x03, 0x07, 0x48, 0x4f, 0x4b, 0x40, 0x0b, 0x01, 0x98},//-4
|
||||||
|
{0x1d, 0x60, 0x03, 0x08, 0x54, 0x5c, 0x58, 0x4b, 0x0d, 0x01, 0x98},//-3
|
||||||
|
{0x1d, 0x60, 0x03, 0x0a, 0x60, 0x6a, 0x64, 0x56, 0x0e, 0x01, 0x98},//-2
|
||||||
|
{0x1d, 0x60, 0x03, 0x0b, 0x6c, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98},//-1
|
||||||
|
{0x1d, 0x60, 0x03, 0x0c, 0x78, 0x84, 0x7d, 0x6b, 0x12, 0x01, 0x98},//0
|
||||||
|
{0x1d, 0x60, 0x03, 0x0d, 0x84, 0x91, 0x8a, 0x76, 0x14, 0x01, 0x98},//+1
|
||||||
|
{0x1d, 0x60, 0x03, 0x0e, 0x90, 0x9e, 0x96, 0x80, 0x16, 0x01, 0x98},//+2
|
||||||
|
{0x1d, 0x60, 0x03, 0x10, 0x9c, 0xac, 0xa2, 0x8b, 0x17, 0x01, 0x98},//+3
|
||||||
|
{0x1d, 0x60, 0x03, 0x11, 0xa8, 0xb9, 0xaf, 0x96, 0x19, 0x01, 0x98},//+4
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = {
|
||||||
|
{0x06, 0x40, 0x2c, 0x08},//Normal
|
||||||
|
{0x46, 0x40, 0x28, 0x08},//Negative
|
||||||
|
{0x1e, 0x80, 0x80, 0x08},//Grayscale
|
||||||
|
{0x1e, 0x80, 0xc0, 0x08},//Red Tint
|
||||||
|
{0x1e, 0x60, 0x60, 0x08},//Green Tint
|
||||||
|
{0x1e, 0xa0, 0x40, 0x08},//Blue Tint
|
||||||
|
{0x1e, 0x40, 0xa0, 0x08},//Sepia
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
1105
code/lib/sensors/ov5640.c
Normal file
1105
code/lib/sensors/ov5640.c
Normal file
File diff suppressed because it is too large
Load Diff
9
code/lib/sensors/ov5640.h
Normal file
9
code/lib/sensors/ov5640.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
#ifndef __OV5640_H__
|
||||||
|
#define __OV5640_H__
|
||||||
|
|
||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
int ov5640_init(sensor_t *sensor);
|
||||||
|
|
||||||
|
#endif // __OV5640_H__
|
||||||
213
code/lib/sensors/ov5640_regs.h
Normal file
213
code/lib/sensors/ov5640_regs.h
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* OV5640 register definitions.
|
||||||
|
*/
|
||||||
|
#ifndef __OV5640_REG_REGS_H__
|
||||||
|
#define __OV5640_REG_REGS_H__
|
||||||
|
|
||||||
|
/* system control registers */
|
||||||
|
#define SYSTEM_CTROL0 0x3008 // Bit[7]: Software reset
|
||||||
|
// Bit[6]: Software power down
|
||||||
|
// Bit[5]: Reserved
|
||||||
|
// Bit[4]: SRB clock SYNC enable
|
||||||
|
// Bit[3]: Isolation suspend select
|
||||||
|
// Bit[2:0]: Not used
|
||||||
|
|
||||||
|
#define DRIVE_CAPABILITY 0x302c // Bit[7:6]:
|
||||||
|
// 00: 1x
|
||||||
|
// 01: 2x
|
||||||
|
// 10: 3x
|
||||||
|
// 11: 4x
|
||||||
|
|
||||||
|
#define SC_PLLS_CTRL0 0x303a // Bit[7]: PLLS bypass
|
||||||
|
#define SC_PLLS_CTRL1 0x303b // Bit[4:0]: PLLS multiplier
|
||||||
|
#define SC_PLLS_CTRL2 0x303c // Bit[6:4]: PLLS charge pump control
|
||||||
|
// Bit[3:0]: PLLS system divider
|
||||||
|
#define SC_PLLS_CTRL3 0x303d // Bit[5:4]: PLLS pre-divider
|
||||||
|
// 00: 1
|
||||||
|
// 01: 1.5
|
||||||
|
// 10: 2
|
||||||
|
// 11: 3
|
||||||
|
// Bit[2]: PLLS root-divider - 1
|
||||||
|
// Bit[1:0]: PLLS seld5
|
||||||
|
// 00: 1
|
||||||
|
// 01: 1
|
||||||
|
// 10: 2
|
||||||
|
// 11: 2.5
|
||||||
|
|
||||||
|
/* AEC/AGC control functions */
|
||||||
|
#define AEC_PK_MANUAL 0x3503 // AEC Manual Mode Control
|
||||||
|
// Bit[7:6]: Reserved
|
||||||
|
// Bit[5]: Gain delay option
|
||||||
|
// Valid when 0x3503[4]=1’b0
|
||||||
|
// 0: Delay one frame latch
|
||||||
|
// 1: One frame latch
|
||||||
|
// Bit[4:2]: Reserved
|
||||||
|
// Bit[1]: AGC manual
|
||||||
|
// 0: Auto enable
|
||||||
|
// 1: Manual enable
|
||||||
|
// Bit[0]: AEC manual
|
||||||
|
// 0: Auto enable
|
||||||
|
// 1: Manual enable
|
||||||
|
|
||||||
|
//gain = {0x350A[1:0], 0x350B[7:0]} / 16
|
||||||
|
|
||||||
|
|
||||||
|
#define X_ADDR_ST_H 0x3800 //Bit[3:0]: X address start[11:8]
|
||||||
|
#define X_ADDR_ST_L 0x3801 //Bit[7:0]: X address start[7:0]
|
||||||
|
#define Y_ADDR_ST_H 0x3802 //Bit[2:0]: Y address start[10:8]
|
||||||
|
#define Y_ADDR_ST_L 0x3803 //Bit[7:0]: Y address start[7:0]
|
||||||
|
#define X_ADDR_END_H 0x3804 //Bit[3:0]: X address end[11:8]
|
||||||
|
#define X_ADDR_END_L 0x3805 //Bit[7:0]:
|
||||||
|
#define Y_ADDR_END_H 0x3806 //Bit[2:0]: Y address end[10:8]
|
||||||
|
#define Y_ADDR_END_L 0x3807 //Bit[7:0]:
|
||||||
|
// Size after scaling
|
||||||
|
#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
|
||||||
|
#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
|
||||||
|
#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
|
||||||
|
#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
|
||||||
|
#define X_TOTAL_SIZE_H 0x380c //Bit[3:0]: Total horizontal size[11:8]
|
||||||
|
#define X_TOTAL_SIZE_L 0x380d //Bit[7:0]:
|
||||||
|
#define Y_TOTAL_SIZE_H 0x380e //Bit[7:0]: Total vertical size[15:8]
|
||||||
|
#define Y_TOTAL_SIZE_L 0x380f //Bit[7:0]:
|
||||||
|
#define X_OFFSET_H 0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
|
||||||
|
#define X_OFFSET_L 0x3811 //Bit[7:0]:
|
||||||
|
#define Y_OFFSET_H 0x3812 //Bit[2:0]: ISP vertical offset[10:8]
|
||||||
|
#define Y_OFFSET_L 0x3813 //Bit[7:0]:
|
||||||
|
#define X_INCREMENT 0x3814 //Bit[7:4]: Horizontal odd subsample increment
|
||||||
|
//Bit[3:0]: Horizontal even subsample increment
|
||||||
|
#define Y_INCREMENT 0x3815 //Bit[7:4]: Vertical odd subsample increment
|
||||||
|
//Bit[3:0]: Vertical even subsample increment
|
||||||
|
// Size before scaling
|
||||||
|
//#define X_INPUT_SIZE (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
|
||||||
|
//#define Y_INPUT_SIZE (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
|
||||||
|
|
||||||
|
/* mirror and flip registers */
|
||||||
|
#define TIMING_TC_REG20 0x3820 // Timing Control Register
|
||||||
|
// Bit[2:1]: Vertical flip enable
|
||||||
|
// 00: Normal
|
||||||
|
// 11: Vertical flip
|
||||||
|
// Bit[0]: Vertical binning enable
|
||||||
|
#define TIMING_TC_REG21 0x3821 // Timing Control Register
|
||||||
|
// Bit[5]: Compression Enable
|
||||||
|
// Bit[2:1]: Horizontal mirror enable
|
||||||
|
// 00: Normal
|
||||||
|
// 11: Horizontal mirror
|
||||||
|
// Bit[0]: Horizontal binning enable
|
||||||
|
|
||||||
|
#define PCLK_RATIO 0x3824 // Bit[4:0]: PCLK ratio manual
|
||||||
|
|
||||||
|
/* frame control registers */
|
||||||
|
#define FRAME_CTRL01 0x4201 // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
|
||||||
|
// Bit[7:4]: Not used
|
||||||
|
// Bit[3:0]: Frame ON number
|
||||||
|
#define FRAME_CTRL02 0x4202 // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
|
||||||
|
// Bit[7:4]: Not used
|
||||||
|
// BIT[3:0]: Frame OFF number
|
||||||
|
|
||||||
|
/* format control registers */
|
||||||
|
#define FORMAT_CTRL00 0x4300
|
||||||
|
|
||||||
|
#define CLOCK_POL_CONTROL 0x4740// Bit[5]: PCLK polarity 0: active low
|
||||||
|
// 1: active high
|
||||||
|
// Bit[3]: Gate PCLK under VSYNC
|
||||||
|
// Bit[2]: Gate PCLK under HREF
|
||||||
|
// Bit[1]: HREF polarity
|
||||||
|
// 0: active low
|
||||||
|
// 1: active high
|
||||||
|
// Bit[0] VSYNC polarity
|
||||||
|
// 0: active low
|
||||||
|
// 1: active high
|
||||||
|
|
||||||
|
#define ISP_CONTROL_01 0x5001 // Bit[5]: Scale enable
|
||||||
|
// 0: Disable
|
||||||
|
// 1: Enable
|
||||||
|
|
||||||
|
/* output format control registers */
|
||||||
|
#define FORMAT_CTRL 0x501F // Format select
|
||||||
|
// Bit[2:0]:
|
||||||
|
// 000: YUV422
|
||||||
|
// 001: RGB
|
||||||
|
// 010: Dither
|
||||||
|
// 011: RAW after DPC
|
||||||
|
// 101: RAW after CIP
|
||||||
|
|
||||||
|
/* ISP top control registers */
|
||||||
|
#define PRE_ISP_TEST_SETTING_1 0x503D // Bit[7]: Test enable
|
||||||
|
// 0: Test disable
|
||||||
|
// 1: Color bar enable
|
||||||
|
// Bit[6]: Rolling
|
||||||
|
// Bit[5]: Transparent
|
||||||
|
// Bit[4]: Square black and white
|
||||||
|
// Bit[3:2]: Color bar style
|
||||||
|
// 00: Standard 8 color bar
|
||||||
|
// 01: Gradual change at vertical mode 1
|
||||||
|
// 10: Gradual change at horizontal
|
||||||
|
// 11: Gradual change at vertical mode 2
|
||||||
|
// Bit[1:0]: Test select
|
||||||
|
// 00: Color bar
|
||||||
|
// 01: Random data
|
||||||
|
// 10: Square data
|
||||||
|
// 11: Black image
|
||||||
|
|
||||||
|
//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
|
||||||
|
|
||||||
|
#define SCALE_CTRL_1 0x5601 // Bit[6:4]: HDIV RW
|
||||||
|
// DCW scale times
|
||||||
|
// 000: DCW 1 time
|
||||||
|
// 001: DCW 2 times
|
||||||
|
// 010: DCW 4 times
|
||||||
|
// 100: DCW 8 times
|
||||||
|
// 101: DCW 16 times
|
||||||
|
// Others: DCW 16 times
|
||||||
|
// Bit[2:0]: VDIV RW
|
||||||
|
// DCW scale times
|
||||||
|
// 000: DCW 1 time
|
||||||
|
// 001: DCW 2 times
|
||||||
|
// 010: DCW 4 times
|
||||||
|
// 100: DCW 8 times
|
||||||
|
// 101: DCW 16 times
|
||||||
|
// Others: DCW 16 times
|
||||||
|
|
||||||
|
#define SCALE_CTRL_2 0x5602 // X_SCALE High Bits
|
||||||
|
#define SCALE_CTRL_3 0x5603 // X_SCALE Low Bits
|
||||||
|
#define SCALE_CTRL_4 0x5604 // Y_SCALE High Bits
|
||||||
|
#define SCALE_CTRL_5 0x5605 // Y_SCALE Low Bits
|
||||||
|
#define SCALE_CTRL_6 0x5606 // Bit[3:0]: V Offset
|
||||||
|
|
||||||
|
#define VFIFO_CTRL0C 0x460C // Bit[1]: PCLK manual enable
|
||||||
|
// 0: Auto
|
||||||
|
// 1: Manual by PCLK_RATIO
|
||||||
|
|
||||||
|
#define VFIFO_X_SIZE_H 0x4602
|
||||||
|
#define VFIFO_X_SIZE_L 0x4603
|
||||||
|
#define VFIFO_Y_SIZE_H 0x4604
|
||||||
|
#define VFIFO_Y_SIZE_L 0x4605
|
||||||
|
|
||||||
|
#define COMPRESSION_CTRL00 0x4400 //
|
||||||
|
#define COMPRESSION_CTRL01 0x4401 //
|
||||||
|
#define COMPRESSION_CTRL02 0x4402 //
|
||||||
|
#define COMPRESSION_CTRL03 0x4403 //
|
||||||
|
#define COMPRESSION_CTRL04 0x4404 //
|
||||||
|
#define COMPRESSION_CTRL05 0x4405 //
|
||||||
|
#define COMPRESSION_CTRL06 0x4406 //
|
||||||
|
#define COMPRESSION_CTRL07 0x4407 // Bit[5:0]: QS
|
||||||
|
#define COMPRESSION_ISI_CTRL 0x4408 //
|
||||||
|
#define COMPRESSION_CTRL09 0x4409 //
|
||||||
|
#define COMPRESSION_CTRL0a 0x440a //
|
||||||
|
#define COMPRESSION_CTRL0b 0x440b //
|
||||||
|
#define COMPRESSION_CTRL0c 0x440c //
|
||||||
|
#define COMPRESSION_CTRL0d 0x440d //
|
||||||
|
#define COMPRESSION_CTRL0E 0x440e //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief register value
|
||||||
|
*/
|
||||||
|
#define TEST_COLOR_BAR 0xC0 /* Enable Color Bar roling Test */
|
||||||
|
|
||||||
|
#define AEC_PK_MANUAL_AGC_MANUALEN 0x02 /* Enable AGC Manual enable */
|
||||||
|
#define AEC_PK_MANUAL_AEC_MANUALEN 0x01 /* Enable AEC Manual enable */
|
||||||
|
|
||||||
|
#define TIMING_TC_REG20_VFLIP 0x06 /* Vertical flip enable */
|
||||||
|
#define TIMING_TC_REG21_HMIRROR 0x06 /* Horizontal mirror enable */
|
||||||
|
|
||||||
|
#endif // __OV3660_REG_REGS_H__
|
||||||
334
code/lib/sensors/ov5640_settings.h
Normal file
334
code/lib/sensors/ov5640_settings.h
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
#ifndef _OV5640_SETTINGS_H_
|
||||||
|
#define _OV5640_SETTINGS_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "ov5640_regs.h"
|
||||||
|
|
||||||
|
static const ratio_settings_t ratio_table[] = {
|
||||||
|
// mw, mh, sx, sy, ex, ey, ox, oy, tx, ty
|
||||||
|
{ 2560, 1920, 0, 0, 2623, 1951, 32, 16, 2844, 1968 }, //4x3
|
||||||
|
{ 2560, 1704, 0, 110, 2623, 1843, 32, 16, 2844, 1752 }, //3x2
|
||||||
|
{ 2560, 1600, 0, 160, 2623, 1791, 32, 16, 2844, 1648 }, //16x10
|
||||||
|
{ 2560, 1536, 0, 192, 2623, 1759, 32, 16, 2844, 1584 }, //5x3
|
||||||
|
{ 2560, 1440, 0, 240, 2623, 1711, 32, 16, 2844, 1488 }, //16x9
|
||||||
|
{ 2560, 1080, 0, 420, 2623, 1531, 32, 16, 2844, 1128 }, //21x9
|
||||||
|
{ 2400, 1920, 80, 0, 2543, 1951, 32, 16, 2684, 1968 }, //5x4
|
||||||
|
{ 1920, 1920, 320, 0, 2543, 1951, 32, 16, 2684, 1968 }, //1x1
|
||||||
|
{ 1088, 1920, 736, 0, 1887, 1951, 32, 16, 1884, 1968 } //9x16
|
||||||
|
};
|
||||||
|
|
||||||
|
#define REG_DLY 0xffff
|
||||||
|
#define REGLIST_TAIL 0x0000
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||||
|
{SYSTEM_CTROL0, 0x82}, // software reset
|
||||||
|
{REG_DLY, 10}, // delay 10ms
|
||||||
|
{SYSTEM_CTROL0, 0x42}, // power down
|
||||||
|
|
||||||
|
//enable pll
|
||||||
|
{0x3103, 0x13},
|
||||||
|
|
||||||
|
//io direction
|
||||||
|
{0x3017, 0xff},
|
||||||
|
{0x3018, 0xff},
|
||||||
|
|
||||||
|
{DRIVE_CAPABILITY, 0xc3},
|
||||||
|
{CLOCK_POL_CONTROL, 0x21},
|
||||||
|
|
||||||
|
{0x4713, 0x02},//jpg mode select
|
||||||
|
|
||||||
|
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
|
||||||
|
|
||||||
|
//sys reset
|
||||||
|
{0x3000, 0x00},
|
||||||
|
{0x3002, 0x1c},
|
||||||
|
|
||||||
|
//clock enable
|
||||||
|
{0x3004, 0xff},
|
||||||
|
{0x3006, 0xc3},
|
||||||
|
|
||||||
|
//isp control
|
||||||
|
{0x5000, 0xa7},
|
||||||
|
{ISP_CONTROL_01, 0xa3},//+scaling?
|
||||||
|
{0x5003, 0x08},//special_effect
|
||||||
|
|
||||||
|
//unknown
|
||||||
|
{0x370c, 0x02},//!!IMPORTANT
|
||||||
|
{0x3634, 0x40},//!!IMPORTANT
|
||||||
|
|
||||||
|
//AEC/AGC
|
||||||
|
{0x3a02, 0x03},
|
||||||
|
{0x3a03, 0xd8},
|
||||||
|
{0x3a08, 0x01},
|
||||||
|
{0x3a09, 0x27},
|
||||||
|
{0x3a0a, 0x00},
|
||||||
|
{0x3a0b, 0xf6},
|
||||||
|
{0x3a0d, 0x04},
|
||||||
|
{0x3a0e, 0x03},
|
||||||
|
{0x3a0f, 0x30},//ae_level
|
||||||
|
{0x3a10, 0x28},//ae_level
|
||||||
|
{0x3a11, 0x60},//ae_level
|
||||||
|
{0x3a13, 0x43},
|
||||||
|
{0x3a14, 0x03},
|
||||||
|
{0x3a15, 0xd8},
|
||||||
|
{0x3a18, 0x00},//gainceiling
|
||||||
|
{0x3a19, 0xf8},//gainceiling
|
||||||
|
{0x3a1b, 0x30},//ae_level
|
||||||
|
{0x3a1e, 0x26},//ae_level
|
||||||
|
{0x3a1f, 0x14},//ae_level
|
||||||
|
|
||||||
|
//vcm debug
|
||||||
|
{0x3600, 0x08},
|
||||||
|
{0x3601, 0x33},
|
||||||
|
|
||||||
|
//50/60Hz
|
||||||
|
{0x3c01, 0xa4},
|
||||||
|
{0x3c04, 0x28},
|
||||||
|
{0x3c05, 0x98},
|
||||||
|
{0x3c06, 0x00},
|
||||||
|
{0x3c07, 0x08},
|
||||||
|
{0x3c08, 0x00},
|
||||||
|
{0x3c09, 0x1c},
|
||||||
|
{0x3c0a, 0x9c},
|
||||||
|
{0x3c0b, 0x40},
|
||||||
|
|
||||||
|
{0x460c, 0x22},//disable jpeg footer
|
||||||
|
|
||||||
|
//BLC
|
||||||
|
{0x4001, 0x02},
|
||||||
|
{0x4004, 0x02},
|
||||||
|
|
||||||
|
//AWB
|
||||||
|
{0x5180, 0xff},
|
||||||
|
{0x5181, 0xf2},
|
||||||
|
{0x5182, 0x00},
|
||||||
|
{0x5183, 0x14},
|
||||||
|
{0x5184, 0x25},
|
||||||
|
{0x5185, 0x24},
|
||||||
|
{0x5186, 0x09},
|
||||||
|
{0x5187, 0x09},
|
||||||
|
{0x5188, 0x09},
|
||||||
|
{0x5189, 0x75},
|
||||||
|
{0x518a, 0x54},
|
||||||
|
{0x518b, 0xe0},
|
||||||
|
{0x518c, 0xb2},
|
||||||
|
{0x518d, 0x42},
|
||||||
|
{0x518e, 0x3d},
|
||||||
|
{0x518f, 0x56},
|
||||||
|
{0x5190, 0x46},
|
||||||
|
{0x5191, 0xf8},
|
||||||
|
{0x5192, 0x04},
|
||||||
|
{0x5193, 0x70},
|
||||||
|
{0x5194, 0xf0},
|
||||||
|
{0x5195, 0xf0},
|
||||||
|
{0x5196, 0x03},
|
||||||
|
{0x5197, 0x01},
|
||||||
|
{0x5198, 0x04},
|
||||||
|
{0x5199, 0x12},
|
||||||
|
{0x519a, 0x04},
|
||||||
|
{0x519b, 0x00},
|
||||||
|
{0x519c, 0x06},
|
||||||
|
{0x519d, 0x82},
|
||||||
|
{0x519e, 0x38},
|
||||||
|
|
||||||
|
//color matrix (Saturation)
|
||||||
|
{0x5381, 0x1e},
|
||||||
|
{0x5382, 0x5b},
|
||||||
|
{0x5383, 0x08},
|
||||||
|
{0x5384, 0x0a},
|
||||||
|
{0x5385, 0x7e},
|
||||||
|
{0x5386, 0x88},
|
||||||
|
{0x5387, 0x7c},
|
||||||
|
{0x5388, 0x6c},
|
||||||
|
{0x5389, 0x10},
|
||||||
|
{0x538a, 0x01},
|
||||||
|
{0x538b, 0x98},
|
||||||
|
|
||||||
|
//CIP control (Sharpness)
|
||||||
|
{0x5300, 0x10},//sharpness
|
||||||
|
{0x5301, 0x10},//sharpness
|
||||||
|
{0x5302, 0x18},//sharpness
|
||||||
|
{0x5303, 0x19},//sharpness
|
||||||
|
{0x5304, 0x10},
|
||||||
|
{0x5305, 0x10},
|
||||||
|
{0x5306, 0x08},//denoise
|
||||||
|
{0x5307, 0x16},
|
||||||
|
{0x5308, 0x40},
|
||||||
|
{0x5309, 0x10},//sharpness
|
||||||
|
{0x530a, 0x10},//sharpness
|
||||||
|
{0x530b, 0x04},//sharpness
|
||||||
|
{0x530c, 0x06},//sharpness
|
||||||
|
|
||||||
|
//GAMMA
|
||||||
|
{0x5480, 0x01},
|
||||||
|
{0x5481, 0x00},
|
||||||
|
{0x5482, 0x1e},
|
||||||
|
{0x5483, 0x3b},
|
||||||
|
{0x5484, 0x58},
|
||||||
|
{0x5485, 0x66},
|
||||||
|
{0x5486, 0x71},
|
||||||
|
{0x5487, 0x7d},
|
||||||
|
{0x5488, 0x83},
|
||||||
|
{0x5489, 0x8f},
|
||||||
|
{0x548a, 0x98},
|
||||||
|
{0x548b, 0xa6},
|
||||||
|
{0x548c, 0xb8},
|
||||||
|
{0x548d, 0xca},
|
||||||
|
{0x548e, 0xd7},
|
||||||
|
{0x548f, 0xe3},
|
||||||
|
{0x5490, 0x1d},
|
||||||
|
|
||||||
|
//Special Digital Effects (SDE) (UV adjust)
|
||||||
|
{0x5580, 0x06},//enable brightness and contrast
|
||||||
|
{0x5583, 0x40},//special_effect
|
||||||
|
{0x5584, 0x10},//special_effect
|
||||||
|
{0x5586, 0x20},//contrast
|
||||||
|
{0x5587, 0x00},//brightness
|
||||||
|
{0x5588, 0x00},//brightness
|
||||||
|
{0x5589, 0x10},
|
||||||
|
{0x558a, 0x00},
|
||||||
|
{0x558b, 0xf8},
|
||||||
|
{0x501d, 0x40},// enable manual offset of contrast
|
||||||
|
|
||||||
|
//power on
|
||||||
|
{0x3008, 0x02},
|
||||||
|
|
||||||
|
//50Hz
|
||||||
|
{0x3c00, 0x04},
|
||||||
|
|
||||||
|
{REG_DLY, 300},
|
||||||
|
{REGLIST_TAIL, 0x00}, // tail
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x00}, // YUV422
|
||||||
|
{FORMAT_CTRL00, 0x30}, // YUYV
|
||||||
|
{0x3002, 0x00},//0x1c to 0x00 !!!
|
||||||
|
{0x3006, 0xff},//0xc3 to 0xff !!!
|
||||||
|
{0x471c, 0x50},//0xd0 to 0x50 !!!
|
||||||
|
{REGLIST_TAIL, 0x00}, // tail
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x03}, // RAW (DPC)
|
||||||
|
{FORMAT_CTRL00, 0x00}, // RAW
|
||||||
|
{REGLIST_TAIL, 0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x00}, // YUV422
|
||||||
|
{FORMAT_CTRL00, 0x10}, // Y8
|
||||||
|
{REGLIST_TAIL, 0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x00}, // YUV422
|
||||||
|
{FORMAT_CTRL00, 0x30}, // YUYV
|
||||||
|
{REGLIST_TAIL, 0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = {
|
||||||
|
{FORMAT_CTRL, 0x01}, // RGB
|
||||||
|
{FORMAT_CTRL00, 0x61}, // RGB565 (BGR)
|
||||||
|
{REGLIST_TAIL, 0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint8_t sensor_saturation_levels[9][11] = {
|
||||||
|
{0x1d, 0x60, 0x03, 0x07, 0x48, 0x4f, 0x4b, 0x40, 0x0b, 0x01, 0x98},//-4
|
||||||
|
{0x1d, 0x60, 0x03, 0x08, 0x54, 0x5c, 0x58, 0x4b, 0x0d, 0x01, 0x98},//-3
|
||||||
|
{0x1d, 0x60, 0x03, 0x0a, 0x60, 0x6a, 0x64, 0x56, 0x0e, 0x01, 0x98},//-2
|
||||||
|
{0x1d, 0x60, 0x03, 0x0b, 0x6c, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98},//-1
|
||||||
|
{0x1d, 0x60, 0x03, 0x0c, 0x78, 0x84, 0x7d, 0x6b, 0x12, 0x01, 0x98},//0
|
||||||
|
{0x1d, 0x60, 0x03, 0x0d, 0x84, 0x91, 0x8a, 0x76, 0x14, 0x01, 0x98},//+1
|
||||||
|
{0x1d, 0x60, 0x03, 0x0e, 0x90, 0x9e, 0x96, 0x80, 0x16, 0x01, 0x98},//+2
|
||||||
|
{0x1d, 0x60, 0x03, 0x10, 0x9c, 0xac, 0xa2, 0x8b, 0x17, 0x01, 0x98},//+3
|
||||||
|
{0x1d, 0x60, 0x03, 0x11, 0xa8, 0xb9, 0xaf, 0x96, 0x19, 0x01, 0x98},//+4
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = {
|
||||||
|
{0x06, 0x40, 0x2c, 0x08},//Normal
|
||||||
|
{0x46, 0x40, 0x28, 0x08},//Negative
|
||||||
|
{0x1e, 0x80, 0x80, 0x08},//Grayscale
|
||||||
|
{0x1e, 0x80, 0xc0, 0x08},//Red Tint
|
||||||
|
{0x1e, 0x60, 0x60, 0x08},//Green Tint
|
||||||
|
{0x1e, 0xa0, 0x40, 0x08},//Blue Tint
|
||||||
|
{0x1e, 0x40, 0xa0, 0x08},//Sepia
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_regs_gamma0[][2] = {
|
||||||
|
{0x5480, 0x01},
|
||||||
|
{0x5481, 0x08},
|
||||||
|
{0x5482, 0x14},
|
||||||
|
{0x5483, 0x28},
|
||||||
|
{0x5484, 0x51},
|
||||||
|
{0x5485, 0x65},
|
||||||
|
{0x5486, 0x71},
|
||||||
|
{0x5487, 0x7d},
|
||||||
|
{0x5488, 0x87},
|
||||||
|
{0x5489, 0x91},
|
||||||
|
{0x548a, 0x9a},
|
||||||
|
{0x548b, 0xaa},
|
||||||
|
{0x548c, 0xb8},
|
||||||
|
{0x548d, 0xcd},
|
||||||
|
{0x548e, 0xdd},
|
||||||
|
{0x548f, 0xea},
|
||||||
|
{0x5490, 0x1d}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_regs_gamma1[][2] = {
|
||||||
|
{0x5480, 0x1},
|
||||||
|
{0x5481, 0x0},
|
||||||
|
{0x5482, 0x1e},
|
||||||
|
{0x5483, 0x3b},
|
||||||
|
{0x5484, 0x58},
|
||||||
|
{0x5485, 0x66},
|
||||||
|
{0x5486, 0x71},
|
||||||
|
{0x5487, 0x7d},
|
||||||
|
{0x5488, 0x83},
|
||||||
|
{0x5489, 0x8f},
|
||||||
|
{0x548a, 0x98},
|
||||||
|
{0x548b, 0xa6},
|
||||||
|
{0x548c, 0xb8},
|
||||||
|
{0x548d, 0xca},
|
||||||
|
{0x548e, 0xd7},
|
||||||
|
{0x548f, 0xe3},
|
||||||
|
{0x5490, 0x1d}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DRAM_ATTR uint16_t sensor_regs_awb0[][2] = {
|
||||||
|
{0x5180, 0xff},
|
||||||
|
{0x5181, 0xf2},
|
||||||
|
{0x5182, 0x00},
|
||||||
|
{0x5183, 0x14},
|
||||||
|
{0x5184, 0x25},
|
||||||
|
{0x5185, 0x24},
|
||||||
|
{0x5186, 0x09},
|
||||||
|
{0x5187, 0x09},
|
||||||
|
{0x5188, 0x09},
|
||||||
|
{0x5189, 0x75},
|
||||||
|
{0x518a, 0x54},
|
||||||
|
{0x518b, 0xe0},
|
||||||
|
{0x518c, 0xb2},
|
||||||
|
{0x518d, 0x42},
|
||||||
|
{0x518e, 0x3d},
|
||||||
|
{0x518f, 0x56},
|
||||||
|
{0x5190, 0x46},
|
||||||
|
{0x5191, 0xf8},
|
||||||
|
{0x5192, 0x04},
|
||||||
|
{0x5193, 0x70},
|
||||||
|
{0x5194, 0xf0},
|
||||||
|
{0x5195, 0xf0},
|
||||||
|
{0x5196, 0x03},
|
||||||
|
{0x5197, 0x01},
|
||||||
|
{0x5198, 0x04},
|
||||||
|
{0x5199, 0x12},
|
||||||
|
{0x519a, 0x04},
|
||||||
|
{0x519b, 0x00},
|
||||||
|
{0x519c, 0x06},
|
||||||
|
{0x519d, 0x82},
|
||||||
|
{0x519e, 0x38}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
557
code/lib/sensors/ov7725.c
Normal file
557
code/lib/sensors/ov7725.c
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* OV7725 driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "sccb.h"
|
||||||
|
#include "ov7725.h"
|
||||||
|
#include "ov7725_regs.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#else
|
||||||
|
#include "esp_log.h"
|
||||||
|
static const char* TAG = "ov7725";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static const uint8_t default_regs[][2] = {
|
||||||
|
{COM3, COM3_SWAP_YUV},
|
||||||
|
{COM7, COM7_RES_QVGA | COM7_FMT_YUV},
|
||||||
|
|
||||||
|
{COM4, 0x01 | 0x00}, /* bypass PLL (0x00:off, 0x40:4x, 0x80:6x, 0xC0:8x) */
|
||||||
|
{CLKRC, 0x80 | 0x03}, /* Res/Bypass pre-scalar (0x40:bypass, 0x00-0x3F:prescaler PCLK=XCLK/(prescaler + 1)/2 ) */
|
||||||
|
|
||||||
|
// QVGA Window Size
|
||||||
|
{HSTART, 0x3F},
|
||||||
|
{HSIZE, 0x50},
|
||||||
|
{VSTART, 0x03},
|
||||||
|
{VSIZE, 0x78},
|
||||||
|
{HREF, 0x00},
|
||||||
|
|
||||||
|
// Scale down to QVGA Resolution
|
||||||
|
{HOUTSIZE, 0x50},
|
||||||
|
{VOUTSIZE, 0x78},
|
||||||
|
{EXHCH, 0x00},
|
||||||
|
|
||||||
|
{COM12, 0x03},
|
||||||
|
{TGT_B, 0x7F},
|
||||||
|
{FIXGAIN, 0x09},
|
||||||
|
{AWB_CTRL0, 0xE0},
|
||||||
|
{DSP_CTRL1, 0xFF},
|
||||||
|
|
||||||
|
{DSP_CTRL2, DSP_CTRL2_VDCW_EN | DSP_CTRL2_HDCW_EN | DSP_CTRL2_HZOOM_EN | DSP_CTRL2_VZOOM_EN},
|
||||||
|
|
||||||
|
{DSP_CTRL3, 0x00},
|
||||||
|
{DSP_CTRL4, 0x00},
|
||||||
|
{DSPAUTO, 0xFF},
|
||||||
|
|
||||||
|
{COM8, 0xF0},
|
||||||
|
{COM6, 0xC5},
|
||||||
|
{COM9, 0x11},
|
||||||
|
{COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK}, //Invert VSYNC and MASK PCLK
|
||||||
|
{BDBASE, 0x7F},
|
||||||
|
{DBSTEP, 0x03},
|
||||||
|
{AEW, 0x96},
|
||||||
|
{AEB, 0x64},
|
||||||
|
{VPT, 0xA1},
|
||||||
|
{EXHCL, 0x00},
|
||||||
|
{AWB_CTRL3, 0xAA},
|
||||||
|
{COM8, 0xFF},
|
||||||
|
|
||||||
|
//Gamma
|
||||||
|
{GAM1, 0x0C},
|
||||||
|
{GAM2, 0x16},
|
||||||
|
{GAM3, 0x2A},
|
||||||
|
{GAM4, 0x4E},
|
||||||
|
{GAM5, 0x61},
|
||||||
|
{GAM6, 0x6F},
|
||||||
|
{GAM7, 0x7B},
|
||||||
|
{GAM8, 0x86},
|
||||||
|
{GAM9, 0x8E},
|
||||||
|
{GAM10, 0x97},
|
||||||
|
{GAM11, 0xA4},
|
||||||
|
{GAM12, 0xAF},
|
||||||
|
{GAM13, 0xC5},
|
||||||
|
{GAM14, 0xD7},
|
||||||
|
{GAM15, 0xE8},
|
||||||
|
|
||||||
|
{SLOP, 0x20},
|
||||||
|
{EDGE1, 0x05},
|
||||||
|
{EDGE2, 0x03},
|
||||||
|
{EDGE3, 0x00},
|
||||||
|
{DNSOFF, 0x01},
|
||||||
|
|
||||||
|
{MTX1, 0xB0},
|
||||||
|
{MTX2, 0x9D},
|
||||||
|
{MTX3, 0x13},
|
||||||
|
{MTX4, 0x16},
|
||||||
|
{MTX5, 0x7B},
|
||||||
|
{MTX6, 0x91},
|
||||||
|
{MTX_CTRL, 0x1E},
|
||||||
|
|
||||||
|
{BRIGHTNESS, 0x08},
|
||||||
|
{CONTRAST, 0x30},
|
||||||
|
{UVADJ0, 0x81},
|
||||||
|
{SDE, (SDE_CONT_BRIGHT_EN | SDE_SATURATION_EN)},
|
||||||
|
|
||||||
|
// For 30 fps/60Hz
|
||||||
|
{DM_LNL, 0x00},
|
||||||
|
{DM_LNH, 0x00},
|
||||||
|
{BDBASE, 0x7F},
|
||||||
|
{DBSTEP, 0x03},
|
||||||
|
|
||||||
|
// Lens Correction, should be tuned with real camera module
|
||||||
|
{LC_RADI, 0x10},
|
||||||
|
{LC_COEF, 0x10},
|
||||||
|
{LC_COEFB, 0x14},
|
||||||
|
{LC_COEFR, 0x17},
|
||||||
|
{LC_CTR, 0x05},
|
||||||
|
{COM5, 0xF5}, //0x65
|
||||||
|
|
||||||
|
{0x00, 0x00},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||||
|
{
|
||||||
|
int ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
|
||||||
|
if(ret > 0){
|
||||||
|
ret &= mask;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
|
||||||
|
if(ret < 0){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
value = (ret & ~mask) | (value & mask);
|
||||||
|
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = SCCB_Read(sensor->slv_addr, reg);
|
||||||
|
if(ret < 0){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
uint8_t mask = ((1 << length) - 1) << offset;
|
||||||
|
value = (ret & ~mask) | ((value << offset) & mask);
|
||||||
|
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = SCCB_Read(sensor->slv_addr, reg);
|
||||||
|
if(ret < 0){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
uint8_t mask = ((1 << length) - 1) << offset;
|
||||||
|
return (ret & mask) >> offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int reset(sensor_t *sensor)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
const uint8_t (*regs)[2];
|
||||||
|
|
||||||
|
// Reset all registers
|
||||||
|
SCCB_Write(sensor->slv_addr, COM7, COM7_RESET);
|
||||||
|
|
||||||
|
// Delay 10 ms
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
// Write default regsiters
|
||||||
|
for (i=0, regs = default_regs; regs[i][0]; i++) {
|
||||||
|
SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay
|
||||||
|
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
sensor->pixformat = pixformat;
|
||||||
|
// Read register COM7
|
||||||
|
uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
|
||||||
|
|
||||||
|
switch (pixformat) {
|
||||||
|
case PIXFORMAT_RGB565:
|
||||||
|
reg = COM7_SET_RGB(reg, COM7_FMT_RGB565);
|
||||||
|
break;
|
||||||
|
case PIXFORMAT_YUV422:
|
||||||
|
case PIXFORMAT_GRAYSCALE:
|
||||||
|
reg = COM7_SET_FMT(reg, COM7_FMT_YUV);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back register COM7
|
||||||
|
ret = SCCB_Write(sensor->slv_addr, COM7, reg);
|
||||||
|
|
||||||
|
// Delay
|
||||||
|
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
if (framesize > FRAMESIZE_VGA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint16_t w = resolution[framesize].width;
|
||||||
|
uint16_t h = resolution[framesize].height;
|
||||||
|
uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
|
||||||
|
|
||||||
|
sensor->status.framesize = framesize;
|
||||||
|
|
||||||
|
// Write MSBs
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, HOUTSIZE, w>>2);
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, VOUTSIZE, h>>1);
|
||||||
|
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, HSIZE, w>>2);
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, VSIZE, h>>1);
|
||||||
|
|
||||||
|
// Write LSBs
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, HREF, ((w&0x3) | ((h&0x1) << 2)));
|
||||||
|
|
||||||
|
if (framesize < FRAMESIZE_VGA) {
|
||||||
|
// Enable auto-scaling/zooming factors
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, DSPAUTO, 0xFF);
|
||||||
|
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, HSTART, 0x3F);
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, VSTART, 0x03);
|
||||||
|
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, COM7, reg | COM7_RES_QVGA);
|
||||||
|
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, CLKRC, 0x80 | 0x01);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Disable auto-scaling/zooming factors
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, DSPAUTO, 0xF3);
|
||||||
|
|
||||||
|
// Clear auto-scaling/zooming factors
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, SCAL0, 0x00);
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, SCAL1, 0x00);
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, SCAL2, 0x00);
|
||||||
|
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, HSTART, 0x23);
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, VSTART, 0x07);
|
||||||
|
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, COM7, reg & ~COM7_RES_QVGA);
|
||||||
|
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, CLKRC, 0x80 | 0x03);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay
|
||||||
|
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_colorbar(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
uint8_t reg;
|
||||||
|
sensor->status.colorbar = enable;
|
||||||
|
|
||||||
|
// Read reg COM3
|
||||||
|
reg = SCCB_Read(sensor->slv_addr, COM3);
|
||||||
|
// Enable colorbar test pattern output
|
||||||
|
reg = COM3_SET_CBAR(reg, enable);
|
||||||
|
// Write back COM3
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, COM3, reg);
|
||||||
|
|
||||||
|
// Read reg DSP_CTRL3
|
||||||
|
reg = SCCB_Read(sensor->slv_addr, DSP_CTRL3);
|
||||||
|
// Enable DSP colorbar output
|
||||||
|
reg = DSP_CTRL3_SET_CBAR(reg, enable);
|
||||||
|
// Write back DSP_CTRL3
|
||||||
|
ret |= SCCB_Write(sensor->slv_addr, DSP_CTRL3, reg);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_whitebal(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
if(set_reg_bits(sensor, COM8, 1, 1, enable) >= 0){
|
||||||
|
sensor->status.awb = !!enable;
|
||||||
|
}
|
||||||
|
return sensor->status.awb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_gain_ctrl(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
if(set_reg_bits(sensor, COM8, 2, 1, enable) >= 0){
|
||||||
|
sensor->status.agc = !!enable;
|
||||||
|
}
|
||||||
|
return sensor->status.agc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_exposure_ctrl(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
if(set_reg_bits(sensor, COM8, 0, 1, enable) >= 0){
|
||||||
|
sensor->status.aec = !!enable;
|
||||||
|
}
|
||||||
|
return sensor->status.aec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_hmirror(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
if(set_reg_bits(sensor, COM3, 6, 1, enable) >= 0){
|
||||||
|
sensor->status.hmirror = !!enable;
|
||||||
|
}
|
||||||
|
return sensor->status.hmirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_vflip(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
if(set_reg_bits(sensor, COM3, 7, 1, enable) >= 0){
|
||||||
|
sensor->status.vflip = !!enable;
|
||||||
|
}
|
||||||
|
return sensor->status.vflip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_dcw_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = set_reg_bits(sensor, 0x65, 2, 1, !enable);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set dcw to: %d", enable);
|
||||||
|
sensor->status.dcw = enable;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_aec2(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = set_reg_bits(sensor, COM8, 7, 1, enable);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set aec2 to: %d", enable);
|
||||||
|
sensor->status.aec2 = enable;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_bpc_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = set_reg_bits(sensor, 0x64, 1, 1, enable);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set bpc to: %d", enable);
|
||||||
|
sensor->status.bpc = enable;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_wpc_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = set_reg_bits(sensor, 0x64, 0, 1, enable);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set wpc to: %d", enable);
|
||||||
|
sensor->status.wpc = enable;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = set_reg_bits(sensor, 0x64, 2, 1, enable);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set raw_gma to: %d", enable);
|
||||||
|
sensor->status.raw_gma = enable;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_lenc_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = set_reg_bits(sensor, LC_CTR, 0, 1, enable);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set lenc to: %d", enable);
|
||||||
|
sensor->status.lenc = enable;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//real gain
|
||||||
|
static int set_agc_gain(sensor_t *sensor, int gain)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = set_reg_bits(sensor, COM9, 4, 3, gain % 5);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set gain to: %d", gain);
|
||||||
|
sensor->status.agc_gain = gain;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_aec_value(sensor_t *sensor, int value)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = SCCB_Write(sensor->slv_addr, AEC, value & 0xff) | SCCB_Write(sensor->slv_addr, AECH, value >> 8);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set aec_value to: %d", value);
|
||||||
|
sensor->status.aec_value = value;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_awb_gain_dsp(sensor_t *sensor, int enable)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = set_reg_bits(sensor, 0x63, 7, 1, enable);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set awb_gain to: %d", enable);
|
||||||
|
sensor->status.awb_gain = enable;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_brightness(sensor_t *sensor, int level)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = SCCB_Write(sensor->slv_addr, 0x9B, level);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set brightness to: %d", level);
|
||||||
|
sensor->status.brightness = level;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_contrast(sensor_t *sensor, int level)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret = SCCB_Write(sensor->slv_addr, 0x9C, level);
|
||||||
|
if (ret == 0) {
|
||||||
|
ESP_LOGD(TAG, "Set contrast to: %d", level);
|
||||||
|
sensor->status.contrast = level;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_status(sensor_t *sensor)
|
||||||
|
{
|
||||||
|
sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x9B);
|
||||||
|
sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0x9C);
|
||||||
|
sensor->status.saturation = 0;
|
||||||
|
sensor->status.ae_level = 0;
|
||||||
|
sensor->status.special_effect = get_reg_bits(sensor, 0x64, 5, 1);
|
||||||
|
sensor->status.wb_mode = get_reg_bits(sensor, 0x6B, 7, 1);
|
||||||
|
sensor->status.agc_gain = get_reg_bits(sensor, COM9, 4, 3);
|
||||||
|
sensor->status.aec_value = SCCB_Read(sensor->slv_addr, AEC) | (SCCB_Read(sensor->slv_addr, AECH) << 8);
|
||||||
|
sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x00);
|
||||||
|
sensor->status.awb = get_reg_bits(sensor, COM8, 1, 1);
|
||||||
|
sensor->status.awb_gain = get_reg_bits(sensor, 0x63, 7, 1);
|
||||||
|
sensor->status.aec = get_reg_bits(sensor, COM8, 0, 1);
|
||||||
|
sensor->status.aec2 = get_reg_bits(sensor, COM8, 7, 1);
|
||||||
|
sensor->status.agc = get_reg_bits(sensor, COM8, 2, 1);
|
||||||
|
sensor->status.bpc = get_reg_bits(sensor, 0x64, 1, 1);
|
||||||
|
sensor->status.wpc = get_reg_bits(sensor, 0x64, 0, 1);
|
||||||
|
sensor->status.raw_gma = get_reg_bits(sensor, 0x64, 2, 1);
|
||||||
|
sensor->status.lenc = get_reg_bits(sensor, LC_CTR, 0, 1);
|
||||||
|
sensor->status.hmirror = get_reg_bits(sensor, COM3, 6, 1);
|
||||||
|
sensor->status.vflip = get_reg_bits(sensor, COM3, 7, 1);
|
||||||
|
sensor->status.dcw = get_reg_bits(sensor, 0x65, 2, 1);
|
||||||
|
sensor->status.colorbar = get_reg_bits(sensor, COM3, 0, 1);
|
||||||
|
sensor->status.sharpness = get_reg_bits(sensor, EDGE0, 0, 5);
|
||||||
|
sensor->status.denoise = SCCB_Read(sensor->slv_addr, 0x8E);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||||
|
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
|
||||||
|
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
|
||||||
|
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
|
||||||
|
|
||||||
|
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||||
|
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||||
|
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ov7725_init(sensor_t *sensor)
|
||||||
|
{
|
||||||
|
// Set function pointers
|
||||||
|
sensor->reset = reset;
|
||||||
|
sensor->init_status = init_status;
|
||||||
|
sensor->set_pixformat = set_pixformat;
|
||||||
|
sensor->set_framesize = set_framesize;
|
||||||
|
sensor->set_colorbar = set_colorbar;
|
||||||
|
sensor->set_whitebal = set_whitebal;
|
||||||
|
sensor->set_gain_ctrl = set_gain_ctrl;
|
||||||
|
sensor->set_exposure_ctrl = set_exposure_ctrl;
|
||||||
|
sensor->set_hmirror = set_hmirror;
|
||||||
|
sensor->set_vflip = set_vflip;
|
||||||
|
|
||||||
|
sensor->set_brightness = set_brightness;
|
||||||
|
sensor->set_contrast = set_contrast;
|
||||||
|
sensor->set_aec2 = set_aec2;
|
||||||
|
sensor->set_aec_value = set_aec_value;
|
||||||
|
sensor->set_awb_gain = set_awb_gain_dsp;
|
||||||
|
sensor->set_agc_gain = set_agc_gain;
|
||||||
|
sensor->set_dcw = set_dcw_dsp;
|
||||||
|
sensor->set_bpc = set_bpc_dsp;
|
||||||
|
sensor->set_wpc = set_wpc_dsp;
|
||||||
|
sensor->set_raw_gma = set_raw_gma_dsp;
|
||||||
|
sensor->set_lenc = set_lenc_dsp;
|
||||||
|
|
||||||
|
//not supported
|
||||||
|
sensor->set_saturation= set_dummy;
|
||||||
|
sensor->set_sharpness = set_dummy;
|
||||||
|
sensor->set_denoise = set_dummy;
|
||||||
|
sensor->set_quality = set_dummy;
|
||||||
|
sensor->set_special_effect = set_dummy;
|
||||||
|
sensor->set_wb_mode = set_dummy;
|
||||||
|
sensor->set_ae_level = set_dummy;
|
||||||
|
sensor->set_gainceiling = set_gainceiling_dummy;
|
||||||
|
|
||||||
|
|
||||||
|
sensor->get_reg = get_reg;
|
||||||
|
sensor->set_reg = set_reg;
|
||||||
|
sensor->set_res_raw = set_res_raw;
|
||||||
|
sensor->set_pll = _set_pll;
|
||||||
|
sensor->set_xclk = set_xclk;
|
||||||
|
|
||||||
|
// Retrieve sensor's signature
|
||||||
|
sensor->id.MIDH = SCCB_Read(sensor->slv_addr, REG_MIDH);
|
||||||
|
sensor->id.MIDL = SCCB_Read(sensor->slv_addr, REG_MIDL);
|
||||||
|
sensor->id.PID = SCCB_Read(sensor->slv_addr, REG_PID);
|
||||||
|
sensor->id.VER = SCCB_Read(sensor->slv_addr, REG_VER);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "OV7725 Attached");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
14
code/lib/sensors/ov7725.h
Normal file
14
code/lib/sensors/ov7725.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* OV7725 driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef __OV7725_H__
|
||||||
|
#define __OV7725_H__
|
||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
int ov7725_init(sensor_t *sensor);
|
||||||
|
#endif // __OV7725_H__
|
||||||
335
code/lib/sensors/ov7725_regs.h
Normal file
335
code/lib/sensors/ov7725_regs.h
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* OV2640 register definitions.
|
||||||
|
*/
|
||||||
|
#ifndef __REG_REGS_H__
|
||||||
|
#define __REG_REGS_H__
|
||||||
|
#define GAIN 0x00 /* AGC – Gain control gain setting */
|
||||||
|
#define BLUE 0x01 /* AWB – Blue channel gain setting */
|
||||||
|
#define RED 0x02 /* AWB – Red channel gain setting */
|
||||||
|
#define GREEN 0x03 /* AWB – Green channel gain setting */
|
||||||
|
#define BAVG 0x05 /* U/B Average Level */
|
||||||
|
#define GAVG 0x06 /* Y/Gb Average Level */
|
||||||
|
#define RAVG 0x07 /* V/R Average Level */
|
||||||
|
#define AECH 0x08 /* Exposure Value – AEC MSBs */
|
||||||
|
|
||||||
|
#define COM2 0x09 /* Common Control 2 */
|
||||||
|
#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */
|
||||||
|
#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */
|
||||||
|
#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */
|
||||||
|
#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */
|
||||||
|
#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */
|
||||||
|
|
||||||
|
#define REG_PID 0x0A /* Product ID Number MSB */
|
||||||
|
#define REG_VER 0x0B /* Product ID Number LSB */
|
||||||
|
|
||||||
|
#define COM3 0x0C /* Common Control 3 */
|
||||||
|
#define COM3_VFLIP 0x80 /* Vertical flip image ON/OFF selection */
|
||||||
|
#define COM3_MIRROR 0x40 /* Horizontal mirror image ON/OFF selection */
|
||||||
|
#define COM3_SWAP_BR 0x20 /* Swap B/R output sequence in RGB output mode */
|
||||||
|
#define COM3_SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV output mode */
|
||||||
|
#define COM3_SWAP_MSB 0x08 /* Swap output MSB/LSB */
|
||||||
|
#define COM3_TRI_CLOCK 0x04 /* Tri-state option for output clock at power-down period */
|
||||||
|
#define COM3_TRI_DATA 0x02 /* Tri-state option for output data at power-down period */
|
||||||
|
#define COM3_COLOR_BAR 0x01 /* Sensor color bar test pattern output enable */
|
||||||
|
#define COM3_SET_CBAR(r, x) ((r&0xFE)|((x&1)<<0))
|
||||||
|
#define COM3_SET_MIRROR(r, x) ((r&0xBF)|((x&1)<<6))
|
||||||
|
#define COM3_SET_FLIP(r, x) ((r&0x7F)|((x&1)<<7))
|
||||||
|
|
||||||
|
#define COM4 0x0D /* Common Control 4 */
|
||||||
|
#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */
|
||||||
|
#define COM4_PLL_4x 0x40 /* PLL frequency 4x */
|
||||||
|
#define COM4_PLL_6x 0x80 /* PLL frequency 6x */
|
||||||
|
#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */
|
||||||
|
#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */
|
||||||
|
#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */
|
||||||
|
#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */
|
||||||
|
#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */
|
||||||
|
|
||||||
|
#define COM5 0x0E /* Common Control 5 */
|
||||||
|
#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */
|
||||||
|
#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */
|
||||||
|
#define COM5_AFR_0 0x00 /* No reduction of frame rate */
|
||||||
|
#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */
|
||||||
|
#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */
|
||||||
|
#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */
|
||||||
|
#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */
|
||||||
|
#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */
|
||||||
|
#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */
|
||||||
|
#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */
|
||||||
|
|
||||||
|
#define COM6 0x0F /* Common Control 6 */
|
||||||
|
#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */
|
||||||
|
|
||||||
|
#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
|
||||||
|
#define CLKRC 0x11 /* Internal Clock */
|
||||||
|
|
||||||
|
#define COM7 0x12 /* Common Control 7 */
|
||||||
|
#define COM7_RESET 0x80 /* SCCB Register Reset */
|
||||||
|
#define COM7_RES_VGA 0x00 /* Resolution VGA */
|
||||||
|
#define COM7_RES_QVGA 0x40 /* Resolution QVGA */
|
||||||
|
#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */
|
||||||
|
#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */
|
||||||
|
#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */
|
||||||
|
#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */
|
||||||
|
#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */
|
||||||
|
#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */
|
||||||
|
#define COM7_FMT_YUV 0x00 /* Output format YUV */
|
||||||
|
#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */
|
||||||
|
#define COM7_FMT_RGB 0x02 /* Output format RGB */
|
||||||
|
#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */
|
||||||
|
#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x3)<<0))
|
||||||
|
#define COM7_SET_RGB(r, x) ((r&0xF0)|(x&0x0C)|COM7_FMT_RGB)
|
||||||
|
|
||||||
|
#define COM8 0x13 /* Common Control 8 */
|
||||||
|
#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */
|
||||||
|
#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */
|
||||||
|
#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */
|
||||||
|
#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */
|
||||||
|
#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */
|
||||||
|
#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */
|
||||||
|
#define COM8_AGC_EN 0x04 /* AGC Enable */
|
||||||
|
#define COM8_AWB_EN 0x02 /* AWB Enable */
|
||||||
|
#define COM8_AEC_EN 0x01 /* AEC Enable */
|
||||||
|
#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2))
|
||||||
|
#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1))
|
||||||
|
#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0))
|
||||||
|
|
||||||
|
#define COM9 0x14 /* Common Control 9 */
|
||||||
|
#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */
|
||||||
|
#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */
|
||||||
|
#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */
|
||||||
|
#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */
|
||||||
|
#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */
|
||||||
|
#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */
|
||||||
|
#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
|
||||||
|
#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
|
||||||
|
#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4))
|
||||||
|
|
||||||
|
#define COM10 0x15 /* Common Control 10 */
|
||||||
|
#define COM10_NEGATIVE 0x80 /* Output negative data */
|
||||||
|
#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */
|
||||||
|
#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */
|
||||||
|
#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */
|
||||||
|
#define COM10_PCLK_REV 0x10 /* PCLK reverse */
|
||||||
|
#define COM10_HREF_REV 0x08 /* HREF reverse */
|
||||||
|
#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */
|
||||||
|
#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */
|
||||||
|
#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */
|
||||||
|
#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */
|
||||||
|
#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
|
||||||
|
|
||||||
|
#define REG16 0x16 /* Register 16 */
|
||||||
|
#define REG16_BIT_SHIFT 0x80 /* Bit shift test pattern options */
|
||||||
|
#define HSTART 0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */
|
||||||
|
#define HSIZE 0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */
|
||||||
|
#define VSTART 0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */
|
||||||
|
#define VSIZE 0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */
|
||||||
|
#define PSHFT 0x1B /* Data Format - Pixel Delay Select */
|
||||||
|
#define REG_MIDH 0x1C /* Manufacturer ID Byte – High */
|
||||||
|
#define REG_MIDL 0x1D /* Manufacturer ID Byte – Low */
|
||||||
|
#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period */
|
||||||
|
|
||||||
|
#define COM11 0x20 /* Common Control 11 */
|
||||||
|
#define COM11_SNGL_FRAME_EN 0x02 /* Single frame ON/OFF selection */
|
||||||
|
#define COM11_SNGL_XFR_TRIG 0x01 /* Single frame transfer trigger */
|
||||||
|
|
||||||
|
#define BDBASE 0x22 /* Banding Filter Minimum AEC Value */
|
||||||
|
#define DBSTEP 0x23 /* Banding Filter Maximum Step */
|
||||||
|
#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
|
||||||
|
#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
|
||||||
|
#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */
|
||||||
|
#define REG28 0x28 /* Selection on the number of dummy rows, N */
|
||||||
|
#define HOUTSIZE 0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */
|
||||||
|
#define EXHCH 0x2A /* Dummy Pixel Insert MSB */
|
||||||
|
#define EXHCL 0x2B /* Dummy Pixel Insert LSB */
|
||||||
|
#define VOUTSIZE 0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2]) */
|
||||||
|
#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */
|
||||||
|
#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
|
||||||
|
#define YAVE 0x2F /* Y/G Channel Average Value */
|
||||||
|
#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance High Level Threshold */
|
||||||
|
#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance Low Level Threshold */
|
||||||
|
#define HREF 0x32 /* Image Start and Size Control */
|
||||||
|
#define DM_LNL 0x33 /* Dummy Row Low 8 Bits */
|
||||||
|
#define DM_LNH 0x34 /* Dummy Row High 8 Bits */
|
||||||
|
#define ADOFF_B 0x35 /* AD Offset Compensation Value for B Channel */
|
||||||
|
#define ADOFF_R 0x36 /* AD Offset Compensation Value for R Channel */
|
||||||
|
#define ADOFF_GB 0x37 /* AD Offset Compensation Value for GB Channel */
|
||||||
|
#define ADOFF_GR 0x38 /* AD Offset Compensation Value for GR Channel */
|
||||||
|
#define OFF_B 0x39 /* AD Offset Compensation Value for B Channel */
|
||||||
|
#define OFF_R 0x3A /* AD Offset Compensation Value for R Channel */
|
||||||
|
#define OFF_GB 0x3B /* AD Offset Compensation Value for GB Channel */
|
||||||
|
#define OFF_GR 0x3C /* AD Offset Compensation Value for GR Channel */
|
||||||
|
#define COM12 0x3D /* DC offset compensation for analog process */
|
||||||
|
|
||||||
|
#define COM13 0x3E /* Common Control 13 */
|
||||||
|
#define COM13_BLC_EN 0x80 /* BLC enable */
|
||||||
|
#define COM13_ADC_EN 0x40 /* ADC channel BLC ON/OFF control */
|
||||||
|
#define COM13_ANALOG_BLC 0x20 /* Analog processing channel BLC ON/OFF control */
|
||||||
|
#define COM13_ABLC_GAIN_EN 0x04 /* ABLC gain trigger enable */
|
||||||
|
|
||||||
|
#define COM14 0x3F /* Common Control 14 */
|
||||||
|
#define COM15 0x40 /* Common Control 15 */
|
||||||
|
#define COM16 0x41 /* Common Control 16 */
|
||||||
|
#define TGT_B 0x42 /* BLC Blue Channel Target Value */
|
||||||
|
#define TGT_R 0x43 /* BLC Red Channel Target Value */
|
||||||
|
#define TGT_GB 0x44 /* BLC Gb Channel Target Value */
|
||||||
|
#define TGT_GR 0x45 /* BLC Gr Channel Target Value */
|
||||||
|
|
||||||
|
#define LC_CTR 0x46 /* Lens Correction Control */
|
||||||
|
#define LC_CTR_RGB_COMP_1 0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */
|
||||||
|
#define LC_CTR_RGB_COMP_3 0x04 /* R, G, and B channel compensation coefficient is set by registers
|
||||||
|
LC_COEFB (0x4B), LC_COEF (0x49), and LC_COEFR (0x4C), respectively */
|
||||||
|
#define LC_CTR_EN 0x01 /* Lens correction enable */
|
||||||
|
#define LC_XC 0x47 /* X Coordinate of Lens Correction Center Relative to Array Center */
|
||||||
|
#define LC_YC 0x48 /* Y Coordinate of Lens Correction Center Relative to Array Center */
|
||||||
|
#define LC_COEF 0x49 /* Lens Correction Coefficient */
|
||||||
|
#define LC_RADI 0x4A /* Lens Correction Radius */
|
||||||
|
#define LC_COEFB 0x4B /* Lens Correction B Channel Compensation Coefficient */
|
||||||
|
#define LC_COEFR 0x4C /* Lens Correction R Channel Compensation Coefficient */
|
||||||
|
|
||||||
|
#define FIXGAIN 0x4D /* Analog Fix Gain Amplifier */
|
||||||
|
#define AREF0 0x4E /* Sensor Reference Control */
|
||||||
|
#define AREF1 0x4F /* Sensor Reference Current Control */
|
||||||
|
#define AREF2 0x50 /* Analog Reference Control */
|
||||||
|
#define AREF3 0x51 /* ADC Reference Control */
|
||||||
|
#define AREF4 0x52 /* ADC Reference Control */
|
||||||
|
#define AREF5 0x53 /* ADC Reference Control */
|
||||||
|
#define AREF6 0x54 /* Analog Reference Control */
|
||||||
|
#define AREF7 0x55 /* Analog Reference Control */
|
||||||
|
#define UFIX 0x60 /* U Channel Fixed Value Output */
|
||||||
|
#define VFIX 0x61 /* V Channel Fixed Value Output */
|
||||||
|
#define AWBB_BLK 0x62 /* AWB Option for Advanced AWB */
|
||||||
|
|
||||||
|
#define AWB_CTRL0 0x63 /* AWB Control Byte 0 */
|
||||||
|
#define AWB_CTRL0_GAIN_EN 0x80 /* AWB gain enable */
|
||||||
|
#define AWB_CTRL0_CALC_EN 0x40 /* AWB calculate enable */
|
||||||
|
#define AWB_CTRL0_WBC_MASK 0x0F /* WBC threshold 2 */
|
||||||
|
|
||||||
|
#define DSP_CTRL1 0x64 /* DSP Control Byte 1 */
|
||||||
|
#define DSP_CTRL1_FIFO_EN 0x80 /* FIFO enable/disable selection */
|
||||||
|
#define DSP_CTRL1_UV_EN 0x40 /* UV adjust function ON/OFF selection */
|
||||||
|
#define DSP_CTRL1_SDE_EN 0x20 /* SDE enable */
|
||||||
|
#define DSP_CTRL1_MTRX_EN 0x10 /* Color matrix ON/OFF selection */
|
||||||
|
#define DSP_CTRL1_INTRP_EN 0x08 /* Interpolation ON/OFF selection */
|
||||||
|
#define DSP_CTRL1_GAMMA_EN 0x04 /* Gamma function ON/OFF selection */
|
||||||
|
#define DSP_CTRL1_BLACK_EN 0x02 /* Black defect auto correction ON/OFF */
|
||||||
|
#define DSP_CTRL1_WHITE_EN 0x01 /* White defect auto correction ON/OFF */
|
||||||
|
|
||||||
|
#define DSP_CTRL2 0x65 /* DSP Control Byte 2 */
|
||||||
|
#define DSP_CTRL2_VDCW_EN 0x08 /* Vertical DCW enable */
|
||||||
|
#define DSP_CTRL2_HDCW_EN 0x04 /* Horizontal DCW enable */
|
||||||
|
#define DSP_CTRL2_VZOOM_EN 0x02 /* Vertical zoom out enable */
|
||||||
|
#define DSP_CTRL2_HZOOM_EN 0x01 /* Horizontal zoom out enable */
|
||||||
|
|
||||||
|
#define DSP_CTRL3 0x66 /* DSP Control Byte 3 */
|
||||||
|
#define DSP_CTRL3_UV_EN 0x80 /* UV output sequence option */
|
||||||
|
#define DSP_CTRL3_CBAR_EN 0x20 /* DSP color bar ON/OFF selection */
|
||||||
|
#define DSP_CTRL3_FIFO_EN 0x08 /* FIFO power down ON/OFF selection */
|
||||||
|
#define DSP_CTRL3_SCAL1_PWDN 0x04 /* Scaling module power down control 1 */
|
||||||
|
#define DSP_CTRL3_SCAL2_PWDN 0x02 /* Scaling module power down control 2 */
|
||||||
|
#define DSP_CTRL3_INTRP_PWDN 0x01 /* Interpolation module power down control */
|
||||||
|
#define DSP_CTRL3_SET_CBAR(r, x) ((r&0xDF)|((x&1)<<5))
|
||||||
|
|
||||||
|
|
||||||
|
#define DSP_CTRL4 0x67 /* DSP Control Byte 4 */
|
||||||
|
#define DSP_CTRL4_YUV_RGB 0x00 /* Output selection YUV or RGB */
|
||||||
|
#define DSP_CTRL4_RAW8 0x02 /* Output selection RAW8 */
|
||||||
|
#define DSP_CTRL4_RAW10 0x03 /* Output selection RAW10 */
|
||||||
|
|
||||||
|
|
||||||
|
#define AWB_BIAS 0x68 /* AWB BLC Level Clip */
|
||||||
|
#define AWB_CTRL1 0x69 /* AWB Control 1 */
|
||||||
|
#define AWB_CTRL2 0x6A /* AWB Control 2 */
|
||||||
|
|
||||||
|
#define AWB_CTRL3 0x6B /* AWB Control 3 */
|
||||||
|
#define AWB_CTRL3_ADVANCED 0x80 /* AWB mode select - Advanced AWB */
|
||||||
|
#define AWB_CTRL3_SIMPLE 0x00 /* AWB mode select - Simple AWB */
|
||||||
|
|
||||||
|
#define AWB_CTRL4 0x6C /* AWB Control 4 */
|
||||||
|
#define AWB_CTRL5 0x6D /* AWB Control 5 */
|
||||||
|
#define AWB_CTRL6 0x6E /* AWB Control 6 */
|
||||||
|
#define AWB_CTRL7 0x6F /* AWB Control 7 */
|
||||||
|
#define AWB_CTRL8 0x70 /* AWB Control 8 */
|
||||||
|
#define AWB_CTRL9 0x71 /* AWB Control 9 */
|
||||||
|
#define AWB_CTRL10 0x72 /* AWB Control 10 */
|
||||||
|
#define AWB_CTRL11 0x73 /* AWB Control 11 */
|
||||||
|
#define AWB_CTRL12 0x74 /* AWB Control 12 */
|
||||||
|
#define AWB_CTRL13 0x75 /* AWB Control 13 */
|
||||||
|
#define AWB_CTRL14 0x76 /* AWB Control 14 */
|
||||||
|
#define AWB_CTRL15 0x77 /* AWB Control 15 */
|
||||||
|
#define AWB_CTRL16 0x78 /* AWB Control 16 */
|
||||||
|
#define AWB_CTRL17 0x79 /* AWB Control 17 */
|
||||||
|
#define AWB_CTRL18 0x7A /* AWB Control 18 */
|
||||||
|
#define AWB_CTRL19 0x7B /* AWB Control 19 */
|
||||||
|
#define AWB_CTRL20 0x7C /* AWB Control 20 */
|
||||||
|
#define AWB_CTRL21 0x7D /* AWB Control 21 */
|
||||||
|
#define GAM1 0x7E /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
|
||||||
|
#define GAM2 0x7F /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
|
||||||
|
#define GAM3 0x80 /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
|
||||||
|
#define GAM4 0x81 /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
|
||||||
|
#define GAM5 0x82 /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
|
||||||
|
#define GAM6 0x83 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */
|
||||||
|
#define GAM7 0x84 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
|
||||||
|
#define GAM8 0x85 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
|
||||||
|
#define GAM9 0x86 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
|
||||||
|
#define GAM10 0x87 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
|
||||||
|
#define GAM11 0x88 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
|
||||||
|
#define GAM12 0x89 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
|
||||||
|
#define GAM13 0x8A /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
|
||||||
|
#define GAM14 0x8B /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
|
||||||
|
#define GAM15 0x8C /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
|
||||||
|
#define SLOP 0x8D /* Gamma Curve Highest Segment Slope */
|
||||||
|
#define DNSTH 0x8E /* De-noise Threshold */
|
||||||
|
#define EDGE0 0x8F /* Edge Enhancement Strength Control */
|
||||||
|
#define EDGE1 0x90 /* Edge Enhancement Threshold Control */
|
||||||
|
#define DNSOFF 0x91 /* Auto De-noise Threshold Control */
|
||||||
|
#define EDGE2 0x92 /* Edge Enhancement Strength Upper Limit */
|
||||||
|
#define EDGE3 0x93 /* Edge Enhancement Strength Upper Limit */
|
||||||
|
#define MTX1 0x94 /* Matrix Coefficient 1 */
|
||||||
|
#define MTX2 0x95 /* Matrix Coefficient 2 */
|
||||||
|
#define MTX3 0x96 /* Matrix Coefficient 3 */
|
||||||
|
#define MTX4 0x97 /* Matrix Coefficient 4 */
|
||||||
|
#define MTX5 0x98 /* Matrix Coefficient 5 */
|
||||||
|
#define MTX6 0x99 /* Matrix Coefficient 6 */
|
||||||
|
|
||||||
|
#define MTX_CTRL 0x9A /* Matrix Control */
|
||||||
|
#define MTX_CTRL_DBL_EN 0x80 /* Matrix double ON/OFF selection */
|
||||||
|
|
||||||
|
#define BRIGHTNESS 0x9B /* Brightness Control */
|
||||||
|
#define CONTRAST 0x9C /* Contrast Gain */
|
||||||
|
#define UVADJ0 0x9E /* Auto UV Adjust Control 0 */
|
||||||
|
#define UVADJ1 0x9F /* Auto UV Adjust Control 1 */
|
||||||
|
#define SCAL0 0xA0 /* DCW Ratio Control */
|
||||||
|
#define SCAL1 0xA1 /* Horizontal Zoom Out Control */
|
||||||
|
#define SCAL2 0xA2 /* Vertical Zoom Out Control */
|
||||||
|
#define FIFODLYM 0xA3 /* FIFO Manual Mode Delay Control */
|
||||||
|
#define FIFODLYA 0xA4 /* FIFO Auto Mode Delay Control */
|
||||||
|
|
||||||
|
#define SDE 0xA6 /* Special Digital Effect Control */
|
||||||
|
#define SDE_NEGATIVE_EN 0x40 /* Negative image enable */
|
||||||
|
#define SDE_GRAYSCALE_EN 0x20 /* Gray scale image enable */
|
||||||
|
#define SDE_V_FIXED_EN 0x10 /* V fixed value enable */
|
||||||
|
#define SDE_U_FIXED_EN 0x08 /* U fixed value enable */
|
||||||
|
#define SDE_CONT_BRIGHT_EN 0x04 /* Contrast/Brightness enable */
|
||||||
|
#define SDE_SATURATION_EN 0x02 /* Saturation enable */
|
||||||
|
#define SDE_HUE_EN 0x01 /* Hue enable */
|
||||||
|
|
||||||
|
#define USAT 0xA7 /* U Component Saturation Gain */
|
||||||
|
#define VSAT 0xA8 /* V Component Saturation Gain */
|
||||||
|
#define HUECOS 0xA9 /* Cosine value × 0x80 */
|
||||||
|
#define HUESIN 0xAA /* Sine value × 0x80 */
|
||||||
|
#define SIGN_BIT 0xAB /* Sign Bit for Hue and Brightness */
|
||||||
|
|
||||||
|
#define DSPAUTO 0xAC /* DSP Auto Function ON/OFF Control */
|
||||||
|
#define DSPAUTO_AWB_EN 0x80 /* AWB auto threshold control */
|
||||||
|
#define DSPAUTO_DENOISE_EN 0x40 /* De-noise auto threshold control */
|
||||||
|
#define DSPAUTO_EDGE_EN 0x20 /* Sharpness (edge enhancement) auto strength control */
|
||||||
|
#define DSPAUTO_UV_EN 0x10 /* UV adjust auto slope control */
|
||||||
|
#define DSPAUTO_SCAL0_EN 0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */
|
||||||
|
#define DSPAUTO_SCAL1_EN 0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/
|
||||||
|
#define SET_REG(reg, x) (##reg_DEFAULT|x)
|
||||||
|
#endif //__REG_REGS_H__
|
||||||
37
code/lib/tfmicro/CMakeLists.txt
Normal file
37
code/lib/tfmicro/CMakeLists.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
#
|
||||||
|
# This component was generated for the 'hello_world' TF Micro example.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Make sure that the IDF Path environment variable is defined
|
||||||
|
if(NOT DEFINED ENV{IDF_PATH})
|
||||||
|
message(FATAL_ERROR "The IDF_PATH environment variable must point to the location of the ESP-IDF.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS tensorflow/lite/micro/simple_memory_allocator.cc tensorflow/lite/micro/micro_error_reporter.cc tensorflow/lite/micro/memory_helpers.cc tensorflow/lite/micro/test_helpers.cc tensorflow/lite/micro/micro_utils.cc tensorflow/lite/micro/micro_time.cc tensorflow/lite/micro/debug_log.cc tensorflow/lite/micro/micro_string.cc tensorflow/lite/micro/micro_optional_debug_tools.cc tensorflow/lite/micro/micro_interpreter.cc tensorflow/lite/micro/micro_allocator.cc tensorflow/lite/micro/kernels/comparisons.cc tensorflow/lite/micro/kernels/fully_connected.cc tensorflow/lite/micro/kernels/depthwise_conv.cc tensorflow/lite/micro/kernels/logistic.cc tensorflow/lite/micro/kernels/pooling.cc tensorflow/lite/micro/kernels/prelu.cc tensorflow/lite/micro/kernels/concatenation.cc tensorflow/lite/micro/kernels/dequantize.cc tensorflow/lite/micro/kernels/pad.cc tensorflow/lite/micro/kernels/l2norm.cc tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc tensorflow/lite/micro/kernels/activations.cc tensorflow/lite/micro/kernels/ceil.cc tensorflow/lite/micro/kernels/arg_min_max.cc tensorflow/lite/micro/kernels/reduce.cc tensorflow/lite/micro/kernels/unpack.cc tensorflow/lite/micro/kernels/add.cc tensorflow/lite/micro/kernels/split.cc tensorflow/lite/micro/kernels/circular_buffer.cc tensorflow/lite/micro/kernels/softmax.cc tensorflow/lite/micro/kernels/floor.cc tensorflow/lite/micro/kernels/sub.cc tensorflow/lite/micro/kernels/mul.cc tensorflow/lite/micro/kernels/conv.cc tensorflow/lite/micro/kernels/neg.cc tensorflow/lite/micro/kernels/quantize.cc tensorflow/lite/micro/kernels/elementwise.cc tensorflow/lite/micro/kernels/all_ops_resolver.cc tensorflow/lite/micro/kernels/svdf.cc tensorflow/lite/micro/kernels/maximum_minimum.cc tensorflow/lite/micro/kernels/reshape.cc tensorflow/lite/micro/kernels/strided_slice.cc tensorflow/lite/micro/kernels/round.cc tensorflow/lite/micro/kernels/pack.cc tensorflow/lite/micro/kernels/logical.cc tensorflow/lite/micro/memory_planner/linear_memory_planner.cc tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc tensorflow/lite/c/common.c tensorflow/lite/core/api/error_reporter.cc tensorflow/lite/core/api/flatbuffer_conversions.cc tensorflow/lite/core/api/op_resolver.cc tensorflow/lite/core/api/tensor_utils.cc tensorflow/lite/kernels/internal/quantization_util.cc tensorflow/lite/kernels/kernel_util.cc tensorflow/lite/micro/testing/test_utils.cc
|
||||||
|
INCLUDE_DIRS . third_party/gemmlowp third_party/flatbuffers/include third_party/ruy third_party/kissfft)
|
||||||
|
|
||||||
|
# Reduce the level of paranoia to be able to compile TF sources
|
||||||
|
target_compile_options(${COMPONENT_LIB} PRIVATE
|
||||||
|
-Wno-maybe-uninitialized
|
||||||
|
-Wno-missing-field-initializers
|
||||||
|
-Wno-type-limits)
|
||||||
|
|
||||||
|
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c11 -DTF_LITE_STATIC_MEMORY -O3 -Wno-nonnull -Wno-nonnull -Wno-nonnull -Wno-nonnull)
|
||||||
|
target_compile_options(${COMPONENT_LIB} PRIVATE $<$<COMPILE_LANGUAGE:CXX>: -std=c++11 -DTF_LITE_STATIC_MEMORY -O3 -Wno-return-type -Wno-strict-aliasing -Wno-ignored-qualifiers -Wno-return-type -Wno-strict-aliasing -Wno-ignored-qualifiers -Wno-return-type -Wno-strict-aliasing -Wno-return-type -Wno-strict-aliasing >)
|
||||||
|
target_link_libraries(${COMPONENT_LIB} PRIVATE -lm)
|
||||||
900
code/lib/tfmicro/fixedpoint/fixedpoint.h
Normal file
900
code/lib/tfmicro/fixedpoint/fixedpoint.h
Normal file
@@ -0,0 +1,900 @@
|
|||||||
|
// Copyright 2015 The Gemmlowp Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// fixedpoint.h: fixed-point arithmetic, with basic operations and
|
||||||
|
// a few math functions such as tanh.
|
||||||
|
|
||||||
|
#ifndef GEMMLOWP_INTERNAL_FIXEDPOINT_H_
|
||||||
|
#define GEMMLOWP_INTERNAL_FIXEDPOINT_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "../internal/detect_platform.h"
|
||||||
|
|
||||||
|
namespace gemmlowp {
|
||||||
|
|
||||||
|
// Part 1: Low-level integer-arithmetic primitives.
|
||||||
|
// The implementations here are generic implementations valid for
|
||||||
|
// scalar types (e.g. std::int32_t). Architecture-specific SIMD types
|
||||||
|
// (e.g. NEON int32x4_t) may be supported by providing
|
||||||
|
// specializations for them in separate files.
|
||||||
|
//
|
||||||
|
// The purpose of these primitives is two-fold:
|
||||||
|
// - They will be used to implement higher-level fixed-point
|
||||||
|
// abstractions, namely the FixedPoint class and its arithmetic
|
||||||
|
// operators.
|
||||||
|
// - They will be directly used to implement some more involved
|
||||||
|
// fixed-point computations, e.g. the fixed-point implementation
|
||||||
|
// of math functions such as tanh.
|
||||||
|
|
||||||
|
// Some compile-time traits around raw types to handle SIMD aspects:
|
||||||
|
// number of lanes, underlying scalar type.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
struct FixedPointRawTypeTraits {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct FixedPointRawTypeTraits<std::int32_t> {
|
||||||
|
typedef std::int32_t ScalarRawType;
|
||||||
|
static constexpr int kLanes = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct FixedPointRawTypeTraits<std::int16_t> {
|
||||||
|
typedef std::int16_t ScalarRawType;
|
||||||
|
static constexpr int kLanes = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a SIMD value duplicating a scalar value across all lanes.
|
||||||
|
template <typename tRawType>
|
||||||
|
tRawType Dup(typename FixedPointRawTypeTraits<tRawType>::ScalarRawType x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain bit-wise AND
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType BitAnd(tIntegerType a, tIntegerType b) {
|
||||||
|
return a & b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain bit-wise OR
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType BitOr(tIntegerType a, tIntegerType b) {
|
||||||
|
return a | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain bit-wise XOR
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType BitXor(tIntegerType a, tIntegerType b) {
|
||||||
|
return a ^ b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain bit-wise NOT
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType BitNot(tIntegerType a) {
|
||||||
|
return ~a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer addition. Not saturating. Overflow is undefined behavior.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType Add(tIntegerType a, tIntegerType b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer subtraction. Not saturating. Overflow is undefined behavior.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType Mul(tIntegerType a, tIntegerType b) {
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType Sub(tIntegerType a, tIntegerType b) {
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer unary negative. Not saturating. Overflow is undefined behavior.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType Neg(tIntegerType a) {
|
||||||
|
return -a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer arithmetic left-shift, equivalent to multiplying with a power of two.
|
||||||
|
// Negative values are OK. In case of overflow, no Undefined
|
||||||
|
// Behavior, but the results are implementation-defined (in practice,
|
||||||
|
// they currently are saturated, but we make no commitment to that). The idea
|
||||||
|
// is that the caller will want to implement the overflowing cases with
|
||||||
|
// saturation with compare-and-mask, so we don't care about the results
|
||||||
|
// in the overflow case, we just want to avoid undefined behavior.
|
||||||
|
//
|
||||||
|
// tIntegerType may be int32 or any narrower signed type.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType ShiftLeft(tIntegerType a, int offset) {
|
||||||
|
const std::int64_t wide_a = static_cast<std::int64_t>(a);
|
||||||
|
const std::int64_t wide_shifted = wide_a * (1 << offset);
|
||||||
|
const auto min = std::numeric_limits<tIntegerType>::min();
|
||||||
|
const auto max = std::numeric_limits<tIntegerType>::max();
|
||||||
|
return wide_shifted < min
|
||||||
|
? min
|
||||||
|
: wide_shifted > max ? max
|
||||||
|
: static_cast<tIntegerType>(wide_shifted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer arithmetic right-shift. Not rounding.
|
||||||
|
// Relying on implementation-defined, but in-practice-consistent,
|
||||||
|
// C++ compiler behavior.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType ShiftRight(tIntegerType a, int offset) {
|
||||||
|
return a >> offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each bit of the result is set to the corresponding bit of either then_val or
|
||||||
|
// else_val depending on whether the corresponding bit of if_mask is set.
|
||||||
|
// Equivalent to the VBSL instruction in ARM NEON.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType SelectUsingMask(tIntegerType if_mask, tIntegerType then_val,
|
||||||
|
tIntegerType else_val) {
|
||||||
|
return BitXor(BitAnd(if_mask, then_val), BitAnd(BitNot(if_mask), else_val));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each input scalar, the corresponding bits of the result are set if the
|
||||||
|
// input scalar is non-zero.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType MaskIfNonZero(tIntegerType a) {
|
||||||
|
static constexpr tIntegerType zero = 0;
|
||||||
|
return a ? BitNot(zero) : zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each input scalar, the corresponding bits of the result are set if the
|
||||||
|
// input scalar is zero.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType MaskIfZero(tIntegerType a) {
|
||||||
|
return MaskIfNonZero<tIntegerType>(!a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each pair of input scalars, the corresponding bits of the result are
|
||||||
|
// set if the input scalars are equal.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType MaskIfEqual(tIntegerType a, tIntegerType b) {
|
||||||
|
return MaskIfNonZero<tIntegerType>(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each pair of input scalars, the corresponding bits of the result are
|
||||||
|
// set if the input scalars are not equal.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType MaskIfNotEqual(tIntegerType a, tIntegerType b) {
|
||||||
|
return MaskIfNonZero<tIntegerType>(a != b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each pair of input scalars, the corresponding bits of the result are
|
||||||
|
// set if the input scalars a, b satisfy a > b.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType MaskIfGreaterThan(tIntegerType a, tIntegerType b) {
|
||||||
|
return MaskIfNonZero<tIntegerType>(a > b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each pair of input scalars, the corresponding bits of the result are
|
||||||
|
// set if the input scalars a, b satisfy a >= b.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType MaskIfGreaterThanOrEqual(tIntegerType a, tIntegerType b) {
|
||||||
|
return MaskIfNonZero<tIntegerType>(a >= b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each pair of input scalars, the corresponding bits of the result are
|
||||||
|
// set if the input scalars a, b satisfy a < b.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType MaskIfLessThan(tIntegerType a, tIntegerType b) {
|
||||||
|
return MaskIfNonZero<tIntegerType>(a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each pair of input scalars, the corresponding bits of the result are
|
||||||
|
// set if the input scalars a, b satisfy a <= b.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
tIntegerType MaskIfLessThanOrEqual(tIntegerType a, tIntegerType b) {
|
||||||
|
return MaskIfNonZero<tIntegerType>(a <= b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if all of the input scalars are nonzero.
|
||||||
|
// This function may currently assume that each of the input scalars has either
|
||||||
|
// all or none of its bits set. Otherwise, its behavior is currently undefined.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
bool All(tIntegerType a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if any of the input scalars are nonzero.
|
||||||
|
// This function may currently assume that each of the input scalars has either
|
||||||
|
// all or none of its bits set. Otherwise, its behavior is currently undefined.
|
||||||
|
template <typename tIntegerType>
|
||||||
|
bool Any(tIntegerType a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns (a+b)/2, rounded to the nearest integer.
|
||||||
|
// Equivalent to VRHADD in the ARM NEON instruction set.
|
||||||
|
template <typename IntegerType>
|
||||||
|
IntegerType RoundingHalfSum(IntegerType a, IntegerType b) {
|
||||||
|
static_assert(std::is_same<IntegerType, void>::value, "unimplemented");
|
||||||
|
(void)b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline std::int32_t RoundingHalfSum(std::int32_t a, std::int32_t b) {
|
||||||
|
std::int64_t a64 = a;
|
||||||
|
std::int64_t b64 = b;
|
||||||
|
std::int64_t sum = a64 + b64;
|
||||||
|
std::int64_t sign = sum >= 0 ? 1 : -1;
|
||||||
|
return static_cast<std::int32_t>((sum + sign) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline std::int16_t RoundingHalfSum(std::int16_t a, std::int16_t b) {
|
||||||
|
std::int32_t a32 = a;
|
||||||
|
std::int32_t b32 = b;
|
||||||
|
std::int32_t sum = a32 + b32;
|
||||||
|
std::int32_t sign = sum >= 0 ? 1 : -1;
|
||||||
|
return static_cast<std::int16_t>((sum + sign) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntegerType>
|
||||||
|
IntegerType SaturatingAdd(IntegerType a, IntegerType b) {
|
||||||
|
static_assert(std::is_same<IntegerType, void>::value, "unimplemented");
|
||||||
|
(void)b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// So far this is only needed for int16.
|
||||||
|
template <>
|
||||||
|
inline std::int16_t SaturatingAdd(std::int16_t a, std::int16_t b) {
|
||||||
|
std::int32_t a32 = a;
|
||||||
|
std::int32_t b32 = b;
|
||||||
|
std::int32_t sum = a32 + b32;
|
||||||
|
return static_cast<std::int16_t>(
|
||||||
|
std::min(static_cast<std::int32_t>(32767),
|
||||||
|
std::max(static_cast<std::int32_t>(-32768), sum)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a+b, saturating if the integers are 16bit or narrower,
|
||||||
|
// otherwise just a plain addition.
|
||||||
|
template <typename IntegerType, bool Is16Bit>
|
||||||
|
struct AddSaturatingIf16BitImpl {
|
||||||
|
static IntegerType Run(IntegerType a, IntegerType b) { return Add(a, b); }
|
||||||
|
};
|
||||||
|
template <typename IntegerType>
|
||||||
|
struct AddSaturatingIf16BitImpl<IntegerType, true> {
|
||||||
|
static IntegerType Run(IntegerType a, IntegerType b) {
|
||||||
|
return SaturatingAdd(a, b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <typename IntegerType>
|
||||||
|
IntegerType AddSaturatingIf16Bit(IntegerType a, IntegerType b) {
|
||||||
|
using ScalarType =
|
||||||
|
typename FixedPointRawTypeTraits<IntegerType>::ScalarRawType;
|
||||||
|
return AddSaturatingIf16BitImpl<IntegerType, sizeof(ScalarType) == 2>::Run(a,
|
||||||
|
b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the integer that represents the product of two fixed-point
|
||||||
|
// numbers, interpreting all integers as fixed-point values in the
|
||||||
|
// interval [-1, 1), rounding to the nearest value, and saturating
|
||||||
|
// -1 * -1 to the maximum value (since 1 is not in the half-open
|
||||||
|
// interval [-1, 1)).
|
||||||
|
//
|
||||||
|
// [The explanation below specializes to std::int32_t for example purpose.]
|
||||||
|
//
|
||||||
|
// The mapping between IntegerType and the interval [-1, 1) is unique and
|
||||||
|
// implied by IntegerType, which is assumed to be signed. For example,
|
||||||
|
// for IntegerType==std::int32_t, the mapping is
|
||||||
|
// real_value = integer_value / 2^31.
|
||||||
|
// So in this case, and leaving aside rounding and saturating, this
|
||||||
|
// function computes ((a / 2^31) * (b / 2^31)) * 2^31, which simplifies to
|
||||||
|
// (a * b) / 2^31.
|
||||||
|
//
|
||||||
|
// The 'doubling' part in the name of this function comes from the fact that
|
||||||
|
// this operation is very close to a "multiply-high" operation, keeping only
|
||||||
|
// the top half bits, except that that would be effectively computing
|
||||||
|
// (a * b) / 2^32,
|
||||||
|
// so here we are computing 2x that, since
|
||||||
|
// 1/2^31 = 2 * 1/2^32.
|
||||||
|
// The idea is to use all of the available 32 bits in the destination int32
|
||||||
|
// value.
|
||||||
|
//
|
||||||
|
// [End of the explanation specializing to int32.]
|
||||||
|
//
|
||||||
|
// This is equivalent to the VQRDMULH instruction in ARM NEON.
|
||||||
|
template <typename IntegerType>
|
||||||
|
IntegerType SaturatingRoundingDoublingHighMul(IntegerType a, IntegerType b) {
|
||||||
|
static_assert(std::is_same<IntegerType, void>::value, "unimplemented");
|
||||||
|
(void)b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function implements the same computation as the ARMv7 NEON VQRDMULH
|
||||||
|
// instruction.
|
||||||
|
template <>
|
||||||
|
inline std::int32_t SaturatingRoundingDoublingHighMul(std::int32_t a,
|
||||||
|
std::int32_t b) {
|
||||||
|
bool overflow = a == b && a == std::numeric_limits<std::int32_t>::min();
|
||||||
|
std::int64_t a_64(a);
|
||||||
|
std::int64_t b_64(b);
|
||||||
|
std::int64_t ab_64 = a_64 * b_64;
|
||||||
|
std::int32_t nudge = ab_64 >= 0 ? (1 << 30) : (1 - (1 << 30));
|
||||||
|
std::int32_t ab_x2_high32 =
|
||||||
|
static_cast<std::int32_t>((ab_64 + nudge) / (1ll << 31));
|
||||||
|
return overflow ? std::numeric_limits<std::int32_t>::max() : ab_x2_high32;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline std::int16_t SaturatingRoundingDoublingHighMul(std::int16_t a,
|
||||||
|
std::int16_t b) {
|
||||||
|
bool overflow = a == b && a == std::numeric_limits<std::int16_t>::min();
|
||||||
|
std::int32_t a_32(a);
|
||||||
|
std::int32_t b_32(b);
|
||||||
|
std::int32_t ab_32 = a_32 * b_32;
|
||||||
|
std::int16_t nudge = ab_32 >= 0 ? (1 << 14) : (1 - (1 << 14));
|
||||||
|
std::int16_t ab_x2_high16 =
|
||||||
|
static_cast<std::int16_t>((ab_32 + nudge) / (1 << 15));
|
||||||
|
return overflow ? std::numeric_limits<std::int16_t>::max() : ab_x2_high16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correctly-rounded-to-nearest division by a power-of-two.
|
||||||
|
// Also known as a rounding arithmetic right shift.
|
||||||
|
template <typename IntegerType>
|
||||||
|
inline IntegerType RoundingDivideByPOT(IntegerType x, int exponent) {
|
||||||
|
assert(exponent >= 0);
|
||||||
|
assert(exponent <= 31);
|
||||||
|
const IntegerType mask = Dup<IntegerType>((1ll << exponent) - 1);
|
||||||
|
const IntegerType zero = Dup<IntegerType>(0);
|
||||||
|
const IntegerType one = Dup<IntegerType>(1);
|
||||||
|
const IntegerType remainder = BitAnd(x, mask);
|
||||||
|
const IntegerType threshold =
|
||||||
|
Add(ShiftRight(mask, 1), BitAnd(MaskIfLessThan(x, zero), one));
|
||||||
|
return Add(ShiftRight(x, exponent),
|
||||||
|
BitAnd(MaskIfGreaterThan(remainder, threshold), one));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the product of a run-time integer value by a compile-time power
|
||||||
|
// of two, with either a positive exponent (equivalent to an arithmetic
|
||||||
|
// left shift, saturating) or a negative exponent (equivalent to an arithmetic
|
||||||
|
// right shift, rounding to nearest).
|
||||||
|
template <int Exponent, typename IntegerType,
|
||||||
|
int ExponentSign = (Exponent > 0 ? 1 : Exponent < 0 ? -1 : 0)>
|
||||||
|
struct ImplSaturatingRoundingMultiplyByPOT {};
|
||||||
|
|
||||||
|
template <int Exponent, typename IntegerType>
|
||||||
|
struct ImplSaturatingRoundingMultiplyByPOT<Exponent, IntegerType, 0> {
|
||||||
|
static IntegerType eval(IntegerType x) { return x; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int Exponent, typename IntegerType>
|
||||||
|
struct ImplSaturatingRoundingMultiplyByPOT<Exponent, IntegerType, 1> {
|
||||||
|
static IntegerType eval(IntegerType x) {
|
||||||
|
using ScalarIntegerType =
|
||||||
|
typename FixedPointRawTypeTraits<IntegerType>::ScalarRawType;
|
||||||
|
const IntegerType min =
|
||||||
|
Dup<IntegerType>(std::numeric_limits<ScalarIntegerType>::min());
|
||||||
|
const IntegerType max =
|
||||||
|
Dup<IntegerType>(std::numeric_limits<ScalarIntegerType>::max());
|
||||||
|
const int ScalarIntegerTypeBits = 8 * sizeof(ScalarIntegerType);
|
||||||
|
|
||||||
|
const std::int32_t threshold =
|
||||||
|
((1 << (ScalarIntegerTypeBits - 1 - Exponent)) - 1);
|
||||||
|
const IntegerType positive_mask =
|
||||||
|
MaskIfGreaterThan(x, Dup<IntegerType>(threshold));
|
||||||
|
const IntegerType negative_mask =
|
||||||
|
MaskIfLessThan(x, Dup<IntegerType>(-threshold));
|
||||||
|
|
||||||
|
IntegerType result = ShiftLeft(x, Exponent);
|
||||||
|
result = SelectUsingMask(positive_mask, max, result);
|
||||||
|
result = SelectUsingMask(negative_mask, min, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int Exponent, typename IntegerType>
|
||||||
|
struct ImplSaturatingRoundingMultiplyByPOT<Exponent, IntegerType, -1> {
|
||||||
|
static IntegerType eval(IntegerType x) {
|
||||||
|
return RoundingDivideByPOT<IntegerType>(x, -Exponent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int Exponent, typename IntegerType>
|
||||||
|
IntegerType SaturatingRoundingMultiplyByPOT(IntegerType x) {
|
||||||
|
return ImplSaturatingRoundingMultiplyByPOT<Exponent, IntegerType>::eval(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part 2: the FixedPoint class.
|
||||||
|
|
||||||
|
// A FixedPoint object represents a fixed-point value stored in the underlying
|
||||||
|
// integer type tRawType, if tRawType is a plain scalar integer type.
|
||||||
|
// Alternatively, tRawType may be a SIMD type (e.g. NEON int32x4_t) in which
|
||||||
|
// case a FixedPoint object represents a corresponding SIMD vector of fixed
|
||||||
|
// point values.
|
||||||
|
//
|
||||||
|
// tIntegerBits describes the range of the fixed-point format: if
|
||||||
|
// tIntegerBits == m then the range of representable values is the half-open
|
||||||
|
// interval [-2^m; 2^m) where the open boundary on the right side means that
|
||||||
|
// 2^m is not representable (how close the maximum representable value is to
|
||||||
|
// it, depends on bit-depth of tRawType).
|
||||||
|
//
|
||||||
|
// In "Q format notation",
|
||||||
|
// https://en.wikipedia.org/wiki/Q_(number_format)
|
||||||
|
// we are describing the format
|
||||||
|
// Qm.n
|
||||||
|
// where
|
||||||
|
// m = tIntegerBits
|
||||||
|
// and
|
||||||
|
// n = NumberOfBits(tRawType) - (m + 1)
|
||||||
|
// Note that the (m + 1) in the above line is because we adopt the convention
|
||||||
|
// that we count the integer bits exclusively of the sign bit; so (m + 1) is
|
||||||
|
// the total number of integer bits inclusive of the sign bit.
|
||||||
|
//
|
||||||
|
// Accordingly, the number of integral representable values in our range
|
||||||
|
// [-2^m ; 2^m)
|
||||||
|
// is equal to 2^(m+1).
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
class FixedPoint {
|
||||||
|
public:
|
||||||
|
typedef tRawType RawType;
|
||||||
|
|
||||||
|
typedef FixedPointRawTypeTraits<RawType> RawTypeTraits;
|
||||||
|
typedef typename RawTypeTraits::ScalarRawType ScalarRawType;
|
||||||
|
|
||||||
|
static constexpr int kTotalBits = 8 * sizeof(ScalarRawType);
|
||||||
|
static constexpr int kIntegerBits = tIntegerBits;
|
||||||
|
static constexpr int kFractionalBits = kTotalBits - 1 - kIntegerBits;
|
||||||
|
static_assert(kIntegerBits >= 0 && kIntegerBits < kTotalBits,
|
||||||
|
"bad IntegerBits");
|
||||||
|
|
||||||
|
typedef FixedPoint<ScalarRawType, kIntegerBits> ScalarFixedPointType;
|
||||||
|
|
||||||
|
static const ScalarRawType ScalarRawMin() {
|
||||||
|
return std::numeric_limits<ScalarRawType>::min();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const ScalarRawType ScalarRawMax() {
|
||||||
|
return std::numeric_limits<ScalarRawType>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const ScalarRawType RawMin() {
|
||||||
|
return VectorFromScalar(ScalarRawMin());
|
||||||
|
}
|
||||||
|
|
||||||
|
static const ScalarRawType RawMax() {
|
||||||
|
return VectorFromScalar(ScalarRawMax());
|
||||||
|
}
|
||||||
|
|
||||||
|
static FixedPoint FromRaw(RawType x) {
|
||||||
|
FixedPoint retval;
|
||||||
|
retval.raw() = x;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FixedPoint FromScalarRaw(ScalarRawType x) {
|
||||||
|
FixedPoint retval;
|
||||||
|
retval.raw() = Dup<RawType>(x);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FixedPoint FromScalarFixedPoint(ScalarFixedPointType x) {
|
||||||
|
return FromScalarRaw(x.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int Exponent>
|
||||||
|
static FixedPoint ConstantPOT() {
|
||||||
|
static constexpr int kOffset = kFractionalBits + Exponent;
|
||||||
|
static_assert(
|
||||||
|
kOffset < 31,
|
||||||
|
"Constant not exactly representable in this fixed-point format");
|
||||||
|
return FromScalarRaw(ScalarRawType(1) << kOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FixedPoint Zero() { return FromScalarRaw(0); }
|
||||||
|
|
||||||
|
static FixedPoint One() {
|
||||||
|
return FromScalarRaw(
|
||||||
|
kIntegerBits == 0
|
||||||
|
? ScalarRawMax()
|
||||||
|
: (ScalarRawType(1) << (kIntegerBits == 0 ? 0 : kFractionalBits)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static FixedPoint FromDouble(double x) {
|
||||||
|
const double min_bound = static_cast<double>(ScalarRawMin());
|
||||||
|
const double max_bound = static_cast<double>(ScalarRawMax());
|
||||||
|
return FromScalarRaw(static_cast<ScalarRawType>(std::min(
|
||||||
|
std::max(round(x * static_cast<double>(1ll << kFractionalBits)),
|
||||||
|
min_bound),
|
||||||
|
max_bound)));
|
||||||
|
}
|
||||||
|
|
||||||
|
RawType raw() const { return i_; }
|
||||||
|
RawType& raw() { return i_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RawType i_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Part 3: implementation of arithmetic operators for the
|
||||||
|
// FixedPoint class, and a few related functions.
|
||||||
|
|
||||||
|
// A FixedPoint multiplication is just a
|
||||||
|
// SaturatingRoundingDoublingHighMul operation on the underlying
|
||||||
|
// raw integer values. The IntegerBits simply add up, as is obvious
|
||||||
|
// from the fact that the range is [-2^IntegerBits, 2^IntegerBits).
|
||||||
|
template <typename tRawType, int tIntegerBits_a, int tIntegerBits_b>
|
||||||
|
FixedPoint<tRawType, tIntegerBits_a + tIntegerBits_b> operator*(
|
||||||
|
FixedPoint<tRawType, tIntegerBits_a> a,
|
||||||
|
FixedPoint<tRawType, tIntegerBits_b> b) {
|
||||||
|
FixedPoint<tRawType, tIntegerBits_a + tIntegerBits_b> c;
|
||||||
|
c.raw() = SaturatingRoundingDoublingHighMul(a.raw(), b.raw());
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tweaking IntegerBits gives exact multiplication by a power of two.
|
||||||
|
template <int tExponent, typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, tExponent + tIntegerBits> ExactMulByPot(
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a) {
|
||||||
|
FixedPoint<tRawType, tExponent + tIntegerBits> c;
|
||||||
|
c.raw() = a.raw();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we want to leave IntegerBits fixed, then multiplication
|
||||||
|
// by a power of two has to be saturating/rounding, not exact anymore.
|
||||||
|
template <int tExponent, typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, tIntegerBits> SaturatingRoundingMultiplyByPOT(
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a) {
|
||||||
|
return FixedPoint<tRawType, tIntegerBits>::FromRaw(
|
||||||
|
SaturatingRoundingMultiplyByPOT<tExponent>(a.raw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic arithmetic operators.
|
||||||
|
|
||||||
|
#define MAKE_FIXEDPOINT_UNARY_FUNC(FuncName, ImplFuncName) \
|
||||||
|
template <typename tRawType, int tIntegerBits> \
|
||||||
|
FixedPoint<tRawType, tIntegerBits> FuncName( \
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a) { \
|
||||||
|
return FixedPoint<tRawType, tIntegerBits>::FromRaw(ImplFuncName(a.raw())); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAKE_FIXEDPOINT_BINARY_FUNC(FuncName, ImplFuncName) \
|
||||||
|
template <typename tRawType, int tIntegerBits> \
|
||||||
|
FixedPoint<tRawType, tIntegerBits> FuncName( \
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a, \
|
||||||
|
FixedPoint<tRawType, tIntegerBits> b) { \
|
||||||
|
return FixedPoint<tRawType, tIntegerBits>::FromRaw( \
|
||||||
|
ImplFuncName(a.raw(), b.raw())); \
|
||||||
|
}
|
||||||
|
|
||||||
|
MAKE_FIXEDPOINT_UNARY_FUNC(operator-, Neg)
|
||||||
|
MAKE_FIXEDPOINT_UNARY_FUNC(operator~, BitNot)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC(operator+, Add)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC(operator-, Sub)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC(operator&, BitAnd)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC(operator^, BitXor)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC(operator|, BitOr)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC(RoundingHalfSum, RoundingHalfSum)
|
||||||
|
|
||||||
|
#undef MAKE_FIXEDPOINT_UNARY_FUNC
|
||||||
|
#undef MAKE_FIXEDPOINT_BINARY_FUNC
|
||||||
|
|
||||||
|
#define MAKE_FIXEDPOINT_UNARY_FUNC_RETURNING_RAW(FuncName) \
|
||||||
|
template <typename tRawType, int tIntegerBits> \
|
||||||
|
tRawType FuncName(FixedPoint<tRawType, tIntegerBits> a) { \
|
||||||
|
return FuncName(a.raw()); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAKE_FIXEDPOINT_BINARY_FUNC_RETURNING_RAW(FuncName) \
|
||||||
|
template <typename tRawType, int tIntegerBits> \
|
||||||
|
tRawType FuncName(FixedPoint<tRawType, tIntegerBits> a, \
|
||||||
|
FixedPoint<tRawType, tIntegerBits> b) { \
|
||||||
|
return FuncName(a.raw(), b.raw()); \
|
||||||
|
}
|
||||||
|
|
||||||
|
MAKE_FIXEDPOINT_UNARY_FUNC_RETURNING_RAW(MaskIfZero)
|
||||||
|
MAKE_FIXEDPOINT_UNARY_FUNC_RETURNING_RAW(MaskIfNonZero)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC_RETURNING_RAW(MaskIfEqual)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC_RETURNING_RAW(MaskIfNotEqual)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC_RETURNING_RAW(MaskIfGreaterThan)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC_RETURNING_RAW(MaskIfGreaterThanOrEqual)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC_RETURNING_RAW(MaskIfLessThan)
|
||||||
|
MAKE_FIXEDPOINT_BINARY_FUNC_RETURNING_RAW(MaskIfLessThanOrEqual)
|
||||||
|
|
||||||
|
#undef MAKE_FIXEDPOINT_UNARY_FUNC_RETURNING_RAW
|
||||||
|
#undef MAKE_FIXEDPOINT_BINARY_FUNC_RETURNING_RAW
|
||||||
|
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, tIntegerBits> SelectUsingMask(
|
||||||
|
tRawType if_mask, FixedPoint<tRawType, tIntegerBits> then_val,
|
||||||
|
FixedPoint<tRawType, tIntegerBits> else_val) {
|
||||||
|
return FixedPoint<tRawType, tIntegerBits>::FromRaw(
|
||||||
|
SelectUsingMask(if_mask, then_val.raw(), else_val.raw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
bool operator==(FixedPoint<tRawType, tIntegerBits> a,
|
||||||
|
FixedPoint<tRawType, tIntegerBits> b) {
|
||||||
|
return All(MaskIfEqual(a.raw(), b.raw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
bool operator!=(FixedPoint<tRawType, tIntegerBits> a,
|
||||||
|
FixedPoint<tRawType, tIntegerBits> b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, tIntegerBits> SaturatingAdd(
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a,
|
||||||
|
FixedPoint<tRawType, tIntegerBits> b) {
|
||||||
|
return FixedPoint<tRawType, tIntegerBits>::FromRaw(
|
||||||
|
SaturatingAdd(a.raw(), b.raw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, tIntegerBits> AddSaturatingIf16Bit(
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a,
|
||||||
|
FixedPoint<tRawType, tIntegerBits> b) {
|
||||||
|
return FixedPoint<tRawType, tIntegerBits>::FromRaw(
|
||||||
|
AddSaturatingIf16Bit(a.raw(), b.raw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversion to floating-point.
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
double ToDouble(FixedPoint<tRawType, tIntegerBits> x) {
|
||||||
|
static_assert(FixedPointRawTypeTraits<tRawType>::kLanes == 1,
|
||||||
|
"not applicable to SIMD types");
|
||||||
|
typedef FixedPoint<tRawType, tIntegerBits> F;
|
||||||
|
return x.raw() / static_cast<double>(1ll << F::kFractionalBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rescale changes the number of IntegerBits and updates the underlying
|
||||||
|
// raw integer value accordingly.
|
||||||
|
template <int tIntegerBitsDst, typename tRawType, int tIntegerBitsSrc>
|
||||||
|
FixedPoint<tRawType, tIntegerBitsDst> Rescale(
|
||||||
|
FixedPoint<tRawType, tIntegerBitsSrc> x) {
|
||||||
|
static constexpr int kExponent = tIntegerBitsSrc - tIntegerBitsDst;
|
||||||
|
FixedPoint<tRawType, tIntegerBitsDst> result;
|
||||||
|
result.raw() = SaturatingRoundingMultiplyByPOT<kExponent>(x.raw());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckedFixedPointConstant allows to specify fixed-point constants
|
||||||
|
// initialized as real numbers, in a way that does not compile floating-point
|
||||||
|
// arithmetic in production code, yet still checks agreement with the
|
||||||
|
// floating-point expressions when asserts are enabled.
|
||||||
|
//
|
||||||
|
// The raw integer value provided is always a int32, encoding a 32-bit
|
||||||
|
// fixed-point value, regardless of the actual Scalar type. This allows
|
||||||
|
// writing generic code that applies just as well to the 32-bit and 16-bit
|
||||||
|
// cases. In the 16-bit case, the raw integer value is internally
|
||||||
|
// rounding-shifted by 16 bits to the right.
|
||||||
|
template <typename FixedPointType>
|
||||||
|
inline typename FixedPointType::ScalarRawType RescaleConstantInitializer(
|
||||||
|
std::int32_t int32_value) {
|
||||||
|
typedef typename FixedPointType::ScalarRawType ScalarRawType;
|
||||||
|
static constexpr int ScalarTypeBits = 8 * sizeof(ScalarRawType);
|
||||||
|
return static_cast<ScalarRawType>(
|
||||||
|
RoundingDivideByPOT<std::int32_t>(int32_value, 32 - ScalarTypeBits));
|
||||||
|
}
|
||||||
|
#ifdef GEMMLOWP_ENABLE_FIXEDPOINT_CONSTANTS_CHECKS
|
||||||
|
template <typename FixedPointType>
|
||||||
|
FixedPointType CheckedFixedPointConstant(std::int32_t raw_value,
|
||||||
|
double double_value) {
|
||||||
|
const FixedPointType result = FixedPointType::FromScalarRaw(raw_value);
|
||||||
|
assert(result == FixedPointType::FromDouble(double_value));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#define GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(FixedPointType, \
|
||||||
|
ScalarRawInt32Value, DoubleValue) \
|
||||||
|
(gemmlowp::CheckedFixedPointConstant<FixedPointType>( \
|
||||||
|
gemmlowp::RescaleConstantInitializer<FixedPointType>( \
|
||||||
|
ScalarRawInt32Value), \
|
||||||
|
DoubleValue))
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(FixedPointType, \
|
||||||
|
ScalarRawInt32Value, DoubleValue) \
|
||||||
|
(FixedPointType::FromScalarRaw( \
|
||||||
|
gemmlowp::RescaleConstantInitializer<FixedPointType>( \
|
||||||
|
ScalarRawInt32Value)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Implementation of exponential function.
|
||||||
|
|
||||||
|
// Returns exp(x) for x in [-1/4, 0).
|
||||||
|
template <typename tRawType>
|
||||||
|
FixedPoint<tRawType, 0> exp_on_interval_between_negative_one_quarter_and_0_excl(
|
||||||
|
FixedPoint<tRawType, 0> a) {
|
||||||
|
typedef FixedPoint<tRawType, 0> F;
|
||||||
|
const F constant_term =
|
||||||
|
GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(F, 1895147668, std::exp(-1.0 / 8.0));
|
||||||
|
const F constant_1_over_3 =
|
||||||
|
GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(F, 715827883, 1.0 / 3.0);
|
||||||
|
// We're evaluating a Taylor expansion around -1/8, so we do the change of
|
||||||
|
// variable: x = a + 1/8.
|
||||||
|
// In fixed-point with 0 integer bits, 1/8 is represented by 1 << 28.
|
||||||
|
F x = a + F::template ConstantPOT<-3>();
|
||||||
|
F x2 = x * x;
|
||||||
|
F x3 = x2 * x;
|
||||||
|
F x4 = x2 * x2;
|
||||||
|
F x4_over_4 = SaturatingRoundingMultiplyByPOT<-2>(x4);
|
||||||
|
F x4_over_24_plus_x3_over_6_plus_x2_over_2 =
|
||||||
|
SaturatingRoundingMultiplyByPOT<-1>(
|
||||||
|
((x4_over_4 + x3) * constant_1_over_3) + x2);
|
||||||
|
return AddSaturatingIf16Bit(
|
||||||
|
constant_term,
|
||||||
|
constant_term * (x + x4_over_24_plus_x3_over_6_plus_x2_over_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns exp(x) for x < 0.
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, 0> exp_on_negative_values(
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a) {
|
||||||
|
typedef FixedPoint<tRawType, tIntegerBits> InputF;
|
||||||
|
typedef FixedPoint<tRawType, 0> ResultF;
|
||||||
|
static constexpr int kFractionalBits = InputF::kFractionalBits;
|
||||||
|
static constexpr int kIntegerBits = InputF::kIntegerBits;
|
||||||
|
const InputF kOneQuarter = InputF::template ConstantPOT<-2>();
|
||||||
|
InputF mask = kOneQuarter - InputF::FromScalarRaw(1);
|
||||||
|
InputF a_mod_quarter_minus_one_quarter = (a & mask) - kOneQuarter;
|
||||||
|
ResultF result = exp_on_interval_between_negative_one_quarter_and_0_excl(
|
||||||
|
Rescale<0>(a_mod_quarter_minus_one_quarter));
|
||||||
|
tRawType remainder = (a_mod_quarter_minus_one_quarter - a).raw();
|
||||||
|
|
||||||
|
#define GEMMLOWP_EXP_BARREL_SHIFTER(Exponent, FixedPointMultiplier) \
|
||||||
|
if (kIntegerBits > Exponent) { \
|
||||||
|
const ResultF kMultiplier = GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT( \
|
||||||
|
ResultF, FixedPointMultiplier, std::exp(-std::pow(2.0, Exponent))); \
|
||||||
|
static constexpr int kShiftAmount = \
|
||||||
|
kIntegerBits > Exponent ? kFractionalBits + Exponent : 0; \
|
||||||
|
result = SelectUsingMask( \
|
||||||
|
MaskIfNonZero(BitAnd(remainder, Dup<tRawType>(1 << kShiftAmount))), \
|
||||||
|
result * kMultiplier, result); \
|
||||||
|
}
|
||||||
|
|
||||||
|
GEMMLOWP_EXP_BARREL_SHIFTER(-2, 1672461947);
|
||||||
|
GEMMLOWP_EXP_BARREL_SHIFTER(-1, 1302514674);
|
||||||
|
GEMMLOWP_EXP_BARREL_SHIFTER(+0, 790015084);
|
||||||
|
GEMMLOWP_EXP_BARREL_SHIFTER(+1, 290630308);
|
||||||
|
GEMMLOWP_EXP_BARREL_SHIFTER(+2, 39332535);
|
||||||
|
GEMMLOWP_EXP_BARREL_SHIFTER(+3, 720401);
|
||||||
|
GEMMLOWP_EXP_BARREL_SHIFTER(+4, 242);
|
||||||
|
|
||||||
|
#undef GEMMLOWP_EXP_BARREL_SHIFTER
|
||||||
|
|
||||||
|
static constexpr int clampB = kIntegerBits > 5 ? 36 - kIntegerBits : 0;
|
||||||
|
if (kIntegerBits > 5) {
|
||||||
|
const InputF clamp =
|
||||||
|
GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(InputF, -(1 << clampB), -32.0);
|
||||||
|
result = SelectUsingMask(MaskIfLessThan(a, clamp), ResultF::Zero(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = SelectUsingMask(MaskIfZero(a), ResultF::One(), result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of tanh: (1 - exp(-2x)) / (1 + exp(-2x)).
|
||||||
|
|
||||||
|
// Returns (1 - x) / (1 + x) for x in (0, 1).
|
||||||
|
template <typename tRawType>
|
||||||
|
FixedPoint<tRawType, 0> one_minus_x_over_one_plus_x_for_x_in_0_1(
|
||||||
|
FixedPoint<tRawType, 0> a) {
|
||||||
|
typedef FixedPoint<tRawType, 0> F0;
|
||||||
|
typedef FixedPoint<tRawType, 2> F2;
|
||||||
|
F0 half_denominator = RoundingHalfSum(a, F0::One());
|
||||||
|
// Newton-Raphson division
|
||||||
|
// https://en.wikipedia.org/wiki/Division_algorithm#Newton.E2.80.93Raphson_division
|
||||||
|
// Refer to that page for the logic behind the 48/17 and 32/17 constants.
|
||||||
|
const F2 constant_48_over_17 =
|
||||||
|
GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(F2, 1515870810, 48.0 / 17.0);
|
||||||
|
const F2 constant_neg_32_over_17 =
|
||||||
|
GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(F2, -1010580540, -32.0 / 17.0);
|
||||||
|
F2 x = constant_48_over_17 + half_denominator * constant_neg_32_over_17;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
F2 half_denominator_times_x = half_denominator * x;
|
||||||
|
F2 one_minus_half_denominator_times_x =
|
||||||
|
F2::One() - half_denominator_times_x;
|
||||||
|
x = x + Rescale<2>(x * one_minus_half_denominator_times_x);
|
||||||
|
}
|
||||||
|
return Rescale<0>(x - F2::One());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns -tanh(x) for x < 0.
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, 0> neg_tanh_on_negative_values(
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a) {
|
||||||
|
return one_minus_x_over_one_plus_x_for_x_in_0_1(
|
||||||
|
exp_on_negative_values(ExactMulByPot<1>(a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns tanh(x) for any x.
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, 0> tanh(FixedPoint<tRawType, tIntegerBits> a) {
|
||||||
|
typedef FixedPoint<tRawType, tIntegerBits> InputF;
|
||||||
|
typedef FixedPoint<tRawType, 0> ResultF;
|
||||||
|
tRawType mask_if_negative = MaskIfLessThan(a, InputF::Zero());
|
||||||
|
tRawType mask_if_zero = MaskIfZero(a);
|
||||||
|
InputF n = SelectUsingMask(mask_if_negative, a, -a);
|
||||||
|
ResultF t = neg_tanh_on_negative_values(n);
|
||||||
|
return SelectUsingMask(mask_if_zero, ResultF::Zero(),
|
||||||
|
SelectUsingMask(mask_if_negative, -t, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of logistic function.
|
||||||
|
|
||||||
|
// Returns 1 / (1 + x) for x in (0, 1).
|
||||||
|
template <typename tRawType>
|
||||||
|
FixedPoint<tRawType, 0> one_over_one_plus_x_for_x_in_0_1(
|
||||||
|
FixedPoint<tRawType, 0> a) {
|
||||||
|
typedef FixedPoint<tRawType, 0> F0;
|
||||||
|
typedef FixedPoint<tRawType, 2> F2;
|
||||||
|
F0 half_denominator = RoundingHalfSum(a, F0::One());
|
||||||
|
// Newton-Raphson division
|
||||||
|
// https://en.wikipedia.org/wiki/Division_algorithm#Newton.E2.80.93Raphson_division
|
||||||
|
// Refer to that page for the logic behind the 48/17 and 32/17 constants.
|
||||||
|
const F2 constant_48_over_17 =
|
||||||
|
GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(F2, 1515870810, 48.0 / 17.0);
|
||||||
|
const F2 constant_neg_32_over_17 =
|
||||||
|
GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(F2, -1010580540, -32.0 / 17.0);
|
||||||
|
F2 x = constant_48_over_17 + half_denominator * constant_neg_32_over_17;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
F2 half_denominator_times_x = half_denominator * x;
|
||||||
|
F2 one_minus_half_denominator_times_x =
|
||||||
|
F2::One() - half_denominator_times_x;
|
||||||
|
x = x + Rescale<2>(x * one_minus_half_denominator_times_x);
|
||||||
|
}
|
||||||
|
return Rescale<0>(ExactMulByPot<-1>(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns logistic(x) = 1 / (1 + exp(-x)) for x > 0.
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, 0> logistic_on_positive_values(
|
||||||
|
FixedPoint<tRawType, tIntegerBits> a) {
|
||||||
|
return one_over_one_plus_x_for_x_in_0_1(exp_on_negative_values(-a));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns logistic(x) = 1 / (1 + exp(-x)) for any x.
|
||||||
|
template <typename tRawType, int tIntegerBits>
|
||||||
|
FixedPoint<tRawType, 0> logistic(FixedPoint<tRawType, tIntegerBits> a) {
|
||||||
|
typedef FixedPoint<tRawType, tIntegerBits> InputF;
|
||||||
|
typedef FixedPoint<tRawType, 0> ResultF;
|
||||||
|
tRawType mask_if_positive = MaskIfGreaterThan(a, InputF::Zero());
|
||||||
|
tRawType mask_if_zero = MaskIfZero(a);
|
||||||
|
InputF abs_input = SelectUsingMask(mask_if_positive, a, -a);
|
||||||
|
ResultF result_if_positive = logistic_on_positive_values(abs_input);
|
||||||
|
ResultF result_if_negative = ResultF::One() - result_if_positive;
|
||||||
|
const ResultF one_half =
|
||||||
|
GEMMLOWP_CHECKED_FIXEDPOINT_CONSTANT(ResultF, 1 << 30, 0.5);
|
||||||
|
return SelectUsingMask(mask_if_zero, one_half,
|
||||||
|
SelectUsingMask(mask_if_positive, result_if_positive,
|
||||||
|
result_if_negative));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace gemmlowp
|
||||||
|
|
||||||
|
#ifdef GEMMLOWP_NEON
|
||||||
|
#include "./fixedpoint_neon.h"
|
||||||
|
#elif defined(GEMMLOWP_AVX2)
|
||||||
|
#include "./fixedpoint_avx.h"
|
||||||
|
#elif defined(GEMMLOWP_SSE4)
|
||||||
|
#include "./fixedpoint_sse.h"
|
||||||
|
#elif defined(GEMMLOWP_MSA)
|
||||||
|
#include "./fixedpoint_msa.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // GEMMLOWP_INTERNAL_FIXEDPOINT_H_
|
||||||
384
code/lib/tfmicro/fixedpoint/fixedpoint_sse.h
Normal file
384
code/lib/tfmicro/fixedpoint/fixedpoint_sse.h
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// fixedpoint_SSE.h: optimized SSE specializations of the templates
|
||||||
|
// in fixedpoint.h.
|
||||||
|
|
||||||
|
#ifndef GEMMLOWP_INTERNAL_FIXEDPOINT_SSE_H_
|
||||||
|
#define GEMMLOWP_INTERNAL_FIXEDPOINT_SSE_H_
|
||||||
|
|
||||||
|
#include <smmintrin.h>
|
||||||
|
#include "fixedpoint.h"
|
||||||
|
|
||||||
|
namespace gemmlowp {
|
||||||
|
|
||||||
|
// SSE intrinsics are not finely typed: there is a single __m128i vector
|
||||||
|
// type that does not distinguish between "int32x4" and "int16x8" use
|
||||||
|
// cases, unlike the NEON equivalents. Because we had initially focused
|
||||||
|
// on int32x4, we did not pay attention and specialized these fixedpoint
|
||||||
|
// templates directly for __m128i hardcoding the int32x4 semantics,
|
||||||
|
// not leaving room for int16x8 semantics. Amending that by adding a separate
|
||||||
|
// data type, int16x8_m128i, that wraps __m128i while being a separate
|
||||||
|
// type.
|
||||||
|
struct int16x8_m128i {
|
||||||
|
int16x8_m128i() {}
|
||||||
|
explicit int16x8_m128i(__m128i w) : v(w) {}
|
||||||
|
~int16x8_m128i() {}
|
||||||
|
|
||||||
|
__m128i v;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct FixedPointRawTypeTraits<__m128i> {
|
||||||
|
typedef std::int32_t ScalarRawType;
|
||||||
|
static constexpr int kLanes = 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct FixedPointRawTypeTraits<int16x8_m128i> {
|
||||||
|
typedef std::int16_t ScalarRawType;
|
||||||
|
static constexpr int kLanes = 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i BitAnd(__m128i a, __m128i b) {
|
||||||
|
return _mm_and_si128(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i BitAnd(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_and_si128(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i BitOr(__m128i a, __m128i b) {
|
||||||
|
return _mm_or_si128(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i BitOr(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_or_si128(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i BitXor(__m128i a, __m128i b) {
|
||||||
|
return _mm_xor_si128(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i BitXor(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_xor_si128(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i BitNot(__m128i a) {
|
||||||
|
return _mm_andnot_si128(a, _mm_set1_epi32(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i BitNot(int16x8_m128i a) {
|
||||||
|
return int16x8_m128i(_mm_andnot_si128(a.v, _mm_set1_epi16(-1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i Add(__m128i a, __m128i b) {
|
||||||
|
return _mm_add_epi32(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i Add(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_add_epi16(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i Mul(__m128i a, __m128i b) {
|
||||||
|
return _mm_mullo_epi32(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i Mul(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_mullo_epi16(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i Sub(__m128i a, __m128i b) {
|
||||||
|
return _mm_sub_epi32(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i Sub(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_sub_epi16(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i Neg(__m128i a) {
|
||||||
|
return _mm_sign_epi32(a, _mm_set1_epi32(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i Neg(int16x8_m128i a) {
|
||||||
|
return int16x8_m128i(_mm_sign_epi16(a.v, _mm_set1_epi16(-1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i ShiftLeft(__m128i a, int offset) {
|
||||||
|
return _mm_slli_epi32(a, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i ShiftLeft(int16x8_m128i a, int offset) {
|
||||||
|
return int16x8_m128i(_mm_slli_epi16(a.v, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i ShiftRight(__m128i a, int offset) {
|
||||||
|
return _mm_srai_epi32(a, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i ShiftRight(int16x8_m128i a, int offset) {
|
||||||
|
return int16x8_m128i(_mm_srai_epi16(a.v, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i SelectUsingMask(__m128i if_mask, __m128i then_val,
|
||||||
|
__m128i else_val) {
|
||||||
|
// borrowed from Intel's arm_neon_sse.h header.
|
||||||
|
return _mm_or_si128(_mm_and_si128(if_mask, then_val),
|
||||||
|
_mm_andnot_si128(if_mask, else_val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i SelectUsingMask(int16x8_m128i if_mask,
|
||||||
|
int16x8_m128i then_val,
|
||||||
|
int16x8_m128i else_val) {
|
||||||
|
// borrowed from Intel's arm_neon_sse.h header.
|
||||||
|
return int16x8_m128i(SelectUsingMask(if_mask.v, then_val.v, else_val.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i MaskIfEqual(__m128i a, __m128i b) {
|
||||||
|
return _mm_cmpeq_epi32(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i MaskIfEqual(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_cmpeq_epi16(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i MaskIfNotEqual(__m128i a, __m128i b) {
|
||||||
|
return BitNot(MaskIfEqual(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i MaskIfNotEqual(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return BitNot(MaskIfEqual(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i MaskIfZero(__m128i a) {
|
||||||
|
return MaskIfEqual(a, _mm_set1_epi32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i MaskIfZero(int16x8_m128i a) {
|
||||||
|
return MaskIfEqual(a, int16x8_m128i(_mm_set1_epi16(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i MaskIfNonZero(__m128i a) {
|
||||||
|
return MaskIfNotEqual(a, _mm_set1_epi32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i MaskIfNonZero(int16x8_m128i a) {
|
||||||
|
return MaskIfNotEqual(a, int16x8_m128i(_mm_set1_epi16(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i MaskIfGreaterThan(__m128i a, __m128i b) {
|
||||||
|
return _mm_cmpgt_epi32(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i MaskIfGreaterThan(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_cmpgt_epi16(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i MaskIfLessThan(__m128i a, __m128i b) {
|
||||||
|
return _mm_cmplt_epi32(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i MaskIfLessThan(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_cmplt_epi16(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i MaskIfGreaterThanOrEqual(__m128i a, __m128i b) {
|
||||||
|
return BitNot(MaskIfLessThan(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i MaskIfGreaterThanOrEqual(int16x8_m128i a,
|
||||||
|
int16x8_m128i b) {
|
||||||
|
return BitNot(MaskIfLessThan(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i MaskIfLessThanOrEqual(__m128i a, __m128i b) {
|
||||||
|
return BitNot(MaskIfGreaterThan(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i MaskIfLessThanOrEqual(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return BitNot(MaskIfGreaterThan(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assumptions:
|
||||||
|
- All and Any are used on masks.
|
||||||
|
- masks are all_ones for true lanes, all_zeroes otherwise.
|
||||||
|
Hence, All means all 128bits set, and Any means any bit set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool All(__m128i a) {
|
||||||
|
return _mm_testc_si128(a, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool All(int16x8_m128i a) {
|
||||||
|
return _mm_testc_si128(a.v, a.v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool Any(__m128i a) {
|
||||||
|
return !_mm_testz_si128(a, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool Any(int16x8_m128i a) {
|
||||||
|
return !_mm_testz_si128(a.v, a.v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i RoundingHalfSum(__m128i a, __m128i b) {
|
||||||
|
/* __m128i round_bit_mask, a_over_2, b_over_2, round_bit, sum; */
|
||||||
|
/* We divide the inputs before the add to avoid the overflow and costly test
|
||||||
|
*/
|
||||||
|
/* of checking if an overflow occured on signed add */
|
||||||
|
/* round_bit_mask = _mm_set1_epi32(1); */
|
||||||
|
/* a_over_2 = _mm_srai_epi32(a, 1); */
|
||||||
|
/* b_over_2 = _mm_srai_epi32(b, 1); */
|
||||||
|
/* sum = Add(a_over_2, b_over_2); */
|
||||||
|
/* round_bit = _mm_sign_epi32(BitAnd(BitOr(a,b), round_bit_mask), sum); */
|
||||||
|
/* return Add(sum, round_bit); */
|
||||||
|
|
||||||
|
/* Other possibility detecting overflow and xor the sign if an overflow
|
||||||
|
* happened*/
|
||||||
|
__m128i one, sign_bit_mask, sum, rounded_half_sum, overflow, result;
|
||||||
|
one = _mm_set1_epi32(1);
|
||||||
|
sign_bit_mask = _mm_set1_epi32(0x80000000);
|
||||||
|
sum = Add(a, b);
|
||||||
|
rounded_half_sum = _mm_srai_epi32(Add(sum, one), 1);
|
||||||
|
overflow =
|
||||||
|
BitAnd(BitAnd(BitXor(a, rounded_half_sum), BitXor(b, rounded_half_sum)),
|
||||||
|
sign_bit_mask);
|
||||||
|
result = BitXor(rounded_half_sum, overflow);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i RoundingHalfSum(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
// Idea: go to unsigned to use _mm_avg_epu16,
|
||||||
|
// borrowed from Intel's arm_neon_sse.h header.
|
||||||
|
__m128i constant_neg_32768 = _mm_set1_epi16(-32768);
|
||||||
|
__m128i a_unsigned = _mm_sub_epi16(a.v, constant_neg_32768);
|
||||||
|
__m128i b_unsigned = _mm_sub_epi16(b.v, constant_neg_32768);
|
||||||
|
__m128i avg_unsigned = _mm_avg_epu16(a_unsigned, b_unsigned);
|
||||||
|
__m128i avg = _mm_add_epi16(avg_unsigned, constant_neg_32768);
|
||||||
|
return int16x8_m128i(avg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i SaturatingRoundingDoublingHighMul(__m128i a, __m128i b) {
|
||||||
|
__m128i min, saturation_mask, a0_a2, a1_a3, b0_b2, b1_b3;
|
||||||
|
__m128i a0b0_a2b2, a1b1_a3b3, a0b0_a2b2_rounded, a1b1_a3b3_rounded;
|
||||||
|
__m128i a0b0_a2b2_rounded_2x, a1b1_a3b3_rounded_2x, result;
|
||||||
|
__m128i nudge;
|
||||||
|
|
||||||
|
// saturation only happen if a == b == INT_MIN
|
||||||
|
min = _mm_set1_epi32(std::numeric_limits<std::int32_t>::min());
|
||||||
|
saturation_mask = BitAnd(MaskIfEqual(a, b), MaskIfEqual(a, min));
|
||||||
|
|
||||||
|
// a = a0 | a1 | a2 | a3
|
||||||
|
// b = b0 | b1 | b2 | b3
|
||||||
|
a0_a2 = a;
|
||||||
|
a1_a3 = _mm_srli_si128(a, 4);
|
||||||
|
b0_b2 = b;
|
||||||
|
b1_b3 = _mm_srli_si128(b, 4);
|
||||||
|
|
||||||
|
a0b0_a2b2 = _mm_mul_epi32(a0_a2, b0_b2);
|
||||||
|
a1b1_a3b3 = _mm_mul_epi32(a1_a3, b1_b3);
|
||||||
|
|
||||||
|
// do the rounding and take into account that it will be doubled
|
||||||
|
nudge = _mm_set1_epi64x(1 << 30);
|
||||||
|
a0b0_a2b2_rounded = _mm_add_epi64(a0b0_a2b2, nudge);
|
||||||
|
a1b1_a3b3_rounded = _mm_add_epi64(a1b1_a3b3, nudge);
|
||||||
|
|
||||||
|
// do the doubling
|
||||||
|
a0b0_a2b2_rounded_2x = _mm_slli_epi64(a0b0_a2b2_rounded, 1);
|
||||||
|
a1b1_a3b3_rounded_2x = _mm_slli_epi64(a1b1_a3b3_rounded, 1);
|
||||||
|
|
||||||
|
// get the high part of the products
|
||||||
|
result = _mm_blend_epi16(_mm_srli_si128(a0b0_a2b2_rounded_2x, 4),
|
||||||
|
a1b1_a3b3_rounded_2x, 0xcc);
|
||||||
|
|
||||||
|
// saturate those which overflowed
|
||||||
|
return SelectUsingMask(saturation_mask, min, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i SaturatingRoundingDoublingHighMul(int16x8_m128i a,
|
||||||
|
int16x8_m128i b) {
|
||||||
|
// Idea: use _mm_mulhrs_epi16 then saturate with a bit-operation,
|
||||||
|
// borrowed from Intel's arm_neon_sse.h header.
|
||||||
|
__m128i result_unsaturated = _mm_mulhrs_epi16(a.v, b.v);
|
||||||
|
__m128i saturation_mask =
|
||||||
|
_mm_cmpeq_epi16(result_unsaturated, _mm_set1_epi16(0x8000));
|
||||||
|
__m128i result = _mm_xor_si128(result_unsaturated, saturation_mask);
|
||||||
|
return int16x8_m128i(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline __m128i Dup<__m128i>(std::int32_t x) {
|
||||||
|
return _mm_set1_epi32(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i Dup<int16x8_m128i>(std::int16_t x) {
|
||||||
|
return int16x8_m128i(_mm_set1_epi16(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// So far this is only needed for int16.
|
||||||
|
template <>
|
||||||
|
inline int16x8_m128i SaturatingAdd(int16x8_m128i a, int16x8_m128i b) {
|
||||||
|
return int16x8_m128i(_mm_adds_epi16(a.v, b.v));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace gemmlowp
|
||||||
|
|
||||||
|
#endif // GEMMLOWP_INTERNAL_FIXEDPOINT_SSE_H_
|
||||||
398
code/lib/tfmicro/flatbuffers/base.h
Normal file
398
code/lib/tfmicro/flatbuffers/base.h
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
#ifndef FLATBUFFERS_BASE_H_
|
||||||
|
#define FLATBUFFERS_BASE_H_
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
// If activate should be declared and included first.
|
||||||
|
#if defined(FLATBUFFERS_MEMORY_LEAK_TRACKING) && \
|
||||||
|
defined(_MSC_VER) && defined(_DEBUG)
|
||||||
|
// The _CRTDBG_MAP_ALLOC inside <crtdbg.h> will replace
|
||||||
|
// calloc/free (etc) to its debug version using #define directives.
|
||||||
|
#define _CRTDBG_MAP_ALLOC
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <crtdbg.h>
|
||||||
|
// Replace operator new by trace-enabled version.
|
||||||
|
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
|
||||||
|
#define new DEBUG_NEW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(FLATBUFFERS_ASSERT)
|
||||||
|
#include <assert.h>
|
||||||
|
#define FLATBUFFERS_ASSERT assert
|
||||||
|
#elif defined(FLATBUFFERS_ASSERT_INCLUDE)
|
||||||
|
// Include file with forward declaration
|
||||||
|
#include FLATBUFFERS_ASSERT_INCLUDE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINO
|
||||||
|
#include <cstdint>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#if defined(ARDUINO) && !defined(ARDUINOSTL_M_H)
|
||||||
|
#include <utility.h>
|
||||||
|
#else
|
||||||
|
#include <utility>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef _STLPORT_VERSION
|
||||||
|
#define FLATBUFFERS_CPP98_STL
|
||||||
|
#endif
|
||||||
|
#ifndef FLATBUFFERS_CPP98_STL
|
||||||
|
#include <functional>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "flatbuffers/stl_emulation.h"
|
||||||
|
|
||||||
|
#if defined(__ICCARM__)
|
||||||
|
#include <intrinsics.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Note the __clang__ check is needed, because clang presents itself
|
||||||
|
// as an older GNUC compiler (4.2).
|
||||||
|
// Clang 3.3 and later implement all of the ISO C++ 2011 standard.
|
||||||
|
// Clang 3.4 and later implement all of the ISO C++ 2014 standard.
|
||||||
|
// http://clang.llvm.org/cxx_status.html
|
||||||
|
|
||||||
|
// Note the MSVC value '__cplusplus' may be incorrect:
|
||||||
|
// The '__cplusplus' predefined macro in the MSVC stuck at the value 199711L,
|
||||||
|
// indicating (erroneously!) that the compiler conformed to the C++98 Standard.
|
||||||
|
// This value should be correct starting from MSVC2017-15.7-Preview-3.
|
||||||
|
// The '__cplusplus' will be valid only if MSVC2017-15.7-P3 and the `/Zc:__cplusplus` switch is set.
|
||||||
|
// Workaround (for details see MSDN):
|
||||||
|
// Use the _MSC_VER and _MSVC_LANG definition instead of the __cplusplus for compatibility.
|
||||||
|
// The _MSVC_LANG macro reports the Standard version regardless of the '/Zc:__cplusplus' switch.
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#define FLATBUFFERS_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_GCC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define FLATBUFFERS_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_CLANG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @cond FLATBUFFERS_INTERNAL
|
||||||
|
#if __cplusplus <= 199711L && \
|
||||||
|
(!defined(_MSC_VER) || _MSC_VER < 1600) && \
|
||||||
|
(!defined(__GNUC__) || \
|
||||||
|
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40400))
|
||||||
|
#error A C++11 compatible compiler with support for the auto typing is \
|
||||||
|
required for FlatBuffers.
|
||||||
|
#error __cplusplus _MSC_VER __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__clang__) && \
|
||||||
|
defined(__GNUC__) && \
|
||||||
|
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40600)
|
||||||
|
// Backwards compatibility for g++ 4.4, and 4.5 which don't have the nullptr
|
||||||
|
// and constexpr keywords. Note the __clang__ check is needed, because clang
|
||||||
|
// presents itself as an older GNUC compiler.
|
||||||
|
#ifndef nullptr_t
|
||||||
|
const class nullptr_t {
|
||||||
|
public:
|
||||||
|
template<class T> inline operator T*() const { return 0; }
|
||||||
|
private:
|
||||||
|
void operator&() const;
|
||||||
|
} nullptr = {};
|
||||||
|
#endif
|
||||||
|
#ifndef constexpr
|
||||||
|
#define constexpr const
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The wire format uses a little endian encoding (since that's efficient for
|
||||||
|
// the common platforms).
|
||||||
|
#if defined(__s390x__)
|
||||||
|
#define FLATBUFFERS_LITTLEENDIAN 0
|
||||||
|
#endif // __s390x__
|
||||||
|
#if !defined(FLATBUFFERS_LITTLEENDIAN)
|
||||||
|
#if defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__)
|
||||||
|
#if (defined(__BIG_ENDIAN__) || \
|
||||||
|
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||||
|
#define FLATBUFFERS_LITTLEENDIAN 0
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_LITTLEENDIAN 1
|
||||||
|
#endif // __BIG_ENDIAN__
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#if defined(_M_PPC)
|
||||||
|
#define FLATBUFFERS_LITTLEENDIAN 0
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_LITTLEENDIAN 1
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error Unable to determine endianness, define FLATBUFFERS_LITTLEENDIAN.
|
||||||
|
#endif
|
||||||
|
#endif // !defined(FLATBUFFERS_LITTLEENDIAN)
|
||||||
|
|
||||||
|
#define FLATBUFFERS_VERSION_MAJOR 1
|
||||||
|
#define FLATBUFFERS_VERSION_MINOR 12
|
||||||
|
#define FLATBUFFERS_VERSION_REVISION 0
|
||||||
|
#define FLATBUFFERS_STRING_EXPAND(X) #X
|
||||||
|
#define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X)
|
||||||
|
namespace flatbuffers {
|
||||||
|
// Returns version as string "MAJOR.MINOR.REVISION".
|
||||||
|
const char* FLATBUFFERS_VERSION();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (!defined(_MSC_VER) || _MSC_VER > 1600) && \
|
||||||
|
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 407)) || \
|
||||||
|
defined(__clang__)
|
||||||
|
#define FLATBUFFERS_FINAL_CLASS final
|
||||||
|
#define FLATBUFFERS_OVERRIDE override
|
||||||
|
#define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : flatbuffers::voffset_t
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_FINAL_CLASS
|
||||||
|
#define FLATBUFFERS_OVERRIDE
|
||||||
|
#define FLATBUFFERS_VTABLE_UNDERLYING_TYPE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \
|
||||||
|
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \
|
||||||
|
(defined(__cpp_constexpr) && __cpp_constexpr >= 200704)
|
||||||
|
#define FLATBUFFERS_CONSTEXPR constexpr
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_CONSTEXPR const
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__cplusplus) && __cplusplus >= 201402L) || \
|
||||||
|
(defined(__cpp_constexpr) && __cpp_constexpr >= 201304)
|
||||||
|
#define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_CONSTEXPR_CPP14
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \
|
||||||
|
(defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023026)) || \
|
||||||
|
defined(__clang__)
|
||||||
|
#define FLATBUFFERS_NOEXCEPT noexcept
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE: the FLATBUFFERS_DELETE_FUNC macro may change the access mode to
|
||||||
|
// private, so be sure to put it at the end or reset access mode explicitly.
|
||||||
|
#if (!defined(_MSC_VER) || _MSC_FULL_VER >= 180020827) && \
|
||||||
|
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)) || \
|
||||||
|
defined(__clang__)
|
||||||
|
#define FLATBUFFERS_DELETE_FUNC(func) func = delete;
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_DELETE_FUNC(func) private: func;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FLATBUFFERS_HAS_STRING_VIEW
|
||||||
|
// Only provide flatbuffers::string_view if __has_include can be used
|
||||||
|
// to detect a header that provides an implementation
|
||||||
|
#if defined(__has_include)
|
||||||
|
// Check for std::string_view (in c++17)
|
||||||
|
#if __has_include(<string_view>) && (__cplusplus >= 201606 || (defined(_HAS_CXX17) && _HAS_CXX17))
|
||||||
|
#include <string_view>
|
||||||
|
namespace flatbuffers {
|
||||||
|
typedef std::string_view string_view;
|
||||||
|
}
|
||||||
|
#define FLATBUFFERS_HAS_STRING_VIEW 1
|
||||||
|
// Check for std::experimental::string_view (in c++14, compiler-dependent)
|
||||||
|
#elif __has_include(<experimental/string_view>) && (__cplusplus >= 201411)
|
||||||
|
#include <experimental/string_view>
|
||||||
|
namespace flatbuffers {
|
||||||
|
typedef std::experimental::string_view string_view;
|
||||||
|
}
|
||||||
|
#define FLATBUFFERS_HAS_STRING_VIEW 1
|
||||||
|
// Check for absl::string_view
|
||||||
|
#elif __has_include("absl/strings/string_view.h")
|
||||||
|
#include "absl/strings/string_view.h"
|
||||||
|
namespace flatbuffers {
|
||||||
|
typedef absl::string_view string_view;
|
||||||
|
}
|
||||||
|
#define FLATBUFFERS_HAS_STRING_VIEW 1
|
||||||
|
#endif
|
||||||
|
#endif // __has_include
|
||||||
|
#endif // !FLATBUFFERS_HAS_STRING_VIEW
|
||||||
|
|
||||||
|
#ifndef FLATBUFFERS_HAS_NEW_STRTOD
|
||||||
|
// Modern (C++11) strtod and strtof functions are available for use.
|
||||||
|
// 1) nan/inf strings as argument of strtod;
|
||||||
|
// 2) hex-float as argument of strtod/strtof.
|
||||||
|
#if (defined(_MSC_VER) && _MSC_VER >= 1900) || \
|
||||||
|
(defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \
|
||||||
|
(defined(__clang__))
|
||||||
|
#define FLATBUFFERS_HAS_NEW_STRTOD 1
|
||||||
|
#endif
|
||||||
|
#endif // !FLATBUFFERS_HAS_NEW_STRTOD
|
||||||
|
|
||||||
|
#ifndef FLATBUFFERS_LOCALE_INDEPENDENT
|
||||||
|
// Enable locale independent functions {strtof_l, strtod_l,strtoll_l, strtoull_l}.
|
||||||
|
// They are part of the POSIX-2008 but not part of the C/C++ standard.
|
||||||
|
// GCC/Clang have definition (_XOPEN_SOURCE>=700) if POSIX-2008.
|
||||||
|
#if ((defined(_MSC_VER) && _MSC_VER >= 1800) || \
|
||||||
|
(defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE>=700)))
|
||||||
|
#define FLATBUFFERS_LOCALE_INDEPENDENT 1
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_LOCALE_INDEPENDENT 0
|
||||||
|
#endif
|
||||||
|
#endif // !FLATBUFFERS_LOCALE_INDEPENDENT
|
||||||
|
|
||||||
|
// Suppress Undefined Behavior Sanitizer (recoverable only). Usage:
|
||||||
|
// - __supress_ubsan__("undefined")
|
||||||
|
// - __supress_ubsan__("signed-integer-overflow")
|
||||||
|
#if defined(__clang__) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >=7))
|
||||||
|
#define __supress_ubsan__(type) __attribute__((no_sanitize(type)))
|
||||||
|
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)
|
||||||
|
#define __supress_ubsan__(type) __attribute__((no_sanitize_undefined))
|
||||||
|
#else
|
||||||
|
#define __supress_ubsan__(type)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This is constexpr function used for checking compile-time constants.
|
||||||
|
// Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`.
|
||||||
|
template<typename T> FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) {
|
||||||
|
return !!t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable C++ attribute [[]] if std:c++17 or higher.
|
||||||
|
#if ((__cplusplus >= 201703L) \
|
||||||
|
|| (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)))
|
||||||
|
// All attributes unknown to an implementation are ignored without causing an error.
|
||||||
|
#define FLATBUFFERS_ATTRIBUTE(attr) [[attr]]
|
||||||
|
|
||||||
|
#define FLATBUFFERS_FALLTHROUGH() [[fallthrough]]
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_ATTRIBUTE(attr)
|
||||||
|
|
||||||
|
#if FLATBUFFERS_CLANG >= 30800
|
||||||
|
#define FLATBUFFERS_FALLTHROUGH() [[clang::fallthrough]]
|
||||||
|
#elif FLATBUFFERS_GCC >= 70300
|
||||||
|
#define FLATBUFFERS_FALLTHROUGH() [[gnu::fallthrough]]
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_FALLTHROUGH()
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// @file
|
||||||
|
namespace flatbuffers {
|
||||||
|
|
||||||
|
/// @cond FLATBUFFERS_INTERNAL
|
||||||
|
// Our default offset / size type, 32bit on purpose on 64bit systems.
|
||||||
|
// Also, using a consistent offset type maintains compatibility of serialized
|
||||||
|
// offset values between 32bit and 64bit systems.
|
||||||
|
typedef uint32_t uoffset_t;
|
||||||
|
|
||||||
|
// Signed offsets for references that can go in both directions.
|
||||||
|
typedef int32_t soffset_t;
|
||||||
|
|
||||||
|
// Offset/index used in v-tables, can be changed to uint8_t in
|
||||||
|
// format forks to save a bit of space if desired.
|
||||||
|
typedef uint16_t voffset_t;
|
||||||
|
|
||||||
|
typedef uintmax_t largest_scalar_t;
|
||||||
|
|
||||||
|
// In 32bits, this evaluates to 2GB - 1
|
||||||
|
#define FLATBUFFERS_MAX_BUFFER_SIZE ((1ULL << (sizeof(::flatbuffers::soffset_t) * 8 - 1)) - 1)
|
||||||
|
|
||||||
|
// We support aligning the contents of buffers up to this size.
|
||||||
|
#define FLATBUFFERS_MAX_ALIGNMENT 16
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4127) // C4127: conditional expression is constant
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T> T EndianSwap(T t) {
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define FLATBUFFERS_BYTESWAP16 _byteswap_ushort
|
||||||
|
#define FLATBUFFERS_BYTESWAP32 _byteswap_ulong
|
||||||
|
#define FLATBUFFERS_BYTESWAP64 _byteswap_uint64
|
||||||
|
#elif defined(__ICCARM__)
|
||||||
|
#define FLATBUFFERS_BYTESWAP16 __REV16
|
||||||
|
#define FLATBUFFERS_BYTESWAP32 __REV
|
||||||
|
#define FLATBUFFERS_BYTESWAP64(x) \
|
||||||
|
((__REV(static_cast<uint32_t>(x >> 32U))) | (static_cast<uint64_t>(__REV(static_cast<uint32_t>(x)))) << 32U)
|
||||||
|
#else
|
||||||
|
#if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ < 408 && !defined(__clang__)
|
||||||
|
// __builtin_bswap16 was missing prior to GCC 4.8.
|
||||||
|
#define FLATBUFFERS_BYTESWAP16(x) \
|
||||||
|
static_cast<uint16_t>(__builtin_bswap32(static_cast<uint32_t>(x) << 16))
|
||||||
|
#else
|
||||||
|
#define FLATBUFFERS_BYTESWAP16 __builtin_bswap16
|
||||||
|
#endif
|
||||||
|
#define FLATBUFFERS_BYTESWAP32 __builtin_bswap32
|
||||||
|
#define FLATBUFFERS_BYTESWAP64 __builtin_bswap64
|
||||||
|
#endif
|
||||||
|
if (sizeof(T) == 1) { // Compile-time if-then's.
|
||||||
|
return t;
|
||||||
|
} else if (sizeof(T) == 2) {
|
||||||
|
union { T t; uint16_t i; } u = { t };
|
||||||
|
u.i = FLATBUFFERS_BYTESWAP16(u.i);
|
||||||
|
return u.t;
|
||||||
|
} else if (sizeof(T) == 4) {
|
||||||
|
union { T t; uint32_t i; } u = { t };
|
||||||
|
u.i = FLATBUFFERS_BYTESWAP32(u.i);
|
||||||
|
return u.t;
|
||||||
|
} else if (sizeof(T) == 8) {
|
||||||
|
union { T t; uint64_t i; } u = { t };
|
||||||
|
u.i = FLATBUFFERS_BYTESWAP64(u.i);
|
||||||
|
return u.t;
|
||||||
|
} else {
|
||||||
|
FLATBUFFERS_ASSERT(0);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T> T EndianScalar(T t) {
|
||||||
|
#if FLATBUFFERS_LITTLEENDIAN
|
||||||
|
return t;
|
||||||
|
#else
|
||||||
|
return EndianSwap(t);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
// UBSAN: C++ aliasing type rules, see std::bit_cast<> for details.
|
||||||
|
__supress_ubsan__("alignment")
|
||||||
|
T ReadScalar(const void *p) {
|
||||||
|
return EndianScalar(*reinterpret_cast<const T *>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
// UBSAN: C++ aliasing type rules, see std::bit_cast<> for details.
|
||||||
|
__supress_ubsan__("alignment")
|
||||||
|
void WriteScalar(void *p, T t) {
|
||||||
|
*reinterpret_cast<T *>(p) = EndianScalar(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> struct Offset;
|
||||||
|
template<typename T> __supress_ubsan__("alignment") void WriteScalar(void *p, Offset<T> t) {
|
||||||
|
*reinterpret_cast<uoffset_t *>(p) = EndianScalar(t.o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes how many bytes you'd have to pad to be able to write an
|
||||||
|
// "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in
|
||||||
|
// memory).
|
||||||
|
__supress_ubsan__("unsigned-integer-overflow")
|
||||||
|
inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) {
|
||||||
|
return ((~buf_size) + 1) & (scalar_size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace flatbuffers
|
||||||
|
#endif // FLATBUFFERS_BASE_H_
|
||||||
2783
code/lib/tfmicro/flatbuffers/flatbuffers.h
Normal file
2783
code/lib/tfmicro/flatbuffers/flatbuffers.h
Normal file
File diff suppressed because it is too large
Load Diff
307
code/lib/tfmicro/flatbuffers/stl_emulation.h
Normal file
307
code/lib/tfmicro/flatbuffers/stl_emulation.h
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLATBUFFERS_STL_EMULATION_H_
|
||||||
|
#define FLATBUFFERS_STL_EMULATION_H_
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#if defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL)
|
||||||
|
#define FLATBUFFERS_CPP98_STL
|
||||||
|
#endif // defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL)
|
||||||
|
|
||||||
|
#if defined(FLATBUFFERS_CPP98_STL)
|
||||||
|
#include <cctype>
|
||||||
|
#endif // defined(FLATBUFFERS_CPP98_STL)
|
||||||
|
|
||||||
|
// Check if we can use template aliases
|
||||||
|
// Not possible if Microsoft Compiler before 2012
|
||||||
|
// Possible is the language feature __cpp_alias_templates is defined well
|
||||||
|
// Or possible if the C++ std is C+11 or newer
|
||||||
|
#if (defined(_MSC_VER) && _MSC_VER > 1700 /* MSVC2012 */) \
|
||||||
|
|| (defined(__cpp_alias_templates) && __cpp_alias_templates >= 200704) \
|
||||||
|
|| (defined(__cplusplus) && __cplusplus >= 201103L)
|
||||||
|
#define FLATBUFFERS_TEMPLATES_ALIASES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This header provides backwards compatibility for C++98 STLs like stlport.
|
||||||
|
namespace flatbuffers {
|
||||||
|
|
||||||
|
// Retrieve ::back() from a string in a way that is compatible with pre C++11
|
||||||
|
// STLs (e.g stlport).
|
||||||
|
inline char& string_back(std::string &value) {
|
||||||
|
return value[value.length() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char string_back(const std::string &value) {
|
||||||
|
return value[value.length() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method that retrieves ::data() from a vector in a way that is
|
||||||
|
// compatible with pre C++11 STLs (e.g stlport).
|
||||||
|
template <typename T> inline T *vector_data(std::vector<T> &vector) {
|
||||||
|
// In some debug environments, operator[] does bounds checking, so &vector[0]
|
||||||
|
// can't be used.
|
||||||
|
return vector.empty() ? nullptr : &vector[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> inline const T *vector_data(
|
||||||
|
const std::vector<T> &vector) {
|
||||||
|
return vector.empty() ? nullptr : &vector[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename V>
|
||||||
|
inline void vector_emplace_back(std::vector<T> *vector, V &&data) {
|
||||||
|
#if defined(FLATBUFFERS_CPP98_STL)
|
||||||
|
vector->push_back(data);
|
||||||
|
#else
|
||||||
|
vector->emplace_back(std::forward<V>(data));
|
||||||
|
#endif // defined(FLATBUFFERS_CPP98_STL)
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef FLATBUFFERS_CPP98_STL
|
||||||
|
#if defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||||
|
template <typename T>
|
||||||
|
using numeric_limits = std::numeric_limits<T>;
|
||||||
|
#else
|
||||||
|
template <typename T> class numeric_limits :
|
||||||
|
public std::numeric_limits<T> {};
|
||||||
|
#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||||
|
#else
|
||||||
|
template <typename T> class numeric_limits :
|
||||||
|
public std::numeric_limits<T> {
|
||||||
|
public:
|
||||||
|
// Android NDK fix.
|
||||||
|
static T lowest() {
|
||||||
|
return std::numeric_limits<T>::min();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> class numeric_limits<float> :
|
||||||
|
public std::numeric_limits<float> {
|
||||||
|
public:
|
||||||
|
static float lowest() { return -FLT_MAX; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> class numeric_limits<double> :
|
||||||
|
public std::numeric_limits<double> {
|
||||||
|
public:
|
||||||
|
static double lowest() { return -DBL_MAX; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> class numeric_limits<unsigned long long> {
|
||||||
|
public:
|
||||||
|
static unsigned long long min() { return 0ULL; }
|
||||||
|
static unsigned long long max() { return ~0ULL; }
|
||||||
|
static unsigned long long lowest() {
|
||||||
|
return numeric_limits<unsigned long long>::min();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> class numeric_limits<long long> {
|
||||||
|
public:
|
||||||
|
static long long min() {
|
||||||
|
return static_cast<long long>(1ULL << ((sizeof(long long) << 3) - 1));
|
||||||
|
}
|
||||||
|
static long long max() {
|
||||||
|
return static_cast<long long>(
|
||||||
|
(1ULL << ((sizeof(long long) << 3) - 1)) - 1);
|
||||||
|
}
|
||||||
|
static long long lowest() {
|
||||||
|
return numeric_limits<long long>::min();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // FLATBUFFERS_CPP98_STL
|
||||||
|
|
||||||
|
#if defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||||
|
#ifndef FLATBUFFERS_CPP98_STL
|
||||||
|
template <typename T> using is_scalar = std::is_scalar<T>;
|
||||||
|
template <typename T, typename U> using is_same = std::is_same<T,U>;
|
||||||
|
template <typename T> using is_floating_point = std::is_floating_point<T>;
|
||||||
|
template <typename T> using is_unsigned = std::is_unsigned<T>;
|
||||||
|
template <typename T> using is_enum = std::is_enum<T>;
|
||||||
|
template <typename T> using make_unsigned = std::make_unsigned<T>;
|
||||||
|
template<bool B, class T, class F>
|
||||||
|
using conditional = std::conditional<B, T, F>;
|
||||||
|
template<class T, T v>
|
||||||
|
using integral_constant = std::integral_constant<T, v>;
|
||||||
|
#else
|
||||||
|
// Map C++ TR1 templates defined by stlport.
|
||||||
|
template <typename T> using is_scalar = std::tr1::is_scalar<T>;
|
||||||
|
template <typename T, typename U> using is_same = std::tr1::is_same<T,U>;
|
||||||
|
template <typename T> using is_floating_point =
|
||||||
|
std::tr1::is_floating_point<T>;
|
||||||
|
template <typename T> using is_unsigned = std::tr1::is_unsigned<T>;
|
||||||
|
template <typename T> using is_enum = std::tr1::is_enum<T>;
|
||||||
|
// Android NDK doesn't have std::make_unsigned or std::tr1::make_unsigned.
|
||||||
|
template<typename T> struct make_unsigned {
|
||||||
|
static_assert(is_unsigned<T>::value, "Specialization not implemented!");
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
template<> struct make_unsigned<char> { using type = unsigned char; };
|
||||||
|
template<> struct make_unsigned<short> { using type = unsigned short; };
|
||||||
|
template<> struct make_unsigned<int> { using type = unsigned int; };
|
||||||
|
template<> struct make_unsigned<long> { using type = unsigned long; };
|
||||||
|
template<>
|
||||||
|
struct make_unsigned<long long> { using type = unsigned long long; };
|
||||||
|
template<bool B, class T, class F>
|
||||||
|
using conditional = std::tr1::conditional<B, T, F>;
|
||||||
|
template<class T, T v>
|
||||||
|
using integral_constant = std::tr1::integral_constant<T, v>;
|
||||||
|
#endif // !FLATBUFFERS_CPP98_STL
|
||||||
|
#else
|
||||||
|
// MSVC 2010 doesn't support C++11 aliases.
|
||||||
|
template <typename T> struct is_scalar : public std::is_scalar<T> {};
|
||||||
|
template <typename T, typename U> struct is_same : public std::is_same<T,U> {};
|
||||||
|
template <typename T> struct is_floating_point :
|
||||||
|
public std::is_floating_point<T> {};
|
||||||
|
template <typename T> struct is_unsigned : public std::is_unsigned<T> {};
|
||||||
|
template <typename T> struct is_enum : public std::is_enum<T> {};
|
||||||
|
template <typename T> struct make_unsigned : public std::make_unsigned<T> {};
|
||||||
|
template<bool B, class T, class F>
|
||||||
|
struct conditional : public std::conditional<B, T, F> {};
|
||||||
|
template<class T, T v>
|
||||||
|
struct integral_constant : public std::integral_constant<T, v> {};
|
||||||
|
#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||||
|
|
||||||
|
#ifndef FLATBUFFERS_CPP98_STL
|
||||||
|
#if defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||||
|
template <class T> using unique_ptr = std::unique_ptr<T>;
|
||||||
|
#else
|
||||||
|
// MSVC 2010 doesn't support C++11 aliases.
|
||||||
|
// We're manually "aliasing" the class here as we want to bring unique_ptr
|
||||||
|
// into the flatbuffers namespace. We have unique_ptr in the flatbuffers
|
||||||
|
// namespace we have a completely independent implemenation (see below)
|
||||||
|
// for C++98 STL implementations.
|
||||||
|
template <class T> class unique_ptr : public std::unique_ptr<T> {
|
||||||
|
public:
|
||||||
|
unique_ptr() {}
|
||||||
|
explicit unique_ptr(T* p) : std::unique_ptr<T>(p) {}
|
||||||
|
unique_ptr(std::unique_ptr<T>&& u) { *this = std::move(u); }
|
||||||
|
unique_ptr(unique_ptr&& u) { *this = std::move(u); }
|
||||||
|
unique_ptr& operator=(std::unique_ptr<T>&& u) {
|
||||||
|
std::unique_ptr<T>::reset(u.release());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
unique_ptr& operator=(unique_ptr&& u) {
|
||||||
|
std::unique_ptr<T>::reset(u.release());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
unique_ptr& operator=(T* p) {
|
||||||
|
return std::unique_ptr<T>::operator=(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||||
|
#else
|
||||||
|
// Very limited implementation of unique_ptr.
|
||||||
|
// This is provided simply to allow the C++ code generated from the default
|
||||||
|
// settings to function in C++98 environments with no modifications.
|
||||||
|
template <class T> class unique_ptr {
|
||||||
|
public:
|
||||||
|
typedef T element_type;
|
||||||
|
|
||||||
|
unique_ptr() : ptr_(nullptr) {}
|
||||||
|
explicit unique_ptr(T* p) : ptr_(p) {}
|
||||||
|
unique_ptr(unique_ptr&& u) : ptr_(nullptr) { reset(u.release()); }
|
||||||
|
unique_ptr(const unique_ptr& u) : ptr_(nullptr) {
|
||||||
|
reset(const_cast<unique_ptr*>(&u)->release());
|
||||||
|
}
|
||||||
|
~unique_ptr() { reset(); }
|
||||||
|
|
||||||
|
unique_ptr& operator=(const unique_ptr& u) {
|
||||||
|
reset(const_cast<unique_ptr*>(&u)->release());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr& operator=(unique_ptr&& u) {
|
||||||
|
reset(u.release());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr& operator=(T* p) {
|
||||||
|
reset(p);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& operator*() const { return *ptr_; }
|
||||||
|
T* operator->() const { return ptr_; }
|
||||||
|
T* get() const noexcept { return ptr_; }
|
||||||
|
explicit operator bool() const { return ptr_ != nullptr; }
|
||||||
|
|
||||||
|
// modifiers
|
||||||
|
T* release() {
|
||||||
|
T* value = ptr_;
|
||||||
|
ptr_ = nullptr;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(T* p = nullptr) {
|
||||||
|
T* value = ptr_;
|
||||||
|
ptr_ = p;
|
||||||
|
if (value) delete value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(unique_ptr& u) {
|
||||||
|
T* temp_ptr = ptr_;
|
||||||
|
ptr_ = u.ptr_;
|
||||||
|
u.ptr_ = temp_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T> bool operator==(const unique_ptr<T>& x,
|
||||||
|
const unique_ptr<T>& y) {
|
||||||
|
return x.get() == y.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class D> bool operator==(const unique_ptr<T>& x,
|
||||||
|
const D* y) {
|
||||||
|
return static_cast<D*>(x.get()) == y;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> bool operator==(const unique_ptr<T>& x, intptr_t y) {
|
||||||
|
return reinterpret_cast<intptr_t>(x.get()) == y;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> bool operator!=(const unique_ptr<T>& x, decltype(nullptr)) {
|
||||||
|
return !!x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> bool operator!=(decltype(nullptr), const unique_ptr<T>& x) {
|
||||||
|
return !!x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> bool operator==(const unique_ptr<T>& x, decltype(nullptr)) {
|
||||||
|
return !x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> bool operator==(decltype(nullptr), const unique_ptr<T>& x) {
|
||||||
|
return !x;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !FLATBUFFERS_CPP98_STL
|
||||||
|
|
||||||
|
} // namespace flatbuffers
|
||||||
|
|
||||||
|
#endif // FLATBUFFERS_STL_EMULATION_H_
|
||||||
166
code/lib/tfmicro/internal/detect_platform.h
Normal file
166
code/lib/tfmicro/internal/detect_platform.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
// Copyright 2018 The Gemmlowp Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// detect_platform.h: Sets up macros that control architecture-specific
|
||||||
|
// features of gemmlowp's implementation.
|
||||||
|
|
||||||
|
#ifndef GEMMLOWP_INTERNAL_DETECT_PLATFORM_H_
|
||||||
|
#define GEMMLOWP_INTERNAL_DETECT_PLATFORM_H_
|
||||||
|
|
||||||
|
// Our inline assembly path assume GCC/Clang syntax.
|
||||||
|
// Native Client doesn't seem to support inline assembly(?).
|
||||||
|
#if defined(__GNUC__) && !defined(__native_client__)
|
||||||
|
#define GEMMLOWP_ALLOW_INLINE_ASM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Define macro statement that avoids inlining for GCC.
|
||||||
|
// For non-GCC, define as empty macro.
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define GEMMLOWP_NOINLINE __attribute__((noinline))
|
||||||
|
#else
|
||||||
|
#define GEMMLOWP_NOINLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Detect ARM, 32-bit or 64-bit
|
||||||
|
#ifdef __arm__
|
||||||
|
#define GEMMLOWP_ARM_32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __aarch64__
|
||||||
|
#define GEMMLOWP_ARM_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_ARM_32) || defined(GEMMLOWP_ARM_64)
|
||||||
|
#define GEMMLOWP_ARM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Detect MIPS, 32-bit or 64-bit
|
||||||
|
#if defined(__mips) && !defined(__LP64__)
|
||||||
|
#define GEMMLOWP_MIPS_32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__mips) && defined(__LP64__)
|
||||||
|
#define GEMMLOWP_MIPS_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_MIPS_32) || defined(GEMMLOWP_MIPS_64)
|
||||||
|
#define GEMMLOWP_MIPS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Detect x86, 32-bit or 64-bit
|
||||||
|
#if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__i386)
|
||||||
|
#define GEMMLOWP_X86_32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__x86_64__) || defined(_M_X64) || defined(__amd64)
|
||||||
|
#define GEMMLOWP_X86_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_X86_32) || defined(GEMMLOWP_X86_64)
|
||||||
|
#define GEMMLOWP_X86
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Some of our optimized paths use inline assembly and for
|
||||||
|
// now we don't bother enabling some other optimized paths using intrinddics
|
||||||
|
// where we can't use inline assembly paths.
|
||||||
|
#ifdef GEMMLOWP_ALLOW_INLINE_ASM
|
||||||
|
|
||||||
|
// Detect NEON. It's important to check for both tokens.
|
||||||
|
#if (defined __ARM_NEON) || (defined __ARM_NEON__)
|
||||||
|
#define GEMMLOWP_NEON
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Convenience NEON tokens for 32-bit or 64-bit
|
||||||
|
#if defined(GEMMLOWP_NEON) && defined(GEMMLOWP_ARM_32)
|
||||||
|
#define GEMMLOWP_NEON_32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_NEON) && defined(GEMMLOWP_ARM_64)
|
||||||
|
#define GEMMLOWP_NEON_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Detect MIPS MSA.
|
||||||
|
// Limit MSA optimizations to little-endian CPUs for now.
|
||||||
|
// TODO: Perhaps, eventually support MSA optimizations on big-endian CPUs?
|
||||||
|
#if defined(GEMMLOWP_MIPS) && (__mips_isa_rev >= 5) && defined(__mips_msa) && \
|
||||||
|
defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||||
|
#define GEMMLOWP_MSA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Convenience MIPS MSA tokens for 32-bit or 64-bit.
|
||||||
|
#if defined(GEMMLOWP_MSA) && defined(GEMMLOWP_MIPS_32)
|
||||||
|
#define GEMMLOWP_MSA_32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_MSA) && defined(GEMMLOWP_MIPS_64)
|
||||||
|
#define GEMMLOWP_MSA_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// compiler define for AVX2 -D GEMMLOWP_ENABLE_AVX2
|
||||||
|
// Detect AVX2
|
||||||
|
#if defined(__AVX2__) && defined(GEMMLOWP_ENABLE_AVX2)
|
||||||
|
#define GEMMLOWP_AVX2
|
||||||
|
// Detect SSE4.
|
||||||
|
// MSVC does not have __SSE4_1__ macro, but will enable SSE4
|
||||||
|
// when AVX is turned on.
|
||||||
|
#elif defined(__SSE4_1__) || (defined(_MSC_VER) && defined(__AVX__))
|
||||||
|
#define GEMMLOWP_SSE4
|
||||||
|
// Detect SSE3.
|
||||||
|
#elif defined(__SSE3__)
|
||||||
|
#define GEMMLOWP_SSE3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Convenience SSE4 tokens for 32-bit or 64-bit
|
||||||
|
#if defined(GEMMLOWP_SSE4) && defined(GEMMLOWP_X86_32) && \
|
||||||
|
!defined(GEMMLOWP_DISABLE_SSE4)
|
||||||
|
#define GEMMLOWP_SSE4_32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_SSE3) && defined(GEMMLOWP_X86_32)
|
||||||
|
#define GEMMLOWP_SSE3_32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_SSE4) && defined(GEMMLOWP_X86_64) && \
|
||||||
|
!defined(GEMMLOWP_DISABLE_SSE4)
|
||||||
|
#define GEMMLOWP_SSE4_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_SSE3) && defined(GEMMLOWP_X86_64)
|
||||||
|
#define GEMMLOWP_SSE3_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GEMMLOWP_AVX2) && defined(GEMMLOWP_X86_64)
|
||||||
|
#define GEMMLOWP_AVX2_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__has_feature)
|
||||||
|
#if __has_feature(memory_sanitizer)
|
||||||
|
#include <sanitizer/msan_interface.h>
|
||||||
|
#define GEMMLOWP_MARK_MEMORY_AS_INITIALIZED __msan_unpoison
|
||||||
|
#elif __has_feature(address_sanitizer)
|
||||||
|
#include <sanitizer/asan_interface.h>
|
||||||
|
#define GEMMLOWP_MARK_MEMORY_AS_INITIALIZED __asan_unpoison_memory_region
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // GEMMLOWP_ALLOW_INLINE_ASM
|
||||||
|
|
||||||
|
// Detect Android. Don't conflate with ARM - we care about tuning
|
||||||
|
// for non-ARM Android devices too. This can be used in conjunction
|
||||||
|
// with x86 to tune differently for mobile x86 CPUs (Atom) vs. desktop x86 CPUs.
|
||||||
|
#if defined(__ANDROID__) || defined(ANDROID)
|
||||||
|
#define GEMMLOWP_ANDROID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // GEMMLOWP_INTERNAL_DETECT_PLATFORM_H_
|
||||||
11
code/lib/tfmicro/kissfft/COPYING
Normal file
11
code/lib/tfmicro/kissfft/COPYING
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Copyright (c) 2003-2010 Mark Borgerding
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
164
code/lib/tfmicro/kissfft/_kiss_fft_guts.h
Normal file
164
code/lib/tfmicro/kissfft/_kiss_fft_guts.h
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2003-2010, Mark Borgerding
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* kiss_fft.h
|
||||||
|
defines kiss_fft_scalar as either short or a float type
|
||||||
|
and defines
|
||||||
|
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
|
||||||
|
#include "kiss_fft.h"
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#define MAXFACTORS 32
|
||||||
|
/* e.g. an fft of length 128 has 4 factors
|
||||||
|
as far as kissfft is concerned
|
||||||
|
4*4*4*2
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct kiss_fft_state{
|
||||||
|
int nfft;
|
||||||
|
int inverse;
|
||||||
|
int factors[2*MAXFACTORS];
|
||||||
|
kiss_fft_cpx twiddles[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Explanation of macros dealing with complex math:
|
||||||
|
|
||||||
|
C_MUL(m,a,b) : m = a*b
|
||||||
|
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
|
||||||
|
C_SUB( res, a,b) : res = a - b
|
||||||
|
C_SUBFROM( res , a) : res -= a
|
||||||
|
C_ADDTO( res , a) : res += a
|
||||||
|
* */
|
||||||
|
#ifdef FIXED_POINT
|
||||||
|
#if (FIXED_POINT==32)
|
||||||
|
# define FRACBITS 31
|
||||||
|
# define SAMPPROD int64_t
|
||||||
|
#define SAMP_MAX 2147483647
|
||||||
|
#else
|
||||||
|
# define FRACBITS 15
|
||||||
|
# define SAMPPROD int32_t
|
||||||
|
#define SAMP_MAX 32767
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SAMP_MIN -SAMP_MAX
|
||||||
|
|
||||||
|
#if defined(CHECK_OVERFLOW)
|
||||||
|
# define CHECK_OVERFLOW_OP(a,op,b) \
|
||||||
|
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
|
||||||
|
fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
# define smul(a,b) ( (SAMPPROD)(a)*(b) )
|
||||||
|
# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
|
||||||
|
|
||||||
|
# define S_MUL(a,b) sround( smul(a,b) )
|
||||||
|
|
||||||
|
# define C_MUL(m,a,b) \
|
||||||
|
do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
|
||||||
|
(m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
|
||||||
|
|
||||||
|
# define DIVSCALAR(x,k) \
|
||||||
|
(x) = sround( smul( x, SAMP_MAX/k ) )
|
||||||
|
|
||||||
|
# define C_FIXDIV(c,div) \
|
||||||
|
do { DIVSCALAR( (c).r , div); \
|
||||||
|
DIVSCALAR( (c).i , div); }while (0)
|
||||||
|
|
||||||
|
# define C_MULBYSCALAR( c, s ) \
|
||||||
|
do{ (c).r = sround( smul( (c).r , s ) ) ;\
|
||||||
|
(c).i = sround( smul( (c).i , s ) ) ; }while(0)
|
||||||
|
|
||||||
|
#else /* not FIXED_POINT*/
|
||||||
|
|
||||||
|
# define S_MUL(a,b) ( (a)*(b) )
|
||||||
|
#define C_MUL(m,a,b) \
|
||||||
|
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
|
||||||
|
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
|
||||||
|
# define C_FIXDIV(c,div) /* NOOP */
|
||||||
|
# define C_MULBYSCALAR( c, s ) \
|
||||||
|
do{ (c).r *= (s);\
|
||||||
|
(c).i *= (s); }while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CHECK_OVERFLOW_OP
|
||||||
|
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define C_ADD( res, a,b)\
|
||||||
|
do { \
|
||||||
|
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
|
||||||
|
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
|
||||||
|
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
|
||||||
|
}while(0)
|
||||||
|
#define C_SUB( res, a,b)\
|
||||||
|
do { \
|
||||||
|
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
|
||||||
|
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
|
||||||
|
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
|
||||||
|
}while(0)
|
||||||
|
#define C_ADDTO( res , a)\
|
||||||
|
do { \
|
||||||
|
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
|
||||||
|
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
|
||||||
|
(res).r += (a).r; (res).i += (a).i;\
|
||||||
|
}while(0)
|
||||||
|
|
||||||
|
#define C_SUBFROM( res , a)\
|
||||||
|
do {\
|
||||||
|
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
|
||||||
|
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
|
||||||
|
(res).r -= (a).r; (res).i -= (a).i; \
|
||||||
|
}while(0)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef FIXED_POINT
|
||||||
|
# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase))
|
||||||
|
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
|
||||||
|
# define HALF_OF(x) ((x)>>1)
|
||||||
|
#elif defined(USE_SIMD)
|
||||||
|
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
|
||||||
|
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
|
||||||
|
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
|
||||||
|
#else
|
||||||
|
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
|
||||||
|
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
|
||||||
|
# define HALF_OF(x) ((x)*.5)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define kf_cexp(x,phase) \
|
||||||
|
do{ \
|
||||||
|
(x)->r = KISS_FFT_COS(phase);\
|
||||||
|
(x)->i = KISS_FFT_SIN(phase);\
|
||||||
|
}while(0)
|
||||||
|
|
||||||
|
|
||||||
|
/* a debugging function */
|
||||||
|
#define pcpx(c)\
|
||||||
|
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef KISS_FFT_USE_ALLOCA
|
||||||
|
// define this to allow use of alloca instead of malloc for temporary buffers
|
||||||
|
// Temporary buffers are used in two case:
|
||||||
|
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
|
||||||
|
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform.
|
||||||
|
#include <alloca.h>
|
||||||
|
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
|
||||||
|
#define KISS_FFT_TMP_FREE(ptr)
|
||||||
|
#else
|
||||||
|
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
|
||||||
|
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
|
||||||
|
#endif
|
||||||
131
code/lib/tfmicro/kissfft/kiss_fft.h
Normal file
131
code/lib/tfmicro/kissfft/kiss_fft.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#ifndef KISS_FFT_H
|
||||||
|
#define KISS_FFT_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
ATTENTION!
|
||||||
|
If you would like a :
|
||||||
|
-- a utility that will handle the caching of fft objects
|
||||||
|
-- real-only (no imaginary time component ) FFT
|
||||||
|
-- a multi-dimensional FFT
|
||||||
|
-- a command-line utility to perform ffts
|
||||||
|
-- a command-line utility to perform fast-convolution filtering
|
||||||
|
|
||||||
|
Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
|
||||||
|
in the tools/ directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef USE_SIMD
|
||||||
|
# include <xmmintrin.h>
|
||||||
|
# define kiss_fft_scalar __m128
|
||||||
|
#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
|
||||||
|
#define KISS_FFT_FREE _mm_free
|
||||||
|
#else
|
||||||
|
#define KISS_FFT_MALLOC(X) (void*)(0) /* Patched. */
|
||||||
|
#define KISS_FFT_FREE(X) /* Patched. */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Patched automatically by download_dependencies.sh so default is 16 bit.
|
||||||
|
#ifndef FIXED_POINT
|
||||||
|
#define FIXED_POINT (16)
|
||||||
|
#endif
|
||||||
|
// End patch.
|
||||||
|
|
||||||
|
#ifdef FIXED_POINT
|
||||||
|
#include <stdint.h> /* Patched. */
|
||||||
|
#include <sys/types.h>
|
||||||
|
# if (FIXED_POINT == 32)
|
||||||
|
# define kiss_fft_scalar int32_t
|
||||||
|
# else
|
||||||
|
# define kiss_fft_scalar int16_t
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# ifndef kiss_fft_scalar
|
||||||
|
/* default is float */
|
||||||
|
# define kiss_fft_scalar float
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
kiss_fft_scalar r;
|
||||||
|
kiss_fft_scalar i;
|
||||||
|
}kiss_fft_cpx;
|
||||||
|
|
||||||
|
typedef struct kiss_fft_state* kiss_fft_cfg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kiss_fft_alloc
|
||||||
|
*
|
||||||
|
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
|
||||||
|
*
|
||||||
|
* typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
|
||||||
|
*
|
||||||
|
* The return value from fft_alloc is a cfg buffer used internally
|
||||||
|
* by the fft routine or NULL.
|
||||||
|
*
|
||||||
|
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
|
||||||
|
* The returned value should be free()d when done to avoid memory leaks.
|
||||||
|
*
|
||||||
|
* The state can be placed in a user supplied buffer 'mem':
|
||||||
|
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
|
||||||
|
* then the function places the cfg in mem and the size used in *lenmem
|
||||||
|
* and returns mem.
|
||||||
|
*
|
||||||
|
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
|
||||||
|
* then the function returns NULL and places the minimum cfg
|
||||||
|
* buffer size in *lenmem.
|
||||||
|
* */
|
||||||
|
|
||||||
|
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kiss_fft(cfg,in_out_buf)
|
||||||
|
*
|
||||||
|
* Perform an FFT on a complex input buffer.
|
||||||
|
* for a forward FFT,
|
||||||
|
* fin should be f[0] , f[1] , ... ,f[nfft-1]
|
||||||
|
* fout will be F[0] , F[1] , ... ,F[nfft-1]
|
||||||
|
* Note that each element is complex and can be accessed like
|
||||||
|
f[k].r and f[k].i
|
||||||
|
* */
|
||||||
|
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
|
||||||
|
|
||||||
|
/*
|
||||||
|
A more generic version of the above function. It reads its input from every Nth sample.
|
||||||
|
* */
|
||||||
|
void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
|
||||||
|
|
||||||
|
/* If kiss_fft_alloc allocated a buffer, it is one contiguous
|
||||||
|
buffer and can be simply free()d when no longer needed*/
|
||||||
|
#define kiss_fft_free free
|
||||||
|
|
||||||
|
/*
|
||||||
|
Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
|
||||||
|
your compiler output to call this before you exit.
|
||||||
|
*/
|
||||||
|
void kiss_fft_cleanup(void);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
|
||||||
|
*/
|
||||||
|
int kiss_fft_next_fast_size(int n);
|
||||||
|
|
||||||
|
/* for real ffts, we need an even size */
|
||||||
|
#define kiss_fftr_next_fast_size_real(n) \
|
||||||
|
(kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user