Skip to content

Commit

Permalink
docs(#52): update README to have demo and new build instructions (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
andymina authored Nov 4, 2023
1 parent efbef3f commit b45d997
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 75 deletions.
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,12 @@ add_subdirectory(libseam_carving)
enable_testing()
include(GoogleTest)
add_subdirectory(tests)

# demo
add_executable(demo demo.cpp)
target_include_directories(demo
PRIVATE
${LIBRARY_TEST_DIR}/include)
target_link_libraries(demo
PRIVATE
seam_carving ${OPENCV_LIBS})
33 changes: 21 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,30 @@ content-aware image resizing](https://doi.org/10.1145/1275808.1276390). Learn mo

## Getting Started

It is recommended to make a virtual environment as the setup command will install pip packages.
This project uses [vcpkg](https://github.com/microsoft/vcpkg) as its C++ package manager and
[CMake](https://cmake.org) as the build-system.
The seam carving library can be built using CMake. The available targets are:

Run `make setup` from the root directory to set up the project. This will create and populate a `build/` directory and create a new `out/` directory where output files will
go by default.
- `seam_carving` - builds the dylib
- `demo` - runs the demo at `./demo.cpp`
- `carver_unit/energy_unit` - runs the unit tests at `tests/unit/[carver/energy].test.cpp` respectively

The following `make` commands are currently available:
### Running the demo

- `make demo`: builds a C++17 executable to demo seam carving. Sample images are provided in the
`samples/` directory
- `make py`: exports the existing C++ seam carving code to a Python 3.10 package called `seam_carving`
- `make clean`: cleans the project and removes any build files
1. Configure the CMake project. The code sample below uses Ninja as the generator. There are presets in `CMakePresets.json`
available to use.
2. Build the `demo` target.
3. Run the demo target with a path to an image (sample images are provided in `samples/`), and the target size

```
# step 1 - from the root folder of the repo
cmake -B {PATH_TO_BUILD_DIR} -S {PATH_TO_REPO_ROOT} -G Ninja --preset default
# step 2
cmake --build {PATH_TO_BUILD_DIR} --target demo
# step 3
./demo {PATH_TO_IMG} {TARGET COLS} {TARGET ROWS}
```

## Seam Carving Shell (SCS)

The Seam Carving Shell can be run from the `cli.py` file at the root: `python cli.py`. The cli
requires at least Python 3.10.
The Seam Carving Shell (SCS) is undergoing major reconstruction and is not supported in v2.0.0.
71 changes: 66 additions & 5 deletions demo.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,71 @@
//
// Created by Andy Mina on 3/7/23.
//
/**
* Andy Mina
*
* File to test SeamCarver in C++ implementation. Given a path to an image, the target number of
* rows, and the target number of cols, carves the image and displays the result.
*
* Args:
* {path to img}
* {target rows}
* {target cols}
*/
#include "timer.hpp"
#include <seam_carving/carver.hpp>

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>

#include <iostream>
#include <string>
#include <chrono>

namespace sc = seam_carving;

template <class Duration = std::chrono::milliseconds, class Clock = std::chrono::steady_clock>
class Timer {
using time_pt = typename Clock::time_point;

time_pt _start = Clock::now();
time_pt _end = {};

public:
void tick() { _start = Clock::now(); }
void tock() { _end = Clock::now(); }

template <class T = Duration>
auto duration() const {
return std::chrono::duration_cast<T>(_end - _start).count();
}
};

int main(int argc, char **argv) {
if (argc != 4) {
std::cout << "Usage: " << argv[0] << " {path to img} {target cols} {target rows}\n";
return -1;
}

const std::string input_path(argv[1]);
const int target_cols(std::stoi(argv[2]));
const int target_rows(std::stoi(argv[3]));

cv::Mat img = cv::imread(input_path);
std::cout << "Original size: " << img.cols << "x" << img.rows << "\n";

cv::Mat res = img;
Timer carver_timer;

std::cout << "Starting carving...\n";

carver_timer.tick();
sc::Carver::Carve(target_rows, target_cols, img, res);
carver_timer.tock();

std::cout << "Carved size: " << res.cols << "x" << res.rows << "\n";
std::cout << "Carving took " << carver_timer.duration() << " ms\n";

int main() {
std::cout << "hello world\n";
cv::imshow("Original", img);
cv::imshow("Carved", res);
int k = cv::waitKey(0);
return 0;
}
71 changes: 71 additions & 0 deletions libseam_carving/carver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,75 @@ namespace seam_carving {

res.copyTo(output);
}

void Carver::Carve(const int& rows, const int& cols, cv::InputArray input, cv::OutputArray output) {
// (0, 0)
if (rows == 0 && cols == 0) {
input.copyTo(output);
return;
}

// prepare result
cv::Mat res;
input.copyTo(res);

// Difference between target size and current size.
// Positive to insert seams, negative to remove.
int row_diff = rows - res.rows;
int col_diff = cols - res.cols;

/** HANDLE ALL CASES WHERE ONE DIMENSION IS POSITIVE */
// (+, +)
while (row_diff > 0 && col_diff > 0) {
const Seam verticalSeam = FindVerticalSeam(res);
InsertVerticalSeam(verticalSeam, res, res);
col_diff--; // update the remaining difference

const Seam horizontalSeam = FindHorizontalSeam(res);
InsertHorizontalSeam(horizontalSeam, res, res);
row_diff--; // update the remaining difference
}

// (0/-, +)
while (col_diff > 0) {
const Seam verticalSeam = FindVerticalSeam(res);
InsertVerticalSeam(verticalSeam, res, res);
col_diff--;
}

// (+, 0/-)
while (row_diff > 0) {
const Seam horizontalSeam = FindHorizontalSeam(res);
InsertHorizontalSeam(horizontalSeam, res, res);
row_diff--;
}

/** HANDLE ALL CASES WHERE ONE DIMENSION IS NEGATIVE */
// (-, -)
while (row_diff < 0 && col_diff < 0) {
const Seam verticalSeam = FindVerticalSeam(res);
RemoveVerticalSeam(verticalSeam, res, res);
col_diff++; // update the remaining difference

const Seam horizontalSeam = FindHorizontalSeam(res);
RemoveHorizontalSeam(horizontalSeam, res, res);
row_diff++; // update the remaining difference
}

// (0/+, -)
while (col_diff < 0) {
const Seam verticalSeam = FindVerticalSeam(res);
RemoveVerticalSeam(verticalSeam, res, res);
col_diff++;
}

// (-, 0/+)
while (row_diff < 0) {
const Seam horizontalSeam = FindHorizontalSeam(res);
RemoveHorizontalSeam(horizontalSeam, res, res);
row_diff++;
}

res.copyTo(output);
}
}
10 changes: 9 additions & 1 deletion libseam_carving/seam_carving/carver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace seam_carving {
* Base class for all carvers
*/
class Carver {
protected:
public:
/**
* @param img the image to find the optimal seam in
* @returns the optimal seam to be removed
Expand All @@ -42,6 +42,14 @@ namespace seam_carving {
*/
static void InsertVerticalSeam(const Seam &seam, cv::InputArray input, cv::OutputArray output);
static void InsertHorizontalSeam(const Seam &seam, cv::InputArray input, cv::OutputArray output);

/**
* @param rows the target number of rows for the new image
* @param cols the target number of cols for the new image
* @param input the image to be carved
* @param output a carved version of input
*/
static void Carve(const int& rows, const int& cols, cv::InputArray input, cv::OutputArray output);
};
}

Expand Down
40 changes: 0 additions & 40 deletions tests/include/dummy_carver.hpp

This file was deleted.

21 changes: 10 additions & 11 deletions tests/unit/carver.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
// Created by Andy Mina on 3/7/23.
//

// project
#include <seam_carving/carver.hpp>
#include <seam_carving/seam.hpp>

// test
#include "print_utils.hpp"
#include "matrix_utils.hpp"
#include "json_utils.hpp"
#include "carver_data.hpp"
#include "dummy_carver.hpp"

// project
#include <seam_carving/carver.hpp>
#include <seam_carving/seam.hpp>

// 3rd party
#include <gtest/gtest.h>
Expand All @@ -37,7 +36,7 @@ TEST_P(CarverTest, FindVerticalSeam) {
cv::Mat input = carver_data.original_matrix;
sc::Seam expected = carver_data.vertical_seam;

sc::Seam actual = sct::DummyCarver::FindVerticalSeam(input);
sc::Seam actual = sc::Carver::FindVerticalSeam(input);

EXPECT_EQ(expected, actual)
<< sct::PrintWithLabel(expected, "expected") << "\n"
Expand All @@ -50,7 +49,7 @@ TEST_P(CarverTest, FindHorizontalSeam) {
cv::Mat input = carver_data.original_matrix;
sc::Seam expected = carver_data.horizontal_seam;

sc::Seam actual = sct::DummyCarver::FindHorizontalSeam(input);
sc::Seam actual = sc::Carver::FindHorizontalSeam(input);

EXPECT_EQ(expected, actual)
<< sct::PrintWithLabel(expected, "expected") << "\n"
Expand All @@ -65,7 +64,7 @@ TEST_P(CarverTest, RemoveVerticalSeam) {
cv::Mat actual;

sc::Seam seam = carver_data.vertical_seam;
sct::DummyCarver::RemoveVerticalSeam(seam, input, actual);
sc::Carver::RemoveVerticalSeam(seam, input, actual);

EXPECT_TRUE(sct::equalMatrices(expected, actual))
<< sct::PrintWithLabel(expected, "expected") << "\n"
Expand All @@ -80,7 +79,7 @@ TEST_P(CarverTest, RemoveHorizontalSeam) {
cv::Mat actual;

sc::Seam seam = carver_data.horizontal_seam;
sct::DummyCarver::RemoveHorizontalSeam(seam, input, actual);
sc::Carver::RemoveHorizontalSeam(seam, input, actual);

EXPECT_TRUE(sct::equalMatrices(expected, actual))
<< sct::PrintWithLabel(expected, "expected") << "\n"
Expand All @@ -95,7 +94,7 @@ TEST_P(CarverTest, InsertVerticalSeam) {
cv::Mat actual;

sc::Seam seam = carver_data.vertical_seam;
sct::DummyCarver::InsertVerticalSeam(seam, input, actual);
sc::Carver::InsertVerticalSeam(seam, input, actual);

EXPECT_TRUE(sct::equalMatrices(expected, actual))
<< sct::PrintWithLabel(expected, "expected") << "\n"
Expand All @@ -110,7 +109,7 @@ TEST_P(CarverTest, InsertHorizontalSeam) {
cv::Mat actual;

sc::Seam seam = carver_data.horizontal_seam;
sct::DummyCarver::InsertHorizontalSeam(seam, input, actual);
sc::Carver::InsertHorizontalSeam(seam, input, actual);

EXPECT_TRUE(sct::equalMatrices(expected, actual))
<< sct::PrintWithLabel(expected, "expected") << "\n"
Expand Down
3 changes: 0 additions & 3 deletions tests/unit/coord.test.cpp

This file was deleted.

3 changes: 0 additions & 3 deletions tests/unit/seam.test.cpp

This file was deleted.

0 comments on commit b45d997

Please sign in to comment.