Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#75): prevent multiple calls to InsertSeams from inserting the same seam #76

Merged
merged 2 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 104 additions & 30 deletions libseam_carving/carver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <iostream>

namespace seam_carving {
/** PROTECTED */
Seam Carver::FindVerticalSeam(cv::InputArray img) {
// compute the energy map
cv::Mat energy_map;
Expand Down Expand Up @@ -261,6 +262,87 @@ namespace seam_carving {
res.copyTo(output);
}

/** PUBLIC */
std::vector<Seam> Carver::FindVerticalSeams(const int &k, cv::InputArray input) {
cv::Mat temp = input.getMat();
std::vector<Seam> seams;

int count = k;
while (count > 0) {
Seam seam = FindVerticalSeam(temp);
seams.push_back(seam);
RemoveVerticalSeam(seam, temp, temp);
count--;
}

return seams;
}

std::vector<Seam> Carver::FindHorizontalSeams(const int &k, cv::InputArray input) {
cv::Mat temp = input.getMat();
std::vector<Seam> seams;

int count = k;
while (count > 0) {
Seam seam = FindHorizontalSeam(temp);
seams.push_back(seam);
RemoveHorizontalSeam(seam, temp, temp);
count--;
}

return seams;
}

void Carver::InsertVerticalSeams(const int& k, cv::InputArray input, cv::OutputArray output) {
std::vector<Seam> seams = FindVerticalSeams(k, input);
input.copyTo(output);

for (const Seam& s : seams)
InsertVerticalSeam(s, output, output);
}

void Carver::InsertHorizontalSeams(const int& k, cv::InputArray input, cv::OutputArray output) {
std::vector<Seam> seams = FindHorizontalSeams(k, input);
input.copyTo(output);

for (const Seam& s : seams)
InsertHorizontalSeam(s, output, output);
}

void Carver::InsertSeams(const std::vector<Seam>& seams, cv::InputOutputArray output) {
for (const Seam& s : seams) {
if (s.dir() == VERT)
InsertVerticalSeam(s, output, output);
else
InsertHorizontalSeam(s, output, output);
}
}

void Carver::RemoveVerticalSeams(const int& k, cv::InputArray input, cv::OutputArray output) {
std::vector<Seam> seams = FindVerticalSeams(k, input);
input.copyTo(output);

for (const Seam& s : seams)
RemoveVerticalSeam(s, output, output);
}

void Carver::RemoveHorizontalSeams(const int& k, cv::InputArray input, cv::OutputArray output) {
std::vector<Seam> seams = FindHorizontalSeams(k, input);
input.copyTo(output);

for (const Seam& s : seams)
RemoveHorizontalSeam(s, output, output);
}

void Carver::RemoveSeams(const std::vector<Seam>& seams, cv::InputOutputArray output) {
for (const Seam& s : seams) {
if (s.dir() == VERT)
RemoveVerticalSeam(s, output, output);
else
RemoveHorizontalSeam(s, output, output);
}
}

void Carver::Carve(const int& rows, const int& cols, cv::InputArray input, cv::OutputArray output) {
// (0, 0)
if (rows == 0 && cols == 0) {
Expand All @@ -278,55 +360,47 @@ namespace seam_carving {
int col_diff = cols - res.cols;

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

const Seam horizontalSeam = FindHorizontalSeam(res);
InsertHorizontalSeam(horizontalSeam, res, res);
InsertHorizontalSeams(1, 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/-, +) - insert any remaining vertical seams necessary
if (col_diff > 0) {
InsertVerticalSeams(col_diff, res, res);
col_diff = 0;
}

// (+, 0/-)
while (row_diff > 0) {
const Seam horizontalSeam = FindHorizontalSeam(res);
InsertHorizontalSeam(horizontalSeam, res, res);
row_diff--;
// (+, 0/-) - insert any remaining horizontal seams necessary
if (row_diff > 0) {
InsertHorizontalSeams(row_diff, res, res);
row_diff = 0;
}

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

const Seam horizontalSeam = FindHorizontalSeam(res);
RemoveHorizontalSeam(horizontalSeam, res, res);
RemoveHorizontalSeams(1, 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/+, -) - remove any remaining vertical seams necessary
if (col_diff < 0) {
RemoveVerticalSeams(col_diff * -1, res, res);
col_diff = 0;
}

// (-, 0/+)
while (row_diff < 0) {
const Seam horizontalSeam = FindHorizontalSeam(res);
RemoveHorizontalSeam(horizontalSeam, res, res);
row_diff++;
// (-, 0/+) - remove any remaining horizontal seams necessary
if (row_diff < 0) {
RemoveHorizontalSeams(row_diff * -1, res, res);
row_diff = 0;
}

res.copyTo(output);
Expand Down
52 changes: 48 additions & 4 deletions libseam_carving/seam_carving/carver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
// std
#include <algorithm>
#include <limits>
#include <vector>

namespace seam_carving {
/**
* Base class for all carvers
*/
class Carver {
public:
protected:
/**
* @param img the image to find the optimal seam in
* @returns the optimal seam to be removed
Expand All @@ -30,9 +31,9 @@ namespace seam_carving {
static Seam FindHorizontalSeam(cv::InputArray img);

/**
* @param seam the seam to be removed
* @param img the target image
*/
* @param seam the seam to be removed
* @param img the target image
*/
static void RemoveVerticalSeam(const Seam &seam, cv::InputArray input, cv::OutputArray output);
static void RemoveHorizontalSeam(const Seam &seam, cv::InputArray input, cv::OutputArray output);

Expand All @@ -43,6 +44,49 @@ 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);

public:
/**
* Finds the first k vertical/horizontal seams for removal.
*
* @param k the number of seams to find
* @param input the image to find the seams in
* @return a vector of the first k seams for removal
*/
static std::vector<Seam> FindVerticalSeams(const int& k, cv::InputArray input);
static std::vector<Seam> FindHorizontalSeams(const int& k, cv::InputArray input);

/**
* Inserts k vertical/horizontal seams.
* @param k the number of seams to insert
* @param input the image to find seams in
* @param output the image to insert seams into
*/
static void InsertVerticalSeams(const int& k, cv::InputArray input, cv::OutputArray output);
static void InsertHorizontalSeams(const int& k, cv::InputArray input, cv::OutputArray output);

/**
* Inserts the specified seams, regardless of direction, into the image appropriately.
* @param seams the seams to be inserted
* @param output the image to insert seams into
*/
static void InsertSeams(const std::vector<Seam>& seams, cv::InputOutputArray output);

/**
* Removes k vertical/horizontal seams.
* @param k the number of seams to remove
* @param input the image to find seams in
* @param output the image to remove seams from
*/
static void RemoveVerticalSeams(const int& k, cv::InputArray input, cv::OutputArray output);
static void RemoveHorizontalSeams(const int& k, cv::InputArray input, cv::OutputArray output);

/**
* Removes the specified seams, regardless of direction, from the image appropriately.
* @param seams the seams to be removed
* @param output the image to remove seams from
*/
static void RemoveSeams(const std::vector<Seam>& seams, cv::InputOutputArray output);

/**
* @param rows the target number of rows for the new image
* @param cols the target number of cols for the new image
Expand Down
20 changes: 10 additions & 10 deletions tests/unit/carver.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,56 +30,56 @@ INSTANTIATE_TEST_SUITE_P(
)
);

TEST_P(CarverTest, FindVerticalSeam) {
TEST_P(CarverTest, FindVerticalSeams) {
sct::CarverData carver_data = GetParam();

cv::Mat input = carver_data.original_matrix;
sc::Seam expected = carver_data.vertical_seam;

sc::Seam actual = sc::Carver::FindVerticalSeam(input);
sc::Seam actual = sc::Carver::FindVerticalSeams(1, input)[0];

EXPECT_EQ(expected, actual)
<< sct::PrintWithLabel(expected, "expected") << "\n"
<< sct::PrintWithLabel(actual, "actual");
}

TEST_P(CarverTest, FindHorizontalSeam) {
TEST_P(CarverTest, FindHorizontalSeams) {
sct::CarverData carver_data = GetParam();

cv::Mat input = carver_data.original_matrix;
sc::Seam expected = carver_data.horizontal_seam;

sc::Seam actual = sc::Carver::FindHorizontalSeam(input);
sc::Seam actual = sc::Carver::FindHorizontalSeams(1, input)[0];

EXPECT_EQ(expected, actual)
<< sct::PrintWithLabel(expected, "expected") << "\n"
<< sct::PrintWithLabel(actual, "actual");
}

TEST_P(CarverTest, RemoveVerticalSeam) {
TEST_P(CarverTest, RemoveVerticalSeams) {
sct::CarverData carver_data = GetParam();

cv::Mat input = carver_data.original_matrix;
cv::Mat expected = carver_data.remove_vertical_matrix;
cv::Mat actual;

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

EXPECT_TRUE(sct::equalMatrices(expected, actual))
<< sct::PrintWithLabel(expected, "expected") << "\n"
<< sct::PrintWithLabel(actual, "actual");
}

TEST_P(CarverTest, RemoveHorizontalSeam) {
TEST_P(CarverTest, RemoveHorizontalSeams) {
sct::CarverData carver_data = GetParam();

cv::Mat input = carver_data.original_matrix;
cv::Mat expected = carver_data.remove_horizontal_matrix;
cv::Mat actual;

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

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

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

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

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

EXPECT_TRUE(sct::equalMatrices(expected, actual))
<< sct::PrintWithLabel(expected, "expected") << "\n"
Expand Down