<commit_before>// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/nw_shell.h"

#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "content/public/browser/devtools_http_handler.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/nw/src/api/api_messages.h"
#include "content/nw/src/api/app/app.h"
#include "content/nw/src/browser/file_select_helper.h"
#include "content/nw/src/browser/native_window.h"
#include "content/nw/src/browser/shell_devtools_delegate.h"
#include "content/nw/src/browser/shell_javascript_dialog_creator.h"
#include "content/nw/src/common/shell_switches.h"
#include "content/nw/src/media/media_stream_devices_controller.h"
#include "content/nw/src/nw_package.h"
#include "content/nw/src/shell_browser_main_parts.h"
#include "content/nw/src/shell_content_browser_client.h"

namespace content {

std::vector<Shell*> Shell::windows_;

bool Shell::quit_message_loop_ = true;

Shell* Shell::Create(BrowserContext* browser_context,
                     const GURL& url,
                     SiteInstance* site_instance,
                     int routing_id,
                     WebContents* base_web_contents) {
  WebContents* web_contents = WebContents::Create(
      browser_context,
      site_instance,
      routing_id,
      base_web_contents);

  Shell* shell = new Shell(web_contents, GetPackage()->window());
  web_contents->GetController().LoadURL(
      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
  return shell;
}

Shell* Shell::FromRenderViewHost(RenderViewHost* rvh) {
  for (size_t i = 0; i < windows_.size(); ++i) {
    if (windows_[i]->web_contents() &&
        windows_[i]->web_contents()->GetRenderViewHost() == rvh) {
      return windows_[i];
    }
  }
  return NULL;
}

Shell::Shell(WebContents* web_contents, base::DictionaryValue* manifest)
    : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
      is_devtools_(false),
      force_close_(false),
      id_(-1) {
  // Register shell.
  registrar_.Add(this, NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
      Source<WebContents>(web_contents));
  windows_.push_back(this);

  // Add web contents.
  web_contents_.reset(web_contents);
  content::WebContentsObserver::Observe(web_contents);
  web_contents_->SetDelegate(this);

  // Create window.
  window_.reset(nw::NativeWindow::Create(this, manifest));
}

Shell::~Shell() {
  SendEvent("closed");

  for (size_t i = 0; i < windows_.size(); ++i) {
    if (windows_[i] == this) {
      windows_.erase(windows_.begin() + i);

      // Close the devtools window if it has one.
      if (devtools_window_)
        devtools_window_->CloseContents(web_contents());
      break;
    }
  }

  if (windows_.empty() && quit_message_loop_)
    api::App::Quit();
}

void Shell::SendEvent(const std::string& event) {
  if (id() < 0)
    return;

  base::ListValue args;
  web_contents()->GetRenderViewHost()->Send(new ShellViewMsg_Object_On_Event(
      web_contents()->GetRoutingID(), id(), event, args));
}

bool Shell::ShouldCloseWindow() {
  if (id() < 0 || force_close_)
    return true;

  SendEvent("close");
  return false;
}

nw::Package* Shell::GetPackage() {
  ShellContentBrowserClient* browser_client =
      static_cast<ShellContentBrowserClient*>(GetContentClient()->browser());
  return browser_client->shell_browser_main_parts()->package();
}

void Shell::LoadURL(const GURL& url) {
  web_contents_->GetController().LoadURL(
      url,
      Referrer(),
      PAGE_TRANSITION_TYPED,
      std::string());
  web_contents_->Focus();
  window()->SetToolbarButtonEnabled(nw::NativeWindow::BUTTON_FORWARD, false);
}

void Shell::GoBackOrForward(int offset) {
  web_contents_->GetController().GoToOffset(offset);
  web_contents_->Focus();
}

void Shell::Reload() {
  web_contents_->GetController().Reload(false);
  web_contents_->Focus();
}

void Shell::Stop() {
  web_contents_->Stop();
  web_contents_->Focus();
}

void Shell::ReloadOrStop() {
  if (web_contents_->IsLoading())
    Stop();
  else
    Reload();
}

void Shell::ShowDevTools() {
  ShellContentBrowserClient* browser_client =
      static_cast<ShellContentBrowserClient*>(
          GetContentClient()->browser());
  // Reuse reopened one if we have.
  if (devtools_window_) {
    devtools_window_->window_->Focus(true);
    return;
  }

  ShellDevToolsDelegate* delegate =
      browser_client->shell_browser_main_parts()->devtools_delegate();
  GURL url = delegate->devtools_http_handler()->GetFrontendURL(
      web_contents()->GetRenderViewHost());

  // Use our minimum set manifest
  base::DictionaryValue manifest;
  manifest.SetBoolean(switches::kmToolbar, false);
  manifest.SetBoolean(switches::kmFullscreen, false);
  manifest.SetInteger(switches::kmWidth, 600);
  manifest.SetInteger(switches::kmHeight, 500);

  Shell* shell = new Shell(
      WebContents::Create(web_contents()->GetBrowserContext(),
                          NULL, MSG_ROUTING_NONE, NULL),
      &manifest);
  shell->is_devtools_ = true;
  shell->force_close_ = true;
  shell->LoadURL(url);

  // Save devtools window in current shell.
  devtools_window_ = shell->weak_ptr_factory_.GetWeakPtr();
}

void Shell::UpdateDraggableRegions(
      const std::vector<extensions::DraggableRegion>& regions) {
  window()->UpdateDraggableRegions(regions);
}

bool Shell::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(Shell, message)
    IPC_MESSAGE_HANDLER(ShellViewHostMsg_UpdateDraggableRegions,
                        UpdateDraggableRegions)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

WebContents* Shell::OpenURLFromTab(WebContents* source,
                                   const OpenURLParams& params) {
  // The only one we implement for now.
  DCHECK(params.disposition == CURRENT_TAB);
  source->GetController().LoadURL(
      params.url, params.referrer, params.transition, std::string());
  return source;
}

void Shell::LoadingStateChanged(WebContents* source) {
  int current_index = web_contents_->GetController().GetCurrentEntryIndex();
  int max_index = web_contents_->GetController().GetEntryCount() - 1;

  window()->SetToolbarButtonEnabled(nw::NativeWindow::BUTTON_BACK,
                                    current_index > 0);
  window()->SetToolbarButtonEnabled(nw::NativeWindow::BUTTON_FORWARD,
                                    current_index < max_index);
  window()->SetToolbarIsLoading(source->IsLoading());
}

void Shell::ActivateContents(content::WebContents* contents) {
  window()->Focus(true);
}

void Shell::DeactivateContents(content::WebContents* contents) {
  if (windows_.size() == 1) {
    window()->Focus(false);
    return;
  }

  // Move focus to other window
  for (unsigned i = 0; i < windows_.size(); ++i)
    if (windows_[i] != this) {
      windows_[i]->window()->Focus(true);
      break;
    }
}

void Shell::CloseContents(WebContents* source) {
  window()->Close();
}

void Shell::MoveContents(WebContents* source, const gfx::Rect& pos) {
  window()->Move(pos);
}

bool Shell::IsPopupOrPanel(const WebContents* source) const {
  // Treat very window as popup so we can use window operations
  return true;
}

// Window opened by window.open
void Shell::WebContentsCreated(WebContents* source_contents,
                               int64 source_frame_id,
                               const GURL& target_url,
                               WebContents* new_contents) {
  // Create with package's manifest
  scoped_ptr<base::DictionaryValue> manifest(
      GetPackage()->window()->DeepCopy());

  // Get window features
  WebKit::WebWindowFeatures features = new_contents->GetWindowFeatures();
  manifest->SetBoolean(switches::kmResizable, features.resizable);
  manifest->SetBoolean(switches::kmFullscreen, features.fullscreen);
  if (features.widthSet)
    manifest->SetInteger(switches::kmWidth, features.width);
  if (features.heightSet)
    manifest->SetInteger(switches::kmHeight, features.height);
  if (features.xSet)
    manifest->SetInteger(switches::kmX, features.x);
  if (features.ySet)
    manifest->SetInteger(switches::kmY, features.y);

  new Shell(new_contents, manifest.get());
}

void Shell::RunFileChooser(WebContents* web_contents,
                           const content::FileChooserParams& params) {
  FileSelectHelper::RunFileChooser(web_contents, params);
}

void Shell::EnumerateDirectory(WebContents* web_contents,
                               int request_id,
                               const FilePath& path) {
  FileSelectHelper::EnumerateDirectory(web_contents, request_id, path);
}

void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) {
  window()->SetToolbarUrlEntry(web_contents->GetURL().spec());
}

JavaScriptDialogCreator* Shell::GetJavaScriptDialogCreator() {
  if (!dialog_creator_.get())
    dialog_creator_.reset(new ShellJavaScriptDialogCreator());
  return dialog_creator_.get();
}

bool Shell::AddMessageToConsole(WebContents* source,
                                int32 level,
                                const string16& message,
                                int32 line_no,
                                const string16& source_id) {
  return false;
}

void Shell::HandleKeyboardEvent(WebContents* source,
                                const NativeWebKeyboardEvent& event) {
  window()->HandleKeyboardEvent(event);
}

void Shell::RequestMediaAccessPermission(
      content::WebContents* web_contents,
      const content::MediaStreamRequest* request,
      const content::MediaResponseCallback& callback) {
  scoped_ptr<MediaStreamDevicesController>
      controller(new MediaStreamDevicesController(request,
                                                  callback));
  controller->DismissInfoBarAndTakeActionOnSettings();
}

void Shell::Observe(int type,
                    const NotificationSource& source,
                    const NotificationDetails& details) {
  if (type == NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED) {
    std::pair<NavigationEntry*, bool>* title =
        Details<std::pair<NavigationEntry*, bool> >(details).ptr();

    if (title->first) {
      string16 text = title->first->GetTitle();
      window()->SetTitle(UTF16ToUTF8(text));
    }
  }
}

}  // namespace content
<commit_msg>Force closing previous debug session<commit_after>// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/nw_shell.h"

#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "content/public/browser/devtools_agent_host_registry.h"
#include "content/public/browser/devtools_http_handler.h"
#include "content/public/browser/devtools_manager.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/nw/src/api/api_messages.h"
#include "content/nw/src/api/app/app.h"
#include "content/nw/src/browser/file_select_helper.h"
#include "content/nw/src/browser/native_window.h"
#include "content/nw/src/browser/shell_devtools_delegate.h"
#include "content/nw/src/browser/shell_javascript_dialog_creator.h"
#include "content/nw/src/common/shell_switches.h"
#include "content/nw/src/media/media_stream_devices_controller.h"
#include "content/nw/src/nw_package.h"
#include "content/nw/src/shell_browser_main_parts.h"
#include "content/nw/src/shell_content_browser_client.h"

namespace content {

std::vector<Shell*> Shell::windows_;

bool Shell::quit_message_loop_ = true;

Shell* Shell::Create(BrowserContext* browser_context,
                     const GURL& url,
                     SiteInstance* site_instance,
                     int routing_id,
                     WebContents* base_web_contents) {
  WebContents* web_contents = WebContents::Create(
      browser_context,
      site_instance,
      routing_id,
      base_web_contents);

  Shell* shell = new Shell(web_contents, GetPackage()->window());
  web_contents->GetController().LoadURL(
      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
  return shell;
}

Shell* Shell::FromRenderViewHost(RenderViewHost* rvh) {
  for (size_t i = 0; i < windows_.size(); ++i) {
    if (windows_[i]->web_contents() &&
        windows_[i]->web_contents()->GetRenderViewHost() == rvh) {
      return windows_[i];
    }
  }
  return NULL;
}

Shell::Shell(WebContents* web_contents, base::DictionaryValue* manifest)
    : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
      is_devtools_(false),
      force_close_(false),
      id_(-1) {
  // Register shell.
  registrar_.Add(this, NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
      Source<WebContents>(web_contents));
  windows_.push_back(this);

  // Add web contents.
  web_contents_.reset(web_contents);
  content::WebContentsObserver::Observe(web_contents);
  web_contents_->SetDelegate(this);

  // Create window.
  window_.reset(nw::NativeWindow::Create(this, manifest));
}

Shell::~Shell() {
  SendEvent("closed");

  for (size_t i = 0; i < windows_.size(); ++i) {
    if (windows_[i] == this) {
      windows_.erase(windows_.begin() + i);

      // Close the devtools window if it has one.
      if (devtools_window_)
        devtools_window_->CloseContents(web_contents());
      break;
    }
  }

  if (windows_.empty() && quit_message_loop_)
    api::App::Quit();
}

void Shell::SendEvent(const std::string& event) {
  if (id() < 0)
    return;

  base::ListValue args;
  web_contents()->GetRenderViewHost()->Send(new ShellViewMsg_Object_On_Event(
      web_contents()->GetRoutingID(), id(), event, args));
}

bool Shell::ShouldCloseWindow() {
  if (id() < 0 || force_close_)
    return true;

  SendEvent("close");
  return false;
}

nw::Package* Shell::GetPackage() {
  ShellContentBrowserClient* browser_client =
      static_cast<ShellContentBrowserClient*>(GetContentClient()->browser());
  return browser_client->shell_browser_main_parts()->package();
}

void Shell::LoadURL(const GURL& url) {
  web_contents_->GetController().LoadURL(
      url,
      Referrer(),
      PAGE_TRANSITION_TYPED,
      std::string());
  web_contents_->Focus();
  window()->SetToolbarButtonEnabled(nw::NativeWindow::BUTTON_FORWARD, false);
}

void Shell::GoBackOrForward(int offset) {
  web_contents_->GetController().GoToOffset(offset);
  web_contents_->Focus();
}

void Shell::Reload() {
  web_contents_->GetController().Reload(false);
  web_contents_->Focus();
}

void Shell::Stop() {
  web_contents_->Stop();
  web_contents_->Focus();
}

void Shell::ReloadOrStop() {
  if (web_contents_->IsLoading())
    Stop();
  else
    Reload();
}

void Shell::ShowDevTools() {
  ShellContentBrowserClient* browser_client =
      static_cast<ShellContentBrowserClient*>(
          GetContentClient()->browser());
  // Reuse reopened one if we have.
  if (devtools_window_) {
    devtools_window_->window_->Focus(true);
    return;
  }

  RenderViewHost* inspected_rvh = web_contents()->GetRenderViewHost();
  DevToolsAgentHost* agent = DevToolsAgentHostRegistry::GetDevToolsAgentHost(
      inspected_rvh);
  DevToolsManager* manager = DevToolsManager::GetInstance();
  DevToolsClientHost* host = manager->GetDevToolsClientHostFor(agent);

  if (host) {
    // Break remote debugging debugging session.
    manager->UnregisterDevToolsClientHostFor(agent);
  }

  ShellDevToolsDelegate* delegate =
      browser_client->shell_browser_main_parts()->devtools_delegate();
  GURL url = delegate->devtools_http_handler()->GetFrontendURL(inspected_rvh);

  // Use our minimum set manifest
  base::DictionaryValue manifest;
  manifest.SetBoolean(switches::kmToolbar, false);
  manifest.SetBoolean(switches::kmFullscreen, false);
  manifest.SetInteger(switches::kmWidth, 600);
  manifest.SetInteger(switches::kmHeight, 500);

  Shell* shell = new Shell(
      WebContents::Create(web_contents()->GetBrowserContext(),
                          NULL, MSG_ROUTING_NONE, NULL),
      &manifest);
  shell->is_devtools_ = true;
  shell->force_close_ = true;
  shell->LoadURL(url);

  // Save devtools window in current shell.
  devtools_window_ = shell->weak_ptr_factory_.GetWeakPtr();
}

void Shell::UpdateDraggableRegions(
      const std::vector<extensions::DraggableRegion>& regions) {
  window()->UpdateDraggableRegions(regions);
}

bool Shell::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(Shell, message)
    IPC_MESSAGE_HANDLER(ShellViewHostMsg_UpdateDraggableRegions,
                        UpdateDraggableRegions)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

WebContents* Shell::OpenURLFromTab(WebContents* source,
                                   const OpenURLParams& params) {
  // The only one we implement for now.
  DCHECK(params.disposition == CURRENT_TAB);
  source->GetController().LoadURL(
      params.url, params.referrer, params.transition, std::string());
  return source;
}

void Shell::LoadingStateChanged(WebContents* source) {
  int current_index = web_contents_->GetController().GetCurrentEntryIndex();
  int max_index = web_contents_->GetController().GetEntryCount() - 1;

  window()->SetToolbarButtonEnabled(nw::NativeWindow::BUTTON_BACK,
                                    current_index > 0);
  window()->SetToolbarButtonEnabled(nw::NativeWindow::BUTTON_FORWARD,
                                    current_index < max_index);
  window()->SetToolbarIsLoading(source->IsLoading());
}

void Shell::ActivateContents(content::WebContents* contents) {
  window()->Focus(true);
}

void Shell::DeactivateContents(content::WebContents* contents) {
  if (windows_.size() == 1) {
    window()->Focus(false);
    return;
  }

  // Move focus to other window
  for (unsigned i = 0; i < windows_.size(); ++i)
    if (windows_[i] != this) {
      windows_[i]->window()->Focus(true);
      break;
    }
}

void Shell::CloseContents(WebContents* source) {
  window()->Close();
}

void Shell::MoveContents(WebContents* source, const gfx::Rect& pos) {
  window()->Move(pos);
}

bool Shell::IsPopupOrPanel(const WebContents* source) const {
  // Treat very window as popup so we can use window operations
  return true;
}

// Window opened by window.open
void Shell::WebContentsCreated(WebContents* source_contents,
                               int64 source_frame_id,
                               const GURL& target_url,
                               WebContents* new_contents) {
  // Create with package's manifest
  scoped_ptr<base::DictionaryValue> manifest(
      GetPackage()->window()->DeepCopy());

  // Get window features
  WebKit::WebWindowFeatures features = new_contents->GetWindowFeatures();
  manifest->SetBoolean(switches::kmResizable, features.resizable);
  manifest->SetBoolean(switches::kmFullscreen, features.fullscreen);
  if (features.widthSet)
    manifest->SetInteger(switches::kmWidth, features.width);
  if (features.heightSet)
    manifest->SetInteger(switches::kmHeight, features.height);
  if (features.xSet)
    manifest->SetInteger(switches::kmX, features.x);
  if (features.ySet)
    manifest->SetInteger(switches::kmY, features.y);

  new Shell(new_contents, manifest.get());
}

void Shell::RunFileChooser(WebContents* web_contents,
                           const content::FileChooserParams& params) {
  FileSelectHelper::RunFileChooser(web_contents, params);
}

void Shell::EnumerateDirectory(WebContents* web_contents,
                               int request_id,
                               const FilePath& path) {
  FileSelectHelper::EnumerateDirectory(web_contents, request_id, path);
}

void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) {
  window()->SetToolbarUrlEntry(web_contents->GetURL().spec());
}

JavaScriptDialogCreator* Shell::GetJavaScriptDialogCreator() {
  if (!dialog_creator_.get())
    dialog_creator_.reset(new ShellJavaScriptDialogCreator());
  return dialog_creator_.get();
}

bool Shell::AddMessageToConsole(WebContents* source,
                                int32 level,
                                const string16& message,
                                int32 line_no,
                                const string16& source_id) {
  return false;
}

void Shell::HandleKeyboardEvent(WebContents* source,
                                const NativeWebKeyboardEvent& event) {
  window()->HandleKeyboardEvent(event);
}

void Shell::RequestMediaAccessPermission(
      content::WebContents* web_contents,
      const content::MediaStreamRequest* request,
      const content::MediaResponseCallback& callback) {
  scoped_ptr<MediaStreamDevicesController>
      controller(new MediaStreamDevicesController(request,
                                                  callback));
  controller->DismissInfoBarAndTakeActionOnSettings();
}

void Shell::Observe(int type,
                    const NotificationSource& source,
                    const NotificationDetails& details) {
  if (type == NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED) {
    std::pair<NavigationEntry*, bool>* title =
        Details<std::pair<NavigationEntry*, bool> >(details).ptr();

    if (title->first) {
      string16 text = title->first->GetTitle();
      window()->SetTitle(UTF16ToUTF8(text));
    }
  }
}

}  // namespace content
<|endoftext|>


<commit_before>/*
 * PixBinarizer.cpp
 *
 *  Created on: Jul 8, 2014
 *      Author: renard
 */

#include "PixBinarizer.h"
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <cmath>
#include "image_processing.h"

using namespace std;

PixBinarizer::PixBinarizer(bool debug) {
	mDebug= debug;
}

int PixBinarizer::determineThresholdForTile(Pix* pixt, bool debug) {
	l_int32 start = 0, end = 255, i, error, closeSize = 0;
	l_float32 sum, moment, var, y, variance, mean, meanY, countPixels;

	NUMA* norm;
	NUMA* histo = pixGetGrayHistogram(pixt, 1);
	numaSetValue(histo, 255, 0); //ignore white pixels
	error = numaGetNonzeroRange(histo, 0, &start, &end);
	if (end == start || error == 1) {
		numaDestroy(&histo);
		return 0;
	}
	closeSize = end - start;
	if (closeSize % 2 == 0) {
		closeSize++;
	}
	norm = numaNormalizeHistogram(histo, 1);
	/*
	l_float32 median;
	numaGetMedian(histo,&median);
	printf("%2.1f\t", median);
	*/

	l_float32 iMulty;
	for (sum = 0.0, moment = 0.0, var = 0.0, countPixels = 0, i = start;
			i < end; i++) {
		numaGetFValue(norm, i, &y);
		sum += y;
		iMulty = i * y;
		moment += iMulty;
		var += i * iMulty;
		numaGetFValue(histo, i, &y);
		countPixels += y;
	}
	variance = sqrt(var / sum - moment * moment / (sum * sum));
	mean = moment / sum;
	meanY = sum / (end - start);
	int result = 0;
	if (variance < 8) {
		result = 0;
	} else {
		if ((mean * 0.5) < variance) {
			result = mean;// + variance*0.4;
		} else {
			//high variance means we have probably have a very good separation between fore and background
			//in that case the threshold be closer to the mean
			result = mean - sqrt(variance) * 3;
			//result = mean - variance*0.66;
		}
		//result = mean - variance*0.66; //0.2 when median is activated, 0.66 otherwise
	}

	if (debug == true) {
		printf("mean = %f , variance = %f, meanY = %f \n", mean, variance,
				meanY);
		printf("%i ,%i\n", start, end);
		GPLOT *gplot;
		ostringstream name;
		name << mean;
		gplot = gplotCreate(name.str().c_str(), GPLOT_X11, name.str().c_str(),
				"x", "y");
		ostringstream title;
		title << "mean = " << mean << ", " << "variance = " << variance
				<< ", thresh = " << result;
		//gplotAddPlot(gplot, NULL, diff, GPLOT_LINES, "diff histogram");
		gplotAddPlot(gplot, NULL, norm, GPLOT_LINES, title.str().c_str());
		gplotMakeOutput(gplot);
		gplotDestroy(&gplot);
	}
	numaDestroy(&norm);
	numaDestroy(&histo);

	//TODO check std dev of y values. large value > foreground and background, low value > only background
	//idea: narrow histogramm means a high likelyhood for bg
	return result;

}

Pix* PixBinarizer::createEdgeMask(Pix* pixs) {
	L_TIMER timer = startTimerNested();
	ostringstream s;
	Pix* pixConv = pixBlockconvGray(pixs, NULL, 5, 5);
	Pix* pixConvEdges = pixSobelEdgeFilter(pixConv, L_ALL_EDGES);
	pixDestroy(&pixConv);
	pixInvert(pixConvEdges, pixConvEdges);
	s << "sobel edge detection: " << stopTimerNested(timer) << std::endl;
	timer = startTimerNested();
	if(mDebug){
		pixWrite("pixConvEdges.bmp", pixConvEdges, IFF_BMP);
	}

	NUMA* histo = pixGetGrayHistogram(pixConvEdges, 8);
	NUMA* norm = numaNormalizeHistogram(histo, 1.0);
	l_float32 median, mean, variance;
	numaGetHistogramStats(norm, 0, 1, &mean, &median, NULL, &variance);
	numaDestroy(&histo);
	numaDestroy(&norm);

	l_int32 thresh = 0;
	if (variance < 1.0) {
		thresh = 255;
	} else {
		thresh = 254;
	}
	if(mDebug){
		printf("mean = %f, median = %f, std dev = %f, thresh = %i\n", mean, median,
			variance, thresh);
	}

	Pix* pixForeground = pixThresholdToBinary(pixConvEdges, thresh);
	pixDestroy(&pixConvEdges);
	pixCloseSafeBrick(pixForeground,pixForeground,64,64);

	pixWrite("foreground.bmp", pixForeground, IFF_BMP);

	if(mDebug){
		s << "binarization of edge mask: " << stopTimerNested(timer) << std::endl;
	}
	timer = startTimerNested();

	Pix* pixacc = pixBlockconvAccum(pixForeground);
	Pix* pixRank = pixBlockrank(pixForeground, pixacc, 8, 8, 0.1);

	pixDestroy(&pixacc);
	pixDestroy(&pixForeground);
	pixInvert(pixRank, pixRank);
	Pix* pixResult = pixOpenBrick(NULL, pixRank, 10, 10);
	pixDestroy(&pixRank);
	if(mDebug){
		pixWrite("rank.bmp", pixResult, IFF_BMP);
		s << "mask generation: " << stopTimerNested(timer) << std::endl;
		printf("%s", s.str().c_str());
	}

	return pixResult;
}

/**
 * determines and applies a threshold for each tile separately
 */
Pix* PixBinarizer::binarizeTiled(Pix* pixs, const l_uint32 tileSize) {
	L_TIMER timer = startTimerNested();
	l_int32 thresh, w, h;
	Pix* pixb;
	ostringstream s;
	pixGetDimensions(pixs, &w, &h, NULL);
	l_int32 nx = L_MAX(1, w / tileSize);
	l_int32 ny = L_MAX(1, h / tileSize);
	l_int32 ox = L_MAX(1,nx/6);
	l_int32 oy = L_MAX(1,ny/6);
	PIXTILING* pt = pixTilingCreate(pixs, nx, ny, 0, 0, ox, oy);
	Pix* pixth = pixCreate(nx, ny, 8);
	Pix* pixt;
	bool debug = false;
	for (int i = 0; i < ny; i++) {
		for (int j = 0; j < nx; j++) {
			pixt = pixTilingGetTile(pt, i, j);
			if (i == 0 && j == 0) {
				debug = false;
			} else {
				debug = false;
			}
			thresh = determineThresholdForTile(pixt, debug);
			pixSetPixel(pixth, j, i, thresh);
			pixDestroy(&pixt);
		}
	}
	pixTilingDestroy(&pt);
	if(mDebug){
		s << "local threshhold determination: " << stopTimerNested(timer)<< std::endl;
		timer = startTimerNested();
	}

	pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0);
	pixb = pixCreate(w, h, 1);
	l_uint32 val;
	for (int i = 0; i < ny; i++) {
		for (int j = 0; j < nx; j++) {
			pixt = pixTilingGetTile(pt, i, j);
			pixGetPixel(pixth, j, i, &val);
			Pix* pixbTile = pixThresholdToBinary(pixt, val);
			pixTilingPaintTile(pixb, i, j, pixbTile, pt);
			pixDestroy(&pixt);
			pixDestroy(&pixbTile);
		}
	}
	pixTilingDestroy(&pt);
	pixDestroy(&pixth);
	if(mDebug){
		s << "local threshhold application: " << stopTimerNested(timer)<< std::endl;
		printf("%s", s.str().c_str());
	}

	return pixb;
}

void PixBinarizer::binarizeInternal(Pix* pixGrey, Pix* pixhm, Pix** pixb) {
	Pix* pixEdgeMask;
	l_int32 width = pixGetWidth(pixGrey);
	const l_uint32 tileSize = width/10; //size of tile during threshholding
	L_TIMER timer = startTimerNested();
	ostringstream s;

	pixEdgeMask = createEdgeMask(pixGrey);

	pixSetMasked(pixGrey, pixEdgeMask, 255);
	if (pixhm != NULL) {
		//dont allow image mask to cover text
		pixAnd(pixhm, pixhm, pixEdgeMask);
	}

	pixDestroy(&pixEdgeMask);
	if(mDebug){
		s << "text mask creation: " << stopTimerNested(timer) << std::endl;
		printf("%s", s.str().c_str());
		pixDisplay(pixGrey,0,0);
		pixWrite("toThresh.bmp", pixGrey, IFF_BMP);
	}

	/*
	 timer = startTimerNested();
	 Pix* pixMedian = pixRankFilterGray(pixGrey,3,3,0.25);
	 printf("rank filter = %f\n",stopTimerNested(timer));
	 pixWrite("rank.bmp",pixMedian,IFF_BMP);
	 */

	*pixb = binarizeTiled(pixGrey, tileSize);
	//pixDestroy(&pixMedian);

	if (pixhm != NULL) {
		pixSetMasked(*pixb, pixhm, 0);
	}
}

 Pix* PixBinarizer::binarize(Pix* pix){
	 l_int32 depth = pixGetDepth(pix);
	 Pix* pixGrey;
	 Pix* pixtContrast;
	 Pix* pixBinary;
	 switch(depth){
		 case 1:
			 return pixClone(pix);
		 case 8:
			 pixGrey = pixClone(pix);
			 break;
		 case 32:
			 pixGrey = pixConvertRGBToLuminance(pix);
	 }
	 //binarizeInternal(pixGrey, NULL, &pixBinary);

	/* Do combination of contrast norm and sauvola */
	pixtContrast = pixContrastNorm(NULL, pixGrey, 100, 100, 55, 1, 1);
	pixSauvolaBinarizeTiled(pixtContrast, 9, 0.15, 1, 1, NULL, &pixBinary);
//
	pixDestroy(&pixtContrast);
	pixDestroy(&pixGrey);

	return pixBinary;
}

PixBinarizer::~PixBinarizer() {
}

<commit_msg>fix: use original binarisation algorithm<commit_after>/*
 * PixBinarizer.cpp
 *
 *  Created on: Jul 8, 2014
 *      Author: renard
 */

#include "PixBinarizer.h"
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <cmath>
#include "image_processing.h"

using namespace std;

PixBinarizer::PixBinarizer(bool debug) {
	mDebug= debug;
}

int PixBinarizer::determineThresholdForTile(Pix* pixt, bool debug) {
	l_int32 start = 0, end = 255, i, error, closeSize = 0;
	l_float32 sum, moment, var, y, variance, mean, meanY, countPixels;

	NUMA* norm;
	NUMA* histo = pixGetGrayHistogram(pixt, 1);
	numaSetValue(histo, 255, 0); //ignore white pixels
	error = numaGetNonzeroRange(histo, 0, &start, &end);
	if (end == start || error == 1) {
		numaDestroy(&histo);
		return 0;
	}
	closeSize = end - start;
	if (closeSize % 2 == 0) {
		closeSize++;
	}
	norm = numaNormalizeHistogram(histo, 1);
	/*
	l_float32 median;
	numaGetMedian(histo,&median);
	printf("%2.1f\t", median);
	*/

	l_float32 iMulty;
	for (sum = 0.0, moment = 0.0, var = 0.0, countPixels = 0, i = start;
			i < end; i++) {
		numaGetFValue(norm, i, &y);
		sum += y;
		iMulty = i * y;
		moment += iMulty;
		var += i * iMulty;
		numaGetFValue(histo, i, &y);
		countPixels += y;
	}
	variance = sqrt(var / sum - moment * moment / (sum * sum));
	mean = moment / sum;
	meanY = sum / (end - start);
	int result = 0;
	if (variance < 8) {
		result = 0;
	} else {
		if ((mean * 0.5) < variance) {
			result = mean;// + variance*0.4;
		} else {
			//high variance means we have probably have a very good separation between fore and background
			//in that case the threshold be closer to the mean
			result = mean - sqrt(variance) * 3;
			//result = mean - variance*0.66;
		}
		//result = mean - variance*0.66; //0.2 when median is activated, 0.66 otherwise
	}

	if (debug == true) {
		printf("mean = %f , variance = %f, meanY = %f \n", mean, variance,
				meanY);
		printf("%i ,%i\n", start, end);
		GPLOT *gplot;
		ostringstream name;
		name << mean;
		gplot = gplotCreate(name.str().c_str(), GPLOT_X11, name.str().c_str(),
				"x", "y");
		ostringstream title;
		title << "mean = " << mean << ", " << "variance = " << variance
				<< ", thresh = " << result;
		//gplotAddPlot(gplot, NULL, diff, GPLOT_LINES, "diff histogram");
		gplotAddPlot(gplot, NULL, norm, GPLOT_LINES, title.str().c_str());
		gplotMakeOutput(gplot);
		gplotDestroy(&gplot);
	}
	numaDestroy(&norm);
	numaDestroy(&histo);

	//TODO check std dev of y values. large value > foreground and background, low value > only background
	//idea: narrow histogramm means a high likelyhood for bg
	return result;

}

Pix* PixBinarizer::createEdgeMask(Pix* pixs) {
	L_TIMER timer = startTimerNested();
	ostringstream s;
	Pix* pixConv = pixBlockconvGray(pixs, NULL, 5, 5);
	Pix* pixConvEdges = pixSobelEdgeFilter(pixConv, L_ALL_EDGES);
	pixDestroy(&pixConv);
	pixInvert(pixConvEdges, pixConvEdges);
	s << "sobel edge detection: " << stopTimerNested(timer) << std::endl;
	timer = startTimerNested();
	if(mDebug){
		pixWrite("pixConvEdges.bmp", pixConvEdges, IFF_BMP);
	}

	NUMA* histo = pixGetGrayHistogram(pixConvEdges, 8);
	NUMA* norm = numaNormalizeHistogram(histo, 1.0);
	l_float32 median, mean, variance;
	numaGetHistogramStats(norm, 0, 1, &mean, &median, NULL, &variance);
	numaDestroy(&histo);
	numaDestroy(&norm);

	l_int32 thresh = 0;
	if (variance < 1.0) {
		thresh = 255;
	} else {
		thresh = 254;
	}
	if(mDebug){
		printf("mean = %f, median = %f, std dev = %f, thresh = %i\n", mean, median,
			variance, thresh);
	}

	Pix* pixForeground = pixThresholdToBinary(pixConvEdges, thresh);
	pixDestroy(&pixConvEdges);
	pixCloseSafeBrick(pixForeground,pixForeground,64,64);

	pixWrite("foreground.bmp", pixForeground, IFF_BMP);

	if(mDebug){
		s << "binarization of edge mask: " << stopTimerNested(timer) << std::endl;
	}
	timer = startTimerNested();

	Pix* pixacc = pixBlockconvAccum(pixForeground);
	Pix* pixRank = pixBlockrank(pixForeground, pixacc, 8, 8, 0.1);

	pixDestroy(&pixacc);
	pixDestroy(&pixForeground);
	pixInvert(pixRank, pixRank);
	Pix* pixResult = pixOpenBrick(NULL, pixRank, 10, 10);
	pixDestroy(&pixRank);
	if(mDebug){
		pixWrite("rank.bmp", pixResult, IFF_BMP);
		s << "mask generation: " << stopTimerNested(timer) << std::endl;
		printf("%s", s.str().c_str());
	}

	return pixResult;
}

/**
 * determines and applies a threshold for each tile separately
 */
Pix* PixBinarizer::binarizeTiled(Pix* pixs, const l_uint32 tileSize) {
	L_TIMER timer = startTimerNested();
	l_int32 thresh, w, h;
	Pix* pixb;
	ostringstream s;
	pixGetDimensions(pixs, &w, &h, NULL);
	l_int32 nx = L_MAX(1, w / tileSize);
	l_int32 ny = L_MAX(1, h / tileSize);
	l_int32 ox = L_MAX(1,nx/6);
	l_int32 oy = L_MAX(1,ny/6);
	PIXTILING* pt = pixTilingCreate(pixs, nx, ny, 0, 0, ox, oy);
	Pix* pixth = pixCreate(nx, ny, 8);
	Pix* pixt;
	bool debug = false;
	for (int i = 0; i < ny; i++) {
		for (int j = 0; j < nx; j++) {
			pixt = pixTilingGetTile(pt, i, j);
			if (i == 0 && j == 0) {
				debug = false;
			} else {
				debug = false;
			}
			thresh = determineThresholdForTile(pixt, debug);
			pixSetPixel(pixth, j, i, thresh);
			pixDestroy(&pixt);
		}
	}
	pixTilingDestroy(&pt);
	if(mDebug){
		s << "local threshhold determination: " << stopTimerNested(timer)<< std::endl;
		timer = startTimerNested();
	}

	pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0);
	pixb = pixCreate(w, h, 1);
	l_uint32 val;
	for (int i = 0; i < ny; i++) {
		for (int j = 0; j < nx; j++) {
			pixt = pixTilingGetTile(pt, i, j);
			pixGetPixel(pixth, j, i, &val);
			Pix* pixbTile = pixThresholdToBinary(pixt, val);
			pixTilingPaintTile(pixb, i, j, pixbTile, pt);
			pixDestroy(&pixt);
			pixDestroy(&pixbTile);
		}
	}
	pixTilingDestroy(&pt);
	pixDestroy(&pixth);
	if(mDebug){
		s << "local threshhold application: " << stopTimerNested(timer)<< std::endl;
		printf("%s", s.str().c_str());
	}

	return pixb;
}

void PixBinarizer::binarizeInternal(Pix* pixGrey, Pix* pixhm, Pix** pixb) {
	Pix* pixEdgeMask;
	l_int32 width = pixGetWidth(pixGrey);
	const l_uint32 tileSize = width/10; //size of tile during threshholding
	L_TIMER timer = startTimerNested();
	ostringstream s;

	pixEdgeMask = createEdgeMask(pixGrey);

	pixSetMasked(pixGrey, pixEdgeMask, 255);
	if (pixhm != NULL) {
		//dont allow image mask to cover text
		pixAnd(pixhm, pixhm, pixEdgeMask);
	}

	pixDestroy(&pixEdgeMask);
	if(mDebug){
		s << "text mask creation: " << stopTimerNested(timer) << std::endl;
		printf("%s", s.str().c_str());
		pixDisplay(pixGrey,0,0);
		pixWrite("toThresh.bmp", pixGrey, IFF_BMP);
	}

	/*
	 timer = startTimerNested();
	 Pix* pixMedian = pixRankFilterGray(pixGrey,3,3,0.25);
	 printf("rank filter = %f\n",stopTimerNested(timer));
	 pixWrite("rank.bmp",pixMedian,IFF_BMP);
	 */

	*pixb = binarizeTiled(pixGrey, tileSize);
	//pixDestroy(&pixMedian);

	if (pixhm != NULL) {
		pixSetMasked(*pixb, pixhm, 0);
	}
}

 Pix* PixBinarizer::binarize(Pix* pix){
	 l_int32 depth = pixGetDepth(pix);
	 Pix* pixGrey = NULL;
	 Pix* pixtContrast;
	 Pix* pixBinary;
	 switch(depth){
		 case 1:
			 return pixClone(pix);
		 case 8:
			 pixGrey = pixClone(pix);
			 break;
		 case 32:
			 pixGrey = pixConvertRGBToLuminance(pix);
	 }
	 binarizeInternal(pixGrey, NULL, &pixBinary);

	/* Do combination of contrast norm and sauvola */
//	pixtContrast = pixContrastNorm(NULL, pixGrey, 100, 100, 55, 1, 1);
//	pixSauvolaBinarizeTiled(pixtContrast, 9, 0.15, 1, 1, NULL, &pixBinary);

	pixDestroy(&pixtContrast);
	pixDestroy(&pixGrey);

	return pixBinary;
}

PixBinarizer::~PixBinarizer() {
}

<|endoftext|>


<commit_before>// Catch
#include <catch.hpp>
#include <helper.hpp>

// C++ Standard Library
#include <cmath>

// Mantella
#include <mantella>

template <typename ParameterType>
class MockDistanceFunction : public mant::DistanceFunction<ParameterType>{
  public:
    ParameterType getDistanceImplementation(
        const arma::Col<ParameterType>& parameter) const noexcept override;

    arma::Col<ParameterType> getRandomNeighbourImplementation(
        const arma::Col<ParameterType>& parameter,
        const ParameterType& minimalDistance,
        const ParameterType& maximalDistance) const noexcept override;
};

template <>
double MockDistanceFunction<double>::getDistanceImplementation(
    const arma::Col<double>& parameter) const noexcept {
  return arma::norm(parameter, 1);
}

template <>
unsigned int MockDistanceFunction<unsigned int>::getDistanceImplementation(
    const arma::Col<unsigned int>& parameter) const noexcept {
  return arma::accu(parameter);
}

template <ParameterType>
arma::Col<ParameterType> MockDistanceFunction<ParameterType>::getRandomNeighbourImplementation(
    const arma::Col<ParameterType>& parameter,
    const ParameterType& minimalDistance,
    const ParameterType& maximalDistance) const noexcept {
  arma::Col<ParameterType> result = parameter;
  result.at(0) += std::floor(maximalDistance + minimalDistance / 2.0);
  return result;
}

TEST_CASE("DistanceFunction<double>", "") {
  MockDistanceFunction<double> distanceFunction;

  SECTION("Testing getRandomNeighbour(..)") {
    SECTION("Selected random neighbour remains unchanged, considering an unrestricted minimal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<double>>(1), 0.0, 2.3), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<double>>(1), 0.0, 2.3));
    }

    SECTION("Selected random neighbour remains unchanged, considering an unrestricted minimal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<double>>(1), 0.5, 2.3), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<double>>(1), 0.5, 2.3));
    }

    SECTION("Selected random neighbour remains unchanged, considering an equal minimal and maximal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<double>>(1), 3.0, 3.0), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<double>>(1), 1.0, 1.0));
    }

    SECTION("Throws an exception, if minimal distance < 0.") {
      arma::Col<double>::fixed<5> parameter = arma::zeros<arma::Col<double>>(5);
      CHECK_THROWS_AS(distanceFunction.getRandomNeighbour(parameter, -10.3, 4.0), std::logic_error);
    }

    SECTION("Throws an exception, if maximal distance < 0.") {
      arma::Col<double>::fixed<5> parameter = arma::zeros<arma::Col<double>>(5);
      CHECK_THROWS_AS(distanceFunction.getRandomNeighbour(parameter, -8.3, -4.0), std::logic_error);
    }

    SECTION("Throws an exception, if minimal distance > maximal distance.") {
      arma::Col<double>::fixed<5> parameter = arma::zeros<arma::Col<double>>(5);
      CHECK_THROWS_AS(distanceFunction.getRandomNeighbour(parameter, 10.3, 4.0), std::logic_error);
    }
  }

  SECTION("Testing getDistance(...)") {
    SECTION("Distance value remains unchanged.") {
      CHECK(distanceFunction.getDistance(arma::zeros<arma::Col<double>>(1)) == distanceFunction.getDistanceImplementation(arma::zeros<arma::Col<double>>(1)));
      CHECK(distanceFunction.getDistance(arma::Col<double>::fixed<5>({-1.2, 3.0, 1.0, -4.0, 0.0})) == distanceFunction.getDistanceImplementation(arma::Col<double>::fixed<5>({-1.2, 3.0, 1.0, -4.0, 0.0})));
    }
  }
}

TEST_CASE("DistanceFunction<unsigned int>", "") {
  MockDistanceFunction<unsigned int> distanceFunction;

  SECTION("Testing getRandomNeighbour(..)") {
    SECTION("Selected random neighbour remains unchanged, considering an unrestricted minimal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<unsigned int>>(1), 0, 2), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<unsigned int>>(1), 0, 2));
    }

    SECTION("Selected random neighbour remains unchanged, considering an unrestricted minimal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<unsigned int>>(1), 1, 2), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<unsigned int>>(1), 1, 2));
    }

    SECTION("Selected random neighbour remains unchanged, considering an equal minimal and maximal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<unsigned int>>(1), 3, 3), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<unsigned int>>(1), 3, 3));
    }

    SECTION("Throws an exception, if minimal distance > maximal distance.") {
      arma::Col<unsigned int>::fixed<5> parameter = arma::zeros<arma::Col<unsigned int>>(5);
      CHECK_THROWS_AS(distanceFunction.getRandomNeighbour(parameter, 10, 4), std::logic_error);
    }
  }

  SECTION("Testing getDistance(...)") {
    SECTION("Distance value remains unchanged.") {
      CHECK(distanceFunction.getDistance(arma::zeros<arma::Col<unsigned int>>(1)) == distanceFunction.getDistanceImplementation(arma::zeros<arma::Col<unsigned int>>(1)));
      CHECK(distanceFunction.getDistance(arma::Col<unsigned int>::fixed<5>({1, 3, 1, 4, 0})) == distanceFunction.getDistanceImplementation(arma::Col<unsigned int>::fixed<5>({1, 3, 1, 4, 0})));
    }
  }
}
<commit_msg>test: Fixed minor typo<commit_after>// Catch
#include <catch.hpp>
#include <helper.hpp>

// C++ Standard Library
#include <cmath>

// Mantella
#include <mantella>

template <typename ParameterType>
class MockDistanceFunction : public mant::DistanceFunction<ParameterType>{
  public:
    ParameterType getDistanceImplementation(
        const arma::Col<ParameterType>& parameter) const noexcept override;

    arma::Col<ParameterType> getRandomNeighbourImplementation(
        const arma::Col<ParameterType>& parameter,
        const ParameterType& minimalDistance,
        const ParameterType& maximalDistance) const noexcept override;
};

template <>
double MockDistanceFunction<double>::getDistanceImplementation(
    const arma::Col<double>& parameter) const noexcept {
  return arma::norm(parameter, 1);
}

template <>
unsigned int MockDistanceFunction<unsigned int>::getDistanceImplementation(
    const arma::Col<unsigned int>& parameter) const noexcept {
  return arma::accu(parameter);
}

template <typename ParameterType>
arma::Col<ParameterType> MockDistanceFunction<ParameterType>::getRandomNeighbourImplementation(
    const arma::Col<ParameterType>& parameter,
    const ParameterType& minimalDistance,
    const ParameterType& maximalDistance) const noexcept {
  arma::Col<ParameterType> result = parameter;
  result.at(0) += std::floor(maximalDistance + minimalDistance / 2.0);
  return result;
}

TEST_CASE("DistanceFunction<double>", "") {
  MockDistanceFunction<double> distanceFunction;

  SECTION("Testing getRandomNeighbour(..)") {
    SECTION("Selected random neighbour remains unchanged, considering an unrestricted minimal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<double>>(1), 0.0, 2.3), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<double>>(1), 0.0, 2.3));
    }

    SECTION("Selected random neighbour remains unchanged, considering an unrestricted minimal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<double>>(1), 0.5, 2.3), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<double>>(1), 0.5, 2.3));
    }

    SECTION("Selected random neighbour remains unchanged, considering an equal minimal and maximal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<double>>(1), 3.0, 3.0), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<double>>(1), 1.0, 1.0));
    }

    SECTION("Throws an exception, if minimal distance < 0.") {
      arma::Col<double>::fixed<5> parameter = arma::zeros<arma::Col<double>>(5);
      CHECK_THROWS_AS(distanceFunction.getRandomNeighbour(parameter, -10.3, 4.0), std::logic_error);
    }

    SECTION("Throws an exception, if maximal distance < 0.") {
      arma::Col<double>::fixed<5> parameter = arma::zeros<arma::Col<double>>(5);
      CHECK_THROWS_AS(distanceFunction.getRandomNeighbour(parameter, -8.3, -4.0), std::logic_error);
    }

    SECTION("Throws an exception, if minimal distance > maximal distance.") {
      arma::Col<double>::fixed<5> parameter = arma::zeros<arma::Col<double>>(5);
      CHECK_THROWS_AS(distanceFunction.getRandomNeighbour(parameter, 10.3, 4.0), std::logic_error);
    }
  }

  SECTION("Testing getDistance(...)") {
    SECTION("Distance value remains unchanged.") {
      CHECK(distanceFunction.getDistance(arma::zeros<arma::Col<double>>(1)) == distanceFunction.getDistanceImplementation(arma::zeros<arma::Col<double>>(1)));
      CHECK(distanceFunction.getDistance(arma::Col<double>::fixed<5>({-1.2, 3.0, 1.0, -4.0, 0.0})) == distanceFunction.getDistanceImplementation(arma::Col<double>::fixed<5>({-1.2, 3.0, 1.0, -4.0, 0.0})));
    }
  }
}

TEST_CASE("DistanceFunction<unsigned int>", "") {
  MockDistanceFunction<unsigned int> distanceFunction;

  SECTION("Testing getRandomNeighbour(..)") {
    SECTION("Selected random neighbour remains unchanged, considering an unrestricted minimal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<unsigned int>>(1), 0, 2), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<unsigned int>>(1), 0, 2));
    }

    SECTION("Selected random neighbour remains unchanged, considering an unrestricted minimal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<unsigned int>>(1), 1, 2), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<unsigned int>>(1), 1, 2));
    }

    SECTION("Selected random neighbour remains unchanged, considering an equal minimal and maximal distance.") {
      compare(distanceFunction.getRandomNeighbour(arma::zeros<arma::Col<unsigned int>>(1), 3, 3), distanceFunction.getRandomNeighbourImplementation(arma::zeros<arma::Col<unsigned int>>(1), 3, 3));
    }

    SECTION("Throws an exception, if minimal distance > maximal distance.") {
      arma::Col<unsigned int>::fixed<5> parameter = arma::zeros<arma::Col<unsigned int>>(5);
      CHECK_THROWS_AS(distanceFunction.getRandomNeighbour(parameter, 10, 4), std::logic_error);
    }
  }

  SECTION("Testing getDistance(...)") {
    SECTION("Distance value remains unchanged.") {
      CHECK(distanceFunction.getDistance(arma::zeros<arma::Col<unsigned int>>(1)) == distanceFunction.getDistanceImplementation(arma::zeros<arma::Col<unsigned int>>(1)));
      CHECK(distanceFunction.getDistance(arma::Col<unsigned int>::fixed<5>({1, 3, 1, 4, 0})) == distanceFunction.getDistanceImplementation(arma::Col<unsigned int>::fixed<5>({1, 3, 1, 4, 0})));
    }
  }
}
<|endoftext|>


<commit_before>/*
 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
 *
 * File: include/eos/fitting/linear_shape_fitting.hpp
 *
 * Copyright 2014, 2015 Patrik Huber
 *
 * 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.
 */
#pragma once

#ifndef LINEARSHAPEFITTING_HPP_
#define LINEARSHAPEFITTING_HPP_

#include "eos/morphablemodel/MorphableModel.hpp"

//#include "Eigen/LU"

#include "opencv2/core/core.hpp"

#include "boost/optional.hpp"

#include <vector>
#include <cassert>

namespace eos {
	namespace fitting {

/**
 * Fits the shape of a Morphable Model to given 2D landmarks (i.e. estimates the maximum likelihood solution of the shape coefficients) as proposed in [1].
 * It's a linear, closed-form solution fitting of the shape, with regularisation (prior towards the mean).
 *
 * [1] O. Aldrian & W. Smith, Inverse Rendering of Faces with a 3D Morphable Model, PAMI 2013.
 *
 * Note: Using less than the maximum number of coefficients to fit is not thoroughly tested yet and may contain an error.
 * Note: Returns coefficients following standard normal distribution (i.e. all have similar magnitude). Why? Because we fit using the normalised basis?
 * Note: The standard deviations given should be a vector, i.e. different for each landmark. This is not implemented yet.
 *
 * @param[in] morphable_model The Morphable Model whose shape (coefficients) are estimated.
 * @param[in] affine_camera_matrix A 3x4 affine camera matrix from model to screen-space (should probably be of type CV_32FC1 as all our calculations are done with float).
 * @param[in] landmarks 2D landmarks from an image to fit the model to.
 * @param[in] vertex_ids The vertex ids in the model that correspond to the 2D points.
 * @param[in] base_face The base or reference face from where the fitting is started. Usually this would be the models mean face, which is what will be used if the parameter is not explicitly specified.
 * @param[in] lambda The regularisation parameter (weight of the prior towards the mean).
 * @param[in] num_coefficients_to_fit How many shape-coefficients to fit (all others will stay 0). Should be bigger than zero, or boost::none to fit all coefficients.
 * @param[in] detector_standard_deviation The standard deviation of the 2D landmarks given (e.g. of the detector used), in pixels.
 * @param[in] model_standard_deviation The standard deviation of the 3D vertex points in the 3D model, projected to 2D (so the value is in pixels).
 * @return The estimated shape-coefficients (alphas).
 */
inline std::vector<float> fit_shape_to_landmarks_linear(const morphablemodel::MorphableModel& morphable_model, cv::Mat affine_camera_matrix, const std::vector<cv::Vec2f>& landmarks, const std::vector<int>& vertex_ids, Eigen::VectorXf base_face=Eigen::VectorXf(), float lambda=3.0f, boost::optional<int> num_coefficients_to_fit=boost::optional<int>(), boost::optional<float> detector_standard_deviation=boost::optional<float>(), boost::optional<float> model_standard_deviation=boost::optional<float>())
{
	using cv::Mat;
	assert(landmarks.size() == vertex_ids.size());

	int num_coeffs_to_fit = num_coefficients_to_fit.get_value_or(morphable_model.get_shape_model().get_num_principal_components());
	int num_landmarks = static_cast<int>(landmarks.size());

	if (base_face.size() == 0)
	{
		base_face = morphable_model.get_shape_model().get_mean();
	}

	// $\hat{V} \in R^{3N\times m-1}$, subselect the rows of the eigenvector matrix $V$ associated with the $N$ feature points
	// And we insert a row of zeros after every third row, resulting in matrix $\hat{V}_h \in R^{4N\times m-1}$:
	Mat V_hat_h = Mat::zeros(4 * num_landmarks, num_coeffs_to_fit, CV_32FC1);
	int row_index = 0;
	for (int i = 0; i < num_landmarks; ++i) {
		Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> basis_rows_ = morphable_model.get_shape_model().get_rescaled_pca_basis_at_point(vertex_ids[i]); // In the paper, the orthonormal basis might be used? I'm not sure, check it. It's even a mess in the paper. PH 26.5.2014: I think the rescaled basis is fine/better.
		// Above converts to a RowMajor matrix on return - for now, since the core algorithm still uses cv::Mat (and OpenCV stores data in row-major memory order).
		Mat basis_rows = Mat(basis_rows_.rows(), basis_rows_.cols(), CV_32FC1, basis_rows_.data());
		//basisRows.copyTo(V_hat_h.rowRange(rowIndex, rowIndex + 3));
		basis_rows.colRange(0, num_coeffs_to_fit).copyTo(V_hat_h.rowRange(row_index, row_index + 3));
		row_index += 4; // replace 3 rows and skip the 4th one, it has all zeros
	}
	// Form a block diagonal matrix $P \in R^{3N\times 4N}$ in which the camera matrix C (P_Affine, affine_camera_matrix) is placed on the diagonal:
	Mat P = Mat::zeros(3 * num_landmarks, 4 * num_landmarks, CV_32FC1);
	for (int i = 0; i < num_landmarks; ++i) {
		Mat submatrix_to_replace = P.colRange(4 * i, (4 * i) + 4).rowRange(3 * i, (3 * i) + 3);
		affine_camera_matrix.copyTo(submatrix_to_replace);
	}
	// The variances: Add the 2D and 3D standard deviations.
	// If the user doesn't provide them, we choose the following:
	// 2D (detector) standard deviation: In pixel, we follow [1] and choose sqrt(3) as the default value.
	// 3D (model) variance: 0.0f. It only makes sense to set it to something when we have a different variance for different vertices.
	// The 3D variance has to be projected to 2D (for details, see paper [1]) so the units do match up.
	float sigma_squared_2D = std::pow(detector_standard_deviation.get_value_or(std::sqrt(3.0f)), 2) + std::pow(model_standard_deviation.get_value_or(0.0f), 2);
	Mat Omega = Mat::zeros(3 * num_landmarks, 3 * num_landmarks, CV_32FC1);
	for (int i = 0; i < 3 * num_landmarks; ++i) {
		// Sigma(i, i) = sqrt(sigma_squared_2D), but then Omega is Sigma.t() * Sigma (squares the diagonal) - so we just assign 1/sigma_squared_2D to Omega here:
		Omega.at<float>(i, i) = 1.0f / sigma_squared_2D; // the higher the sigma_squared_2D, the smaller the diagonal entries of Sigma will be
	}
	// The landmarks in matrix notation (in homogeneous coordinates), $3N\times 1$
	Mat y = Mat::ones(3 * num_landmarks, 1, CV_32FC1);
	for (int i = 0; i < num_landmarks; ++i) {
		y.at<float>(3 * i, 0) = landmarks[i][0];
		y.at<float>((3 * i) + 1, 0) = landmarks[i][1];
		//y.at<float>((3 * i) + 2, 0) = 1; // already 1, stays (homogeneous coordinate)
	}
	// The mean, with an added homogeneous coordinate (x_1, y_1, z_1, 1, x_2, ...)^t
	Mat v_bar = Mat::ones(4 * num_landmarks, 1, CV_32FC1);
	for (int i = 0; i < num_landmarks; ++i) {
		//cv::Vec4f model_mean = morphable_model.get_shape_model().get_mean_at_point(vertex_ids[i]);
		cv::Vec4f model_mean(base_face(vertex_ids[i] * 3), base_face(vertex_ids[i] * 3 + 1), base_face(vertex_ids[i] * 3 + 2), 1.0f);
		v_bar.at<float>(4 * i, 0) = model_mean[0];
		v_bar.at<float>((4 * i) + 1, 0) = model_mean[1];
		v_bar.at<float>((4 * i) + 2, 0) = model_mean[2];
		//v_bar.at<float>((4 * i) + 3, 0) = 1; // already 1, stays (homogeneous coordinate)
		// note: now that a Vec4f is returned, we could use copyTo?
	}

	// Bring into standard regularised quadratic form with diagonal distance matrix Omega
	Mat A = P * V_hat_h; // camera matrix times the basis
	Mat b = P * v_bar - y; // camera matrix times the mean, minus the landmarks.
	//Mat c_s; // The x, we solve for this! (the variance-normalised shape parameter vector, $c_s = [a_1/sigma_{s,1} , ..., a_m-1/sigma_{s,m-1}]^t$
	//int numShapePc = morphableModel.getShapeModel().getNumberOfPrincipalComponents();
	const int num_shape_pc = num_coeffs_to_fit;
	Mat AtOmegaA = A.t() * Omega * A;
	Mat AtOmegaAReg = AtOmegaA + lambda * Mat::eye(num_shape_pc, num_shape_pc, CV_32FC1);

	// Invert (and perform some sanity checks) using Eigen:
/*	using RowMajorMatrixXf = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
	Eigen::Map<RowMajorMatrixXf> AtOmegaAReg_Eigen(AtOmegaAReg.ptr<float>(), AtOmegaAReg.rows, AtOmegaAReg.cols);
	Eigen::FullPivLU<RowMajorMatrixXf> luOfAtOmegaAReg(AtOmegaAReg_Eigen); // Calculate the full-pivoting LU decomposition of the regularized AtA. Note: We could also try FullPivHouseholderQR if our system is non-minimal (i.e. there are more constraints than unknowns).
	auto rankOfAtOmegaAReg = luOfAtOmegaAReg.rank();
	bool isAtOmegaARegInvertible = luOfAtOmegaAReg.isInvertible();
	float threshold = std::abs(luOfAtOmegaAReg.maxPivot()) * luOfAtOmegaAReg.threshold(); // originaly "2 * ..." but I commented it out
	RowMajorMatrixXf AtARegInv_EigenFullLU = luOfAtOmegaAReg.inverse(); // Note: We should use ::solve() instead
	Mat AtOmegaARegInvFullLU(AtARegInv_EigenFullLU.rows(), AtARegInv_EigenFullLU.cols(), CV_32FC1, AtARegInv_EigenFullLU.data()); // create an OpenCV Mat header for the Eigen data
*/
	// Solve using OpenCV:
	Mat c_s; // Note/Todo: We get coefficients ~ N(0, sigma) I think. They are not multiplied with the eigenvalues.
	bool non_singular = cv::solve(AtOmegaAReg, -A.t() * Omega.t() * b, c_s, cv::DECOMP_SVD); // DECOMP_SVD calculates the pseudo-inverse if the matrix is not invertible.
	// Because we're using SVD, non_singular will always be true. If we were to use e.g. Cholesky, we could return an expected<T>.

	return std::vector<float>(c_s);
};

	} /* namespace fitting */
} /* namespace eos */

#endif /* LINEARSHAPEFITTING_HPP_ */
<commit_msg>Changed linear shape fitting to use Eigen<commit_after>/*
 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
 *
 * File: include/eos/fitting/linear_shape_fitting.hpp
 *
 * Copyright 2014, 2015 Patrik Huber
 *
 * 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.
 */
#pragma once

#ifndef LINEARSHAPEFITTING_HPP_
#define LINEARSHAPEFITTING_HPP_

#include "eos/morphablemodel/MorphableModel.hpp"

#include "Eigen/QR"

#include "opencv2/core/core.hpp"

#include "boost/optional.hpp"

#include <vector>
#include <cassert>

namespace eos {
	namespace fitting {

/**
 * Fits the shape of a Morphable Model to given 2D landmarks (i.e. estimates the maximum likelihood solution of the shape coefficients) as proposed in [1].
 * It's a linear, closed-form solution fitting of the shape, with regularisation (prior towards the mean).
 *
 * [1] O. Aldrian & W. Smith, Inverse Rendering of Faces with a 3D Morphable Model, PAMI 2013.
 *
 * Note: Using less than the maximum number of coefficients to fit is not thoroughly tested yet and may contain an error.
 * Note: Returns coefficients following standard normal distribution (i.e. all have similar magnitude). Why? Because we fit using the normalised basis?
 * Note: The standard deviations given should be a vector, i.e. different for each landmark. This is not implemented yet.
 *
 * @param[in] morphable_model The Morphable Model whose shape (coefficients) are estimated.
 * @param[in] affine_camera_matrix A 3x4 affine camera matrix from model to screen-space (should probably be of type CV_32FC1 as all our calculations are done with float).
 * @param[in] landmarks 2D landmarks from an image to fit the model to.
 * @param[in] vertex_ids The vertex ids in the model that correspond to the 2D points.
 * @param[in] base_face The base or reference face from where the fitting is started. Usually this would be the models mean face, which is what will be used if the parameter is not explicitly specified.
 * @param[in] lambda The regularisation parameter (weight of the prior towards the mean).
 * @param[in] num_coefficients_to_fit How many shape-coefficients to fit (all others will stay 0). Should be bigger than zero, or boost::none to fit all coefficients.
 * @param[in] detector_standard_deviation The standard deviation of the 2D landmarks given (e.g. of the detector used), in pixels.
 * @param[in] model_standard_deviation The standard deviation of the 3D vertex points in the 3D model, projected to 2D (so the value is in pixels).
 * @return The estimated shape-coefficients (alphas).
 */
inline std::vector<float> fit_shape_to_landmarks_linear(const morphablemodel::MorphableModel& morphable_model, cv::Mat affine_camera_matrix, const std::vector<cv::Vec2f>& landmarks, const std::vector<int>& vertex_ids, Eigen::VectorXf base_face=Eigen::VectorXf(), float lambda=3.0f, boost::optional<int> num_coefficients_to_fit=boost::optional<int>(), boost::optional<float> detector_standard_deviation=boost::optional<float>(), boost::optional<float> model_standard_deviation=boost::optional<float>())
{
	assert(landmarks.size() == vertex_ids.size());

	using Eigen::VectorXf;
	using Eigen::MatrixXf;

	int num_coeffs_to_fit = num_coefficients_to_fit.get_value_or(morphable_model.get_shape_model().get_num_principal_components());
	int num_landmarks = static_cast<int>(landmarks.size());

	if (base_face.size() == 0)
	{
		base_face = morphable_model.get_shape_model().get_mean();
	}

	// $\hat{V} \in R^{3N\times m-1}$, subselect the rows of the eigenvector matrix $V$ associated with the $N$ feature points
	// And we insert a row of zeros after every third row, resulting in matrix $\hat{V}_h \in R^{4N\times m-1}$:
	MatrixXf V_hat_h = MatrixXf::Zero(4 * num_landmarks, num_coeffs_to_fit);
	int row_index = 0;
	for (int i = 0; i < num_landmarks; ++i) {
		MatrixXf basis_rows_ = morphable_model.get_shape_model().get_rescaled_pca_basis_at_point(vertex_ids[i]); // In the paper, the orthonormal basis might be used? I'm not sure, check it. It's even a mess in the paper. PH 26.5.2014: I think the rescaled basis is fine/better.
		V_hat_h.block(row_index, 0, 3, V_hat_h.cols()) = basis_rows_.block(0, 0, basis_rows_.rows(), num_coeffs_to_fit);
		row_index += 4; // replace 3 rows and skip the 4th one, it has all zeros
	}
	// Form a block diagonal matrix $P \in R^{3N\times 4N}$ in which the camera matrix C (P_Affine, affine_camera_matrix) is placed on the diagonal:
	MatrixXf P = MatrixXf::Zero(3 * num_landmarks, 4 * num_landmarks);
	for (int i = 0; i < num_landmarks; ++i) {
		using RowMajorMatrixXf = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
		P.block(3 * i, 4 * i, 3, 4) = Eigen::Map<RowMajorMatrixXf>(affine_camera_matrix.ptr<float>(), affine_camera_matrix.rows, affine_camera_matrix.cols);
	}
	// The variances: Add the 2D and 3D standard deviations.
	// If the user doesn't provide them, we choose the following:
	// 2D (detector) standard deviation: In pixel, we follow [1] and choose sqrt(3) as the default value.
	// 3D (model) variance: 0.0f. It only makes sense to set it to something when we have a different variance for different vertices.
	// The 3D variance has to be projected to 2D (for details, see paper [1]) so the units do match up.
	float sigma_squared_2D = std::pow(detector_standard_deviation.get_value_or(std::sqrt(3.0f)), 2) + std::pow(model_standard_deviation.get_value_or(0.0f), 2);
	MatrixXf Omega = MatrixXf::Zero(3 * num_landmarks, 3 * num_landmarks);
	for (int i = 0; i < 3 * num_landmarks; ++i) {
		// Sigma(i, i) = sqrt(sigma_squared_2D), but then Omega is Sigma.t() * Sigma (squares the diagonal) - so we just assign 1/sigma_squared_2D to Omega here:
		Omega(i, i) = 1.0f / sigma_squared_2D;
	}
	// The landmarks in matrix notation (in homogeneous coordinates), $3N\times 1$
	VectorXf y = VectorXf::Ones(3 * num_landmarks);
	for (int i = 0; i < num_landmarks; ++i) {
		y(3 * i) = landmarks[i][0];
		y((3 * i) + 1) = landmarks[i][1];
		//y((3 * i) + 2) = 1; // already 1, stays (homogeneous coordinate)
	}
	// The mean, with an added homogeneous coordinate (x_1, y_1, z_1, 1, x_2, ...)^t
	VectorXf v_bar = VectorXf::Ones(4 * num_landmarks);
	for (int i = 0; i < num_landmarks; ++i) {
		const cv::Vec4f model_mean(base_face(vertex_ids[i] * 3), base_face(vertex_ids[i] * 3 + 1), base_face(vertex_ids[i] * 3 + 2), 1.0f);
		v_bar(4 * i) = model_mean[0];
		v_bar((4 * i) + 1) = model_mean[1];
		v_bar((4 * i) + 2) = model_mean[2];
		//v_bar((4 * i) + 3) = 1; // already 1, stays (homogeneous coordinate)
	}

	// Bring into standard regularised quadratic form with diagonal distance matrix Omega:
	const MatrixXf A = P * V_hat_h; // camera matrix times the basis
	const MatrixXf b = P * v_bar - y; // camera matrix times the mean, minus the landmarks
	const MatrixXf AtOmegaAReg = A.transpose() * Omega * A + lambda * Eigen::MatrixXf::Identity(num_coeffs_to_fit, num_coeffs_to_fit);
	const MatrixXf rhs = -A.transpose() * Omega.transpose() * b;
	// c_s: The 'x' that we solve for. (The variance-normalised shape parameter vector, $c_s = [a_1/sigma_{s,1} , ..., a_m-1/sigma_{s,m-1}]^t$.)
	// We get coefficients ~ N(0, 1), because we're fitting with the rescaled basis. The coefficients are not multiplied with their eigenvalues.
	const VectorXf c_s = AtOmegaAReg.colPivHouseholderQr().solve(rhs);

	return std::vector<float>(c_s.data(), c_s.data() + c_s.size());
};

	} /* namespace fitting */
} /* namespace eos */

#endif /* LINEARSHAPEFITTING_HPP_ */
<|endoftext|>


<commit_before>#ifndef TEST_VECTOR_DS_HPP
#define TEST_VECTOR_DS_HPP

#include <boost/test/unit_test.hpp>
#include <iostream>

#include "types/vector_ds.hpp"

int ITEM_COUNTER = 0;

class Item {
public:
    Item() : Item(-1) {}
    Item(const int& val) : value(val) {
        id = ITEM_COUNTER;
        ++ITEM_COUNTER;

        std::cout << "Item() #" << id << ": " << value << std::endl;
    }

    Item(const Item& other) : Item(other.value) {
//        std::cout << "In copy constructor" << std::endl;
    }

    ~Item() {
        std::cout << "Destruct Item #" << id << ", value = " << value <<
                     std::endl;
    }

    Item& operator=(const Item& other)
    {
        std::cout << "In copy assignment operator" << std::endl;
        this->value = other.value;
        return (*this);
    }

    friend bool operator==(const Item& left, const Item& right);

public:
    int value;

private:
    int id;
};

bool operator==(const Item& left, const Item& right)
{
    return left.value == right.value;
}

BOOST_AUTO_TEST_CASE(test_vds_create_vector)
{
    DS::Vector<int> vector;
    BOOST_CHECK(true == vector.isEmpty());
    BOOST_CHECK(0 == vector.size());
    BOOST_CHECK(16 == vector.capacity());
}

BOOST_AUTO_TEST_CASE(test_vds_create_with_size)
{
    DS::Vector<int> vector(3);
    BOOST_CHECK(false == vector.isEmpty());
    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(0 == vector.at(0));
    BOOST_CHECK(0 == vector.at(1));
    BOOST_CHECK(0 == vector.at(2));
}

BOOST_AUTO_TEST_CASE(test_vds_create_with_size_and_default_value)
{
    Item item(42);
    DS::Vector<Item> vector(5, item);
    BOOST_CHECK(false == vector.isEmpty());
    BOOST_CHECK(5 == vector.size());
    BOOST_CHECK(item == vector.at(0));
    BOOST_CHECK(item == vector.at(1));
    BOOST_CHECK(item == vector.at(2));
    BOOST_CHECK(item == vector.at(3));
    BOOST_CHECK(item == vector.at(4));
}

BOOST_AUTO_TEST_CASE(test_vds_add_elements)
{
    DS::Vector<Item> vector(0);
    BOOST_CHECK(vector.isEmpty());
    BOOST_CHECK(0 == vector.capacity());

    vector.pushBack(1);
    vector.pushBack(2);
    vector.pushBack(3);
    vector.pushBack(4);

    BOOST_CHECK(false == vector.isEmpty());
    BOOST_CHECK(4 == vector.size());
    BOOST_CHECK(Item(1) == vector.at(0));
    BOOST_CHECK(Item(2) == vector.at(1));
    BOOST_CHECK(Item(3) == vector.at(2));
    BOOST_CHECK(Item(4) == vector.at(3));
}

/*
BOOST_AUTO_TEST_CASE(test_vds_clear)
{
    DS::Vector<int> vector;
    list.clear();
    BOOST_CHECK(true == vector.isEmpty());
    BOOST_CHECK(0 == vector.size());

    list.addLast(1);
    list.addLast(2);
    list.clear();

    BOOST_CHECK(true == vector.isEmpty());
    BOOST_CHECK(0 == vector.size());
}

BOOST_AUTO_TEST_CASE(test_vds_first_last)
{
    DS::Vector<unsigned int> vector;

    BOOST_CHECK_THROW(vector.first(), std::length_error);
    BOOST_CHECK_THROW(vector.last(), std::length_error);

    list.addLast(186);
    list.addLast(187);
    list.addLast(28786);

    BOOST_CHECK(186 == vector.first());
    BOOST_CHECK(28786 == vector.last());
}

BOOST_AUTO_TEST_CASE(test_vds_at)
{
    DS::Vector<unsigned int> vector;

    BOOST_CHECK_THROW(vector.at(0), std::length_error);

    list.addLast(186);
    list.addLast(187);
    list.addLast(28786);

    BOOST_CHECK_THROW(vector.at(-156), std::runtime_error);
    BOOST_CHECK_THROW(vector.at(10), std::runtime_error);

    BOOST_CHECK(186 == vector.at(0));
    BOOST_CHECK(187 == vector.at(1));
    BOOST_CHECK(28786 == vector.at(2));
}

BOOST_AUTO_TEST_CASE(test_vds_add_first)
{
    DS::Vector<unsigned int> vector;
    list.addFirst(186);
    list.addLast(187);
    list.addFirst(28786);

    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(28786 == vector.first());
    BOOST_CHECK(186 == vector.at(1));
    BOOST_CHECK(187 == vector.last());
}

BOOST_AUTO_TEST_CASE(test_vds_add_last)
{
    DS::Vector<unsigned int> vector;
    list.addLast(186);
    list.addLast(187);

    BOOST_CHECK(186 == vector.first());
    BOOST_CHECK(187 == vector.last());
}

BOOST_AUTO_TEST_CASE(test_vds_insert)
{
    DS::Vector<unsigned int> vector;
    list.insert(-100, 1);
    list.insert(1500, 2);
    list.insert(0, 3);
    list.insert(1, 4);

    BOOST_CHECK(3 == vector.at(0));
    BOOST_CHECK(4 == vector.at(1));
    BOOST_CHECK(1 == vector.at(2));
    BOOST_CHECK(2 == vector.at(3));
}

BOOST_AUTO_TEST_CASE(test_vds_remove)
{
    DS::Vector<int> vector;
    list.remove(0);
    list.addLast(-100);
    list.addLast(1500);
    list.addLast(0);
    list.addLast(1);

    list.remove(-100);
    list.remove(1501);

    list.remove(1);
    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(-100 == vector.at(0));
    BOOST_CHECK(0 == vector.at(1));
    BOOST_CHECK(1 == vector.at(2));

    list.remove(2);
    BOOST_CHECK(2 == vector.size());
    BOOST_CHECK(-100 == vector.at(0));
    BOOST_CHECK(0 == vector.at(1));

    list.remove(0);
    BOOST_CHECK(1 == vector.size());
    BOOST_CHECK(0 == vector.at(0));

    list.remove(0);
    BOOST_CHECK(0 == vector.size());
    BOOST_CHECK(true == vector.isEmpty());
}

BOOST_AUTO_TEST_CASE(test_vds_remove_first)
{
    DS::Vector<unsigned int> vector;
    list.addLast(1);
    list.addLast(2);
    list.addLast(3);
    list.addLast(4);

    list.removeFirst();
    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(2 == vector.at(0));
    BOOST_CHECK(3 == vector.at(1));
    BOOST_CHECK(4 == vector.at(2));

    list.removeFirst();
    BOOST_CHECK(2 == vector.size());
    BOOST_CHECK(3 == vector.at(0));
    BOOST_CHECK(4 == vector.at(1));

    list.removeFirst();
    BOOST_CHECK(1 == vector.size());
    BOOST_CHECK(4 == vector.at(0));

    list.removeFirst();
    BOOST_CHECK(0 == vector.size());
}

BOOST_AUTO_TEST_CASE(test_vds_remove_last)
{
    DS::Vector<unsigned int> vector;
    list.addLast(1);
    list.addLast(2);
    list.addLast(3);
    list.addLast(4);

    list.removeLast();
    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(1 == vector.at(0));
    BOOST_CHECK(2 == vector.at(1));
    BOOST_CHECK(3 == vector.at(2));

    list.removeLast();
    BOOST_CHECK(2 == vector.size());
    BOOST_CHECK(1 == vector.at(0));
    BOOST_CHECK(2 == vector.at(1));

    list.removeLast();
    BOOST_CHECK(1 == vector.size());
    BOOST_CHECK(1 == vector.at(0));

    list.removeLast();
    BOOST_CHECK(0 == vector.size());
}
*/

#endif // TEST_VECTOR_DS_HPP
<commit_msg>Add tests for Vector<commit_after>#ifndef TEST_VECTOR_DS_HPP
#define TEST_VECTOR_DS_HPP

#include <boost/test/unit_test.hpp>
#include <iostream>

#include "types/vector_ds.hpp"

int ITEM_COUNTER = 0;

class Item {
public:
    Item() : Item(-1) {}
    Item(const int& val) : value(val) {
        id = ITEM_COUNTER;
        ++ITEM_COUNTER;

        std::cout << "Item() #" << id << ": " << value << std::endl;
    }

    Item(const Item& other) : Item(other.value) {
//        std::cout << "In copy constructor" << std::endl;
    }

    ~Item() {
        std::cout << "Destruct Item #" << id << ", value = " << value <<
                     std::endl;
    }

    Item& operator=(const Item& other)
    {
        std::cout << "In copy assignment operator" << std::endl;
        this->value = other.value;
        return (*this);
    }

    friend bool operator==(const Item& left, const Item& right);

public:
    int value;

private:
    int id;
};

bool operator==(const Item& left, const Item& right)
{
    return left.value == right.value;
}

BOOST_AUTO_TEST_CASE(test_vds_create_vector)
{
    DS::Vector<int> vector;
    BOOST_CHECK(true == vector.isEmpty());
    BOOST_CHECK(0 == vector.size());
    BOOST_CHECK(16 == vector.capacity());
}

BOOST_AUTO_TEST_CASE(test_vds_create_with_size)
{
    DS::Vector<int> vector(3);
    BOOST_CHECK(false == vector.isEmpty());
    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(0 == vector.at(0));
    BOOST_CHECK(0 == vector.at(1));
    BOOST_CHECK(0 == vector.at(2));
}

BOOST_AUTO_TEST_CASE(test_vds_create_with_size_and_default_value)
{
    Item item(42);
    DS::Vector<Item> vector(5, item);
    BOOST_CHECK(false == vector.isEmpty());
    BOOST_CHECK(5 == vector.size());
    BOOST_CHECK(item == vector.at(0));
    BOOST_CHECK(item == vector.at(1));
    BOOST_CHECK(item == vector.at(2));
    BOOST_CHECK(item == vector.at(3));
    BOOST_CHECK(item == vector.at(4));
}

BOOST_AUTO_TEST_CASE(test_vds_add_elements)
{
    DS::Vector<Item> vector(0);
    BOOST_CHECK(vector.isEmpty());
    BOOST_CHECK(0 == vector.capacity());

    vector.pushBack(1);
    vector.pushBack(2);
    vector.pushBack(3);
    vector.pushBack(4);

    BOOST_CHECK(false == vector.isEmpty());
    BOOST_CHECK(4 == vector.size());
    BOOST_CHECK(Item(1) == vector.at(0));
    BOOST_CHECK(Item(2) == vector.at(1));
    BOOST_CHECK(Item(3) == vector.at(2));
    BOOST_CHECK(Item(4) == vector.at(3));
}

BOOST_AUTO_TEST_CASE(test_vds_clear)
{
    DS::Vector<Item> vector;
    vector.clear();
    BOOST_CHECK(true == vector.isEmpty());
    BOOST_CHECK(0 == vector.size());
    BOOST_CHECK(0 == vector.capacity());

    vector.pushBack(1);
    vector.pushBack(2);
    vector.clear();

    BOOST_CHECK(true == vector.isEmpty());
    BOOST_CHECK(0 == vector.size());
    BOOST_CHECK(0 == vector.capacity());
}

BOOST_AUTO_TEST_CASE(test_vds_at)
{
    DS::Vector<Item> vector;

    BOOST_CHECK_THROW(vector.at(0), std::out_of_range);

    vector.pushBack(186);
    vector.pushBack(187);
    vector.pushBack(28786);

    BOOST_CHECK_THROW(vector.at(156), std::out_of_range);
    BOOST_CHECK_THROW(vector.at(10), std::out_of_range);

    BOOST_CHECK(Item(186) == vector.at(0));
    BOOST_CHECK(Item(187) == vector.at(1));
    BOOST_CHECK(Item(28786) == vector.at(2));

    vector.clear();
    BOOST_CHECK_THROW(vector.at(0), std::out_of_range);
}

BOOST_AUTO_TEST_CASE(test_vds_prepend)
{
    DS::Vector<unsigned int> vector;
    vector.prepend(186);
    vector.prepend(187);
    vector.prepend(28786);

    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(28786 == vector.at(0));
    BOOST_CHECK(187 == vector.at(1));
    BOOST_CHECK(186 == vector.at(2));
}

/*
BOOST_AUTO_TEST_CASE(test_vds_insert)
{
    DS::Vector<unsigned int> vector;
    list.insert(-100, 1);
    list.insert(1500, 2);
    list.insert(0, 3);
    list.insert(1, 4);

    BOOST_CHECK(3 == vector.at(0));
    BOOST_CHECK(4 == vector.at(1));
    BOOST_CHECK(1 == vector.at(2));
    BOOST_CHECK(2 == vector.at(3));
}

BOOST_AUTO_TEST_CASE(test_vds_remove)
{
    DS::Vector<int> vector;
    list.remove(0);
    list.addLast(-100);
    list.addLast(1500);
    list.addLast(0);
    list.addLast(1);

    list.remove(-100);
    list.remove(1501);

    list.remove(1);
    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(-100 == vector.at(0));
    BOOST_CHECK(0 == vector.at(1));
    BOOST_CHECK(1 == vector.at(2));

    list.remove(2);
    BOOST_CHECK(2 == vector.size());
    BOOST_CHECK(-100 == vector.at(0));
    BOOST_CHECK(0 == vector.at(1));

    list.remove(0);
    BOOST_CHECK(1 == vector.size());
    BOOST_CHECK(0 == vector.at(0));

    list.remove(0);
    BOOST_CHECK(0 == vector.size());
    BOOST_CHECK(true == vector.isEmpty());
}

BOOST_AUTO_TEST_CASE(test_vds_remove_first)
{
    DS::Vector<unsigned int> vector;
    list.addLast(1);
    list.addLast(2);
    list.addLast(3);
    list.addLast(4);

    list.removeFirst();
    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(2 == vector.at(0));
    BOOST_CHECK(3 == vector.at(1));
    BOOST_CHECK(4 == vector.at(2));

    list.removeFirst();
    BOOST_CHECK(2 == vector.size());
    BOOST_CHECK(3 == vector.at(0));
    BOOST_CHECK(4 == vector.at(1));

    list.removeFirst();
    BOOST_CHECK(1 == vector.size());
    BOOST_CHECK(4 == vector.at(0));

    list.removeFirst();
    BOOST_CHECK(0 == vector.size());
}

BOOST_AUTO_TEST_CASE(test_vds_remove_last)
{
    DS::Vector<unsigned int> vector;
    list.addLast(1);
    list.addLast(2);
    list.addLast(3);
    list.addLast(4);

    list.removeLast();
    BOOST_CHECK(3 == vector.size());
    BOOST_CHECK(1 == vector.at(0));
    BOOST_CHECK(2 == vector.at(1));
    BOOST_CHECK(3 == vector.at(2));

    list.removeLast();
    BOOST_CHECK(2 == vector.size());
    BOOST_CHECK(1 == vector.at(0));
    BOOST_CHECK(2 == vector.at(1));

    list.removeLast();
    BOOST_CHECK(1 == vector.size());
    BOOST_CHECK(1 == vector.at(0));

    list.removeLast();
    BOOST_CHECK(0 == vector.size());
}
*/

#endif // TEST_VECTOR_DS_HPP
<|endoftext|>


<commit_before>/**
 *  @file ReactorFactory.cpp
 */
// Copyright 2006  California Institute of Technology

#include "cantera/zeroD/ReactorFactory.h"

#include "cantera/zeroD/Reservoir.h"
#include "cantera/zeroD/Reactor.h"
#include "cantera/zeroD/FlowReactor.h"
#include "cantera/zeroD/ConstPressureReactor.h"

using namespace std;
namespace Cantera
{

ReactorFactory* ReactorFactory::s_factory = 0;
mutex_t ReactorFactory::reactor_mutex;

static int ntypes = 4;
static string _types[] = {"Reservoir", "Reactor", "ConstPressureReactor",
                          "FlowReactor"
                         };

// these constants are defined in ReactorBase.h
static int _itypes[]   = {ReservoirType, ReactorType, FlowReactorType,
                          ConstPressureReactorType
                         };

/**
 * This method returns a new instance of a subclass of ThermoPhase
 */
ReactorBase* ReactorFactory::newReactor(string reactorType)
{

    int ir=-1;

    for (int n = 0; n < ntypes; n++) {
        if (reactorType == _types[n]) {
            ir = _itypes[n];
        }
    }

    return newReactor(ir);
}

ReactorBase* ReactorFactory::newReactor(int ir)
{
    switch (ir) {
    case ReservoirType:
        return new Reservoir();
    case ReactorType:
        return new Reactor();
    case FlowReactorType:
        return new FlowReactor();
    case ConstPressureReactorType:
        return new ConstPressureReactor();
    default:
        throw Cantera::CanteraError("ReactorFactory::newReactor",
                                    "unknown reactor type!");
    }
    return 0;
}

}

<commit_msg>Fixed correspondence of reactor type string names to integer constants<commit_after>/**
 *  @file ReactorFactory.cpp
 */
// Copyright 2006  California Institute of Technology

#include "cantera/zeroD/ReactorFactory.h"

#include "cantera/zeroD/Reservoir.h"
#include "cantera/zeroD/Reactor.h"
#include "cantera/zeroD/FlowReactor.h"
#include "cantera/zeroD/ConstPressureReactor.h"

using namespace std;
namespace Cantera
{

ReactorFactory* ReactorFactory::s_factory = 0;
mutex_t ReactorFactory::reactor_mutex;

static int ntypes = 4;
static string _types[] = {"Reservoir", "Reactor", "ConstPressureReactor",
                          "FlowReactor"
                         };

// these constants are defined in ReactorBase.h
static int _itypes[]   = {ReservoirType, ReactorType, ConstPressureReactorType,
                          FlowReactorType
                         };

/**
 * This method returns a new instance of a subclass of ThermoPhase
 */
ReactorBase* ReactorFactory::newReactor(string reactorType)
{

    int ir=-1;

    for (int n = 0; n < ntypes; n++) {
        if (reactorType == _types[n]) {
            ir = _itypes[n];
        }
    }

    return newReactor(ir);
}

ReactorBase* ReactorFactory::newReactor(int ir)
{
    switch (ir) {
    case ReservoirType:
        return new Reservoir();
    case ReactorType:
        return new Reactor();
    case FlowReactorType:
        return new FlowReactor();
    case ConstPressureReactorType:
        return new ConstPressureReactor();
    default:
        throw Cantera::CanteraError("ReactorFactory::newReactor",
                                    "unknown reactor type!");
    }
    return 0;
}

}

<|endoftext|>


<commit_before>/*
 *  Mod.cpp
 *  OpenLieroX
 *
 *  Created by Albert Zeyer on 08.12.09.
 *  code under LGPL
 *
 */

#include <boost/bind.hpp>
#include "Mod.h"
#include "CGameScript.h"
#include "FindFile.h"
#include "Options.h"
#include "util/macros.h"
#include "ConfigHandler.h"
#include "DeprecatedGUI/CCombobox.h"
#include "gui/List.h"

ModInfo infoForMod(const std::string& f, bool absolute) {
	std::string name;
	ModInfo info;
	CGameScript::CheckFile(f, name, absolute, &info);
	return info;
}

std::string modName(const std::string& f) {
	ModInfo info = infoForMod(f);
	if(info.valid)
		return info.name;
	return "";
}

static GameSettingsPresetInfo settingsInfo(const std::string& path, bool global) {
	GameSettingsPresetInfo info;
	info.global = global;
	if(!ReadString(path, "GameSettings", "Name", info.name, ""))
		info.name = GetBaseFilenameWithoutExt(path);
	ReadString(path, "GameSettings", "Description", info.description, "");
	info.path = path;
	return info;
}

std::set<GameSettingsPresetInfo> presetsForMod(const std::string& modDir) {
	std::set<GameSettingsPresetInfo> presets;
	for(Iterator<std::string>::Ref it = FileListIter(".", false, FM_REG, "*.gamesettings"); it->isValid(); it->next())
		presets.insert(settingsInfo("./" + GetBaseFilename(it->get()), true));

	for(Iterator<std::string>::Ref it = FileListIter(modDir, false, FM_REG, "*.gamesettings"); it->isValid(); it->next())
		presets.insert(settingsInfo(modDir + "/" + GetBaseFilename(it->get()), false));

	return presets;
}

GuiListItem::Pt infoForSettingsPreset(const GameSettingsPresetInfo& preset) {
	struct Item : GuiListItem {
		Item(const GameSettingsPresetInfo& p) : preset(p) {}
		GameSettingsPresetInfo preset;

		virtual std::string caption() { return preset.name; }
		virtual std::string tooltip() { return preset.description; }
		virtual std::string index() { return preset.path; }
	};

	return new Item(preset);
}

static std::list<GuiListItem::Pt> getCurrentSettingsPresetList() {
	std::set<GameSettingsPresetInfo> presets = presetsForMod(tLXOptions->tGameInfo.sModDir);
	std::list<GuiListItem::Pt> presetList;
	foreach(i, presets)
		presetList.push_back(infoForSettingsPreset(*i));
	return presetList;
}

GuiList::Pt dynamicPresetListForCurrentMod() {
	return dynamicGuiList(boost::bind(&getCurrentSettingsPresetList));
}

void setupModGameSettingsPresetComboboxes(DeprecatedGUI::CCombobox* modList, DeprecatedGUI::CCombobox* presetList) {
	presetList->setListBackend( dynamicPresetListForCurrentMod() );
	modList->OnChangeSelection.connect( boost::bind(&DeprecatedGUI::CCombobox::updateFromListBackend, presetList) );
	presetList->updateFromListBackend(); // also update right now
}

<commit_msg>small workaround, so that you have  a working version for now (but why the hell does that hang?)<commit_after>/*
 *  Mod.cpp
 *  OpenLieroX
 *
 *  Created by Albert Zeyer on 08.12.09.
 *  code under LGPL
 *
 */

#include <boost/bind.hpp>
#include "Mod.h"
#include "CGameScript.h"
#include "FindFile.h"
#include "Options.h"
#include "util/macros.h"
#include "ConfigHandler.h"
#include "DeprecatedGUI/CCombobox.h"
#include "gui/List.h"

ModInfo infoForMod(const std::string& f, bool absolute) {
	std::string name;
	ModInfo info;
	CGameScript::CheckFile(f, name, absolute, &info);
	return info;
}

std::string modName(const std::string& f) {
	ModInfo info = infoForMod(f);
	if(info.valid)
		return info.name;
	return "";
}

static GameSettingsPresetInfo settingsInfo(const std::string& path, bool global) {
	GameSettingsPresetInfo info;
	info.global = global;
	if(!ReadString(path, "GameSettings", "Name", info.name, ""))
		info.name = GetBaseFilenameWithoutExt(path);
	ReadString(path, "GameSettings", "Description", info.description, "");
	info.path = path;
	return info;
}

std::set<GameSettingsPresetInfo> presetsForMod(const std::string& modDir) {
	std::set<GameSettingsPresetInfo> presets;
	for(Iterator<std::string>::Ref it = FileListIter(".", false, FM_REG, "*.gamesettings"); it->isValid(); it->next())
		presets.insert(settingsInfo("./" + GetBaseFilename(it->get()), true));

	for(Iterator<std::string>::Ref it = FileListIter(modDir, false, FM_REG, "*.gamesettings"); it->isValid(); it->next())
		presets.insert(settingsInfo(modDir + "/" + GetBaseFilename(it->get()), false));

	return presets;
}

GuiListItem::Pt infoForSettingsPreset(const GameSettingsPresetInfo& preset) {
	struct Item : GuiListItem {
		Item(const GameSettingsPresetInfo& p) : preset(p) {}
		GameSettingsPresetInfo preset;

		virtual std::string caption() { return preset.name; }
		virtual std::string tooltip() { return preset.description; }
		virtual std::string index() { return preset.path; }
	};

	return new Item(preset);
}

static std::list<GuiListItem::Pt> getCurrentSettingsPresetList() {
	std::set<GameSettingsPresetInfo> presets = presetsForMod(tLXOptions->tGameInfo.sModDir);
	std::list<GuiListItem::Pt> presetList;
	foreach(i, presets)
		presetList.push_back(infoForSettingsPreset(*i));
	return presetList;
}

GuiList::Pt dynamicPresetListForCurrentMod() {
	return dynamicGuiList(boost::bind(&getCurrentSettingsPresetList));
}

void setupModGameSettingsPresetComboboxes(DeprecatedGUI::CCombobox* modList, DeprecatedGUI::CCombobox* presetList) {
	return; // TODO: ???
	presetList->setListBackend( dynamicPresetListForCurrentMod() );
	modList->OnChangeSelection.connect( boost::bind(&DeprecatedGUI::CCombobox::updateFromListBackend, presetList) );
	presetList->updateFromListBackend(); // also update right now
}

<|endoftext|>


<commit_before>#include "gfx.h"

#include <FreeImage.h>

#ifdef _RASPI
#include <GLES2/gl2.h>
#else
#include <GL/glew.h>
#endif

using namespace gfx;

Image::Image(const _priv&, uint32_t w, uint32_t h, uint8_t* pixmap)
   : _pixmap(pixmap), _width(w), _height(h)
{
   _tex = 0;
}

Image::~Image()
{
   free(_pixmap);

   if (_tex){
      glDeleteTextures(1, &_tex);
   }
}

std::shared_ptr<Image> Image::create(const std::string& filename)
{
	FREE_IMAGE_FORMAT fmt = FreeImage_GetFileType(filename.c_str(), 0);

	if (fmt == FIF_UNKNOWN) {
		fmt = FreeImage_GetFIFFromFilename(filename.c_str() );
	}

	// if still unkown, return failure.
	if (fmt == FIF_UNKNOWN)
	{
		LOG(ERR) << "image: could not determine format: " << filename;
		return nullptr;
	}

	FIBITMAP* bmp = FreeImage_Load(fmt, filename.c_str());
	if (!bmp)
	{
		LOG(ERR) << "image: could not load image: " << filename;
		return nullptr;
	}

   uint32_t w = FreeImage_GetWidth(bmp);
   uint32_t h = FreeImage_GetHeight(bmp);
   if (!w || !h)
   {
		LOG(ERR) << "image: invalid dimension: " << filename;
		FreeImage_Unload(bmp);
      return nullptr;
	}

   bool alpha = true;

   FIBITMAP* temp = bmp;
	bmp = FreeImage_ConvertTo32Bits(temp);
	FreeImage_Unload(temp);

   uint8_t* pixmap = reinterpret_cast<uint8_t*>(malloc(w * h * 4));
   if (!pixmap)
   {
      FreeImage_Unload(bmp);
      return nullptr;
   }

   //FreeImage_ConvertToRawBits(pixmap, bmp, w*4, 32, 0, 0, 0);

   uint8_t* pixels = reinterpret_cast<uint8_t*>(FreeImage_GetBits(bmp));
   for (int j= 0; j < w*h; ++j)
   {
		pixmap[j*4+0] = pixels[j*4+2];
		pixmap[j*4+1] = pixels[j*4+1];
		pixmap[j*4+2] = pixels[j*4+0];
		pixmap[j*4+3] = pixels[j*4+3];
	}

   FreeImage_Unload(bmp);

	auto img(std::make_shared<Image>(_priv{}, w, h, pixmap));

   GLenum err;

   GLuint tex;
	glGenTextures(1, &tex);
	glBindTexture(GL_TEXTURE_2D, tex);

	//glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glTexImage2D(
			GL_TEXTURE_2D,
			0,
			alpha ? GL_RGBA : GL_RGB,
			w,
			h,
			0,
			alpha ? GL_RGBA : GL_RGB,
			GL_UNSIGNED_BYTE,
			pixmap);

	err = glGetError();

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

   err = glGetError();

	glBindTexture(GL_TEXTURE_2D, 0);

   img->_tex = tex;

	return img;
}

uint32_t Image::texture()
{
   return _tex;
}<commit_msg>compiler errors on pi<commit_after>#include "gfx.h"

#include <FreeImage.h>

#ifdef _RASPI
#include <GLES2/gl2.h>
#else
#include <GL/glew.h>
#endif

using namespace gfx;

Image::Image(const _priv&, uint32_t w, uint32_t h, uint8_t* pixmap)
   : _pixmap(pixmap), _width(w), _height(h)
{
   _tex = 0;
}

Image::~Image()
{
   free(_pixmap);

   if (_tex){
      glDeleteTextures(1, &_tex);
   }
}

std::shared_ptr<Image> Image::create(const std::string& filename)
{
	FREE_IMAGE_FORMAT fmt = FreeImage_GetFileType(filename.c_str(), 0);

	if (fmt == FIF_UNKNOWN) {
		fmt = FreeImage_GetFIFFromFilename(filename.c_str() );
	}

	// if still unkown, return failure.
	if (fmt == FIF_UNKNOWN)
	{
		LOG(ERR) << "image: could not determine format: " << filename;
		return nullptr;
	}

	FIBITMAP* bmp = FreeImage_Load(fmt, filename.c_str());
	if (!bmp)
	{
		LOG(ERR) << "image: could not load image: " << filename;
		return nullptr;
	}

   uint32_t w = FreeImage_GetWidth(bmp);
   uint32_t h = FreeImage_GetHeight(bmp);
   if (!w || !h)
   {
		LOG(ERR) << "image: invalid dimension: " << filename;
		FreeImage_Unload(bmp);
      return nullptr;
	}

   bool alpha = true;

   FIBITMAP* temp = bmp;
	bmp = FreeImage_ConvertTo32Bits(temp);
	FreeImage_Unload(temp);

   uint8_t* pixmap = reinterpret_cast<uint8_t*>(malloc(w * h * 4));
   if (!pixmap)
   {
      FreeImage_Unload(bmp);
      return nullptr;
   }

   //FreeImage_ConvertToRawBits(pixmap, bmp, w*4, 32, 0, 0, 0);

   uint8_t* pixels = reinterpret_cast<uint8_t*>(FreeImage_GetBits(bmp));
   for (int j= 0; j < w*h; ++j)
   {
		pixmap[j*4+0] = pixels[j*4+2];
		pixmap[j*4+1] = pixels[j*4+1];
		pixmap[j*4+2] = pixels[j*4+0];
		pixmap[j*4+3] = pixels[j*4+3];
	}

   FreeImage_Unload(bmp);

	auto img(std::make_shared<Image>(_priv{}, w, h, pixmap));

   GLenum err;

   GLuint tex;
	glGenTextures(1, &tex);
	glBindTexture(GL_TEXTURE_2D, tex);

	//glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glTexImage2D(
			GL_TEXTURE_2D,
			0,
			alpha ? GL_RGBA : GL_RGB,
			w,
			h,
			0,
			alpha ? GL_RGBA : GL_RGB,
			GL_UNSIGNED_BYTE,
			pixmap);

	err = glGetError();

   //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
   //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

   err = glGetError();

	glBindTexture(GL_TEXTURE_2D, 0);

   img->_tex = tex;

	return img;
}

uint32_t Image::texture()
{
   return _tex;
}
<|endoftext|>