mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 01:07:51 -04:00
fill_distance_inside(), fill_distance_outside()
This commit is contained in:
parent
1f309fede5
commit
531c5907fc
@ -1199,29 +1199,57 @@ lighten_sub_image(const PNMImage ©, int xto, int yto,
|
|||||||
// s = select_image.get_channel(x, y). Set this image's
|
// s = select_image.get_channel(x, y). Set this image's
|
||||||
// (x, y) to:
|
// (x, y) to:
|
||||||
//
|
//
|
||||||
// lt.get_xel(x, y) if s <= threshold, or
|
// lt.get_xel(x, y) if s < threshold, or
|
||||||
//
|
//
|
||||||
// ge.get_xel(x, y) if s > threshold
|
// ge.get_xel(x, y) if s >= threshold
|
||||||
//
|
//
|
||||||
// Any of select_image, lt, or ge may be the same
|
// Any of select_image, lt, or ge may be the same
|
||||||
// PNMImge object as this image, or the same as each
|
// PNMImge object as this image, or the same as each
|
||||||
// other; or they may all be different. All images must
|
// other; or they may all be different. All images must
|
||||||
// be the same size.
|
// be the same size. As a special case, lt and ge may
|
||||||
|
// both be 1x1 images instead of the source image size.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void PNMImage::
|
void PNMImage::
|
||||||
threshold(const PNMImage &select_image, int channel, double threshold,
|
threshold(const PNMImage &select_image, int channel, double threshold,
|
||||||
const PNMImage <, const PNMImage &ge) {
|
const PNMImage <, const PNMImage &ge) {
|
||||||
nassertv(get_x_size() <= select_image.get_x_size() && get_y_size() <= select_image.get_y_size());
|
nassertv(get_x_size() <= select_image.get_x_size() && get_y_size() <= select_image.get_y_size());
|
||||||
nassertv(get_x_size() <= lt.get_x_size() && get_y_size() <= lt.get_y_size());
|
|
||||||
nassertv(get_x_size() <= ge.get_x_size() && get_y_size() <= ge.get_y_size());
|
|
||||||
nassertv(channel >= 0 && channel < select_image.get_num_channels());
|
nassertv(channel >= 0 && channel < select_image.get_num_channels());
|
||||||
|
|
||||||
xelval threshold_val = (xelval)(select_image.get_maxval() * threshold + 0.5);
|
xelval threshold_val = (xelval)(select_image.get_maxval() * threshold + 0.5);
|
||||||
|
|
||||||
if (get_maxval() == lt.get_maxval() && get_maxval() == ge.get_maxval()) {
|
if (lt.get_x_size() == 1 && lt.get_y_size() == 1 &&
|
||||||
// Simple case: the maxvals are all equal. Copy by integer value.
|
ge.get_x_size() == 1 && ge.get_y_size() == 1) {
|
||||||
int x, y;
|
// 1x1 source images.
|
||||||
|
xel lt_val = lt.get_xel_val(0, 0);
|
||||||
|
xelval lt_alpha = 0;
|
||||||
|
if (lt.has_alpha()) {
|
||||||
|
lt_alpha = lt.get_alpha_val(0, 0);
|
||||||
|
}
|
||||||
|
if (lt.get_maxval() != get_maxval()) {
|
||||||
|
double scale = (double)get_maxval() / (double)lt.get_maxval();
|
||||||
|
PPM_ASSIGN(lt_val,
|
||||||
|
(xelval)(PPM_GETR(lt_val) * scale + 0.5),
|
||||||
|
(xelval)(PPM_GETG(lt_val) * scale + 0.5),
|
||||||
|
(xelval)(PPM_GETB(lt_val) * scale + 0.5));
|
||||||
|
lt_alpha = (xelval)(lt_alpha * scale + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
xel ge_val = ge.get_xel_val(0, 0);
|
||||||
|
xelval ge_alpha = 0;
|
||||||
|
if (ge.has_alpha()) {
|
||||||
|
ge_alpha = ge.get_alpha_val(0, 0);
|
||||||
|
}
|
||||||
|
if (ge.get_maxval() != get_maxval()) {
|
||||||
|
double scale = (double)get_maxval() / (double)ge.get_maxval();
|
||||||
|
PPM_ASSIGN(ge_val,
|
||||||
|
(xelval)(PPM_GETR(ge_val) * scale + 0.5),
|
||||||
|
(xelval)(PPM_GETG(ge_val) * scale + 0.5),
|
||||||
|
(xelval)(PPM_GETB(ge_val) * scale + 0.5));
|
||||||
|
ge_alpha = (xelval)(ge_alpha * scale + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
|
||||||
if (channel == 3) {
|
if (channel == 3) {
|
||||||
// Further special case: the alpha channel.
|
// Further special case: the alpha channel.
|
||||||
if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
|
if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
|
||||||
@ -1229,23 +1257,23 @@ threshold(const PNMImage &select_image, int channel, double threshold,
|
|||||||
for (y = 0; y < get_y_size(); y++) {
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
for (x = 0; x < get_x_size(); x++) {
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
if (select_image.get_alpha_val(x, y) < threshold_val) {
|
if (select_image.get_alpha_val(x, y) < threshold_val) {
|
||||||
set_xel_val(x, y, lt.get_xel_val(x, y));
|
set_xel_val(x, y, lt_val);
|
||||||
set_alpha_val(x, y, lt.get_alpha_val(x, y));
|
set_alpha_val(x, y, lt_alpha);
|
||||||
} else {
|
} else {
|
||||||
set_xel_val(x, y, ge.get_xel_val(x, y));
|
set_xel_val(x, y, ge_val);
|
||||||
set_alpha_val(x, y, ge.get_alpha_val(x, y));
|
set_alpha_val(x, y, ge_alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Don't copy alpha channel.
|
// Don't copy alpha channel.
|
||||||
for (y = 0; y < get_y_size(); y++) {
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
for (x = 0; x < get_x_size(); x++) {
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
if (select_image.get_alpha_val(x, y) < threshold_val) {
|
if (select_image.get_alpha_val(x, y) < threshold_val) {
|
||||||
set_xel_val(x, y, lt.get_xel_val(x, y));
|
set_xel_val(x, y, lt_val);
|
||||||
} else {
|
} else {
|
||||||
set_xel_val(x, y, ge.get_xel_val(x, y));
|
set_xel_val(x, y, ge_val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1257,52 +1285,120 @@ threshold(const PNMImage &select_image, int channel, double threshold,
|
|||||||
for (y = 0; y < get_y_size(); y++) {
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
for (x = 0; x < get_x_size(); x++) {
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
||||||
set_xel_val(x, y, lt.get_xel_val(x, y));
|
set_xel_val(x, y, lt_val);
|
||||||
set_alpha_val(x, y, lt.get_alpha_val(x, y));
|
set_alpha_val(x, y, lt_alpha);
|
||||||
} else {
|
} else {
|
||||||
set_xel_val(x, y, ge.get_xel_val(x, y));
|
set_xel_val(x, y, ge_val);
|
||||||
set_alpha_val(x, y, ge.get_alpha_val(x, y));
|
set_alpha_val(x, y, ge_alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Don't copy alpha channel.
|
// Don't copy alpha channel.
|
||||||
for (y = 0; y < get_y_size(); y++) {
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
for (x = 0; x < get_x_size(); x++) {
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
||||||
set_xel_val(x, y, lt.get_xel_val(x, y));
|
set_xel_val(x, y, lt_val);
|
||||||
} else {
|
} else {
|
||||||
set_xel_val(x, y, ge.get_xel_val(x, y));
|
set_xel_val(x, y, ge_val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// General case: the maxvals are different. Copy by floating-point value.
|
// Same-sized source images.
|
||||||
int x, y;
|
nassertv(get_x_size() <= lt.get_x_size() && get_y_size() <= lt.get_y_size());
|
||||||
|
nassertv(get_x_size() <= ge.get_x_size() && get_y_size() <= ge.get_y_size());
|
||||||
|
|
||||||
if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
|
if (get_maxval() == lt.get_maxval() && get_maxval() == ge.get_maxval()) {
|
||||||
for (y = 0; y < get_y_size(); y++) {
|
// Simple case: the maxvals are all equal. Copy by integer value.
|
||||||
for (x = 0; x < get_x_size(); x++) {
|
int x, y;
|
||||||
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
|
||||||
set_xel(x, y, lt.get_xel(x, y));
|
if (channel == 3) {
|
||||||
set_alpha(x, y, lt.get_alpha(x, y));
|
// Further special case: the alpha channel.
|
||||||
} else {
|
if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
|
||||||
set_xel(x, y, ge.get_xel(x, y));
|
// Copy alpha channel too.
|
||||||
set_alpha(x, y, ge.get_alpha(x, y));
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
|
if (select_image.get_alpha_val(x, y) < threshold_val) {
|
||||||
|
set_xel_val(x, y, lt.get_xel_val(x, y));
|
||||||
|
set_alpha_val(x, y, lt.get_alpha_val(x, y));
|
||||||
|
} else {
|
||||||
|
set_xel_val(x, y, ge.get_xel_val(x, y));
|
||||||
|
set_alpha_val(x, y, ge.get_alpha_val(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Don't copy alpha channel.
|
||||||
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
|
if (select_image.get_alpha_val(x, y) < threshold_val) {
|
||||||
|
set_xel_val(x, y, lt.get_xel_val(x, y));
|
||||||
|
} else {
|
||||||
|
set_xel_val(x, y, ge.get_xel_val(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Any generic channel.
|
||||||
|
if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
|
||||||
|
// Copy alpha channel too.
|
||||||
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
|
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
||||||
|
set_xel_val(x, y, lt.get_xel_val(x, y));
|
||||||
|
set_alpha_val(x, y, lt.get_alpha_val(x, y));
|
||||||
|
} else {
|
||||||
|
set_xel_val(x, y, ge.get_xel_val(x, y));
|
||||||
|
set_alpha_val(x, y, ge.get_alpha_val(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Don't copy alpha channel.
|
||||||
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
|
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
||||||
|
set_xel_val(x, y, lt.get_xel_val(x, y));
|
||||||
|
} else {
|
||||||
|
set_xel_val(x, y, ge.get_xel_val(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (y = 0; y < get_y_size(); y++) {
|
// General case: the maxvals are different. Copy by floating-point value.
|
||||||
for (x = 0; x < get_x_size(); x++) {
|
int x, y;
|
||||||
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
|
||||||
set_xel(x, y, lt.get_xel(x, y));
|
if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
|
||||||
} else {
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
set_xel(x, y, ge.get_xel(x, y));
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
|
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
||||||
|
set_xel(x, y, lt.get_xel(x, y));
|
||||||
|
set_alpha(x, y, lt.get_alpha(x, y));
|
||||||
|
} else {
|
||||||
|
set_xel(x, y, ge.get_xel(x, y));
|
||||||
|
set_alpha(x, y, ge.get_alpha(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < get_y_size(); y++) {
|
||||||
|
for (x = 0; x < get_x_size(); x++) {
|
||||||
|
if (select_image.get_channel_val(x, y, channel) < threshold_val) {
|
||||||
|
set_xel(x, y, lt.get_xel(x, y));
|
||||||
|
} else {
|
||||||
|
set_xel(x, y, ge.get_xel(x, y));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1310,6 +1406,98 @@ threshold(const PNMImage &select_image, int channel, double threshold,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: PNMImage::fill_distance_inside
|
||||||
|
// Access: Published
|
||||||
|
// Description: Replaces this image with a grayscale image whose gray
|
||||||
|
// channel represents the linear Manhattan distance from
|
||||||
|
// the nearest dark pixel in the given mask image, up to
|
||||||
|
// the specified radius value (which also becomes the
|
||||||
|
// new maxval). radius may range from 0 to maxmaxval;
|
||||||
|
// smaller values will compute faster. A dark pixel is
|
||||||
|
// defined as one whose pixel value is < threshold.
|
||||||
|
//
|
||||||
|
// If shrink_from_border is true, then the mask image is
|
||||||
|
// considered to be surrounded by a border of dark
|
||||||
|
// pixels; otherwise, the border isn't considered.
|
||||||
|
//
|
||||||
|
// This can be used, in conjunction with threshold, to
|
||||||
|
// shrink a mask image inwards by a certain number of
|
||||||
|
// pixels.
|
||||||
|
//
|
||||||
|
// The mask image may be the same image as this one, in
|
||||||
|
// which case it is destructively modified by this
|
||||||
|
// process.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void PNMImage::
|
||||||
|
fill_distance_inside(const PNMImage &mask, double threshold, int radius, bool shrink_from_border) {
|
||||||
|
nassertv(radius <= PNM_MAXMAXVAL);
|
||||||
|
PNMImage dist(mask.get_x_size(), mask.get_y_size(), 1, radius);
|
||||||
|
dist.fill_val(radius);
|
||||||
|
|
||||||
|
xelval threshold_val = (xelval)(mask.get_maxval() * threshold + 0.5);
|
||||||
|
|
||||||
|
for (int yi = 0; yi < mask.get_y_size(); ++yi) {
|
||||||
|
for (int xi = 0; xi < mask.get_x_size(); ++xi) {
|
||||||
|
if (mask.get_gray_val(xi, yi) < threshold_val) {
|
||||||
|
dist.do_fill_distance(xi, yi, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shrink_from_border) {
|
||||||
|
// Also measure from the image border.
|
||||||
|
for (int yi = 0; yi < mask.get_y_size(); ++yi) {
|
||||||
|
dist.do_fill_distance(0, yi, 1);
|
||||||
|
dist.do_fill_distance(mask.get_x_size() - 1, yi, 1);
|
||||||
|
}
|
||||||
|
for (int xi = 0; xi < mask.get_x_size(); ++xi) {
|
||||||
|
dist.do_fill_distance(xi, 0, 1);
|
||||||
|
dist.do_fill_distance(xi, mask.get_y_size() - 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
take_from(dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: PNMImage::fill_distance_outside
|
||||||
|
// Access: Published
|
||||||
|
// Description: Replaces this image with a grayscale image whose gray
|
||||||
|
// channel represents the linear Manhattan distance from
|
||||||
|
// the nearest white pixel in the given mask image, up to
|
||||||
|
// the specified radius value (which also becomes the
|
||||||
|
// new maxval). radius may range from 0 to maxmaxval;
|
||||||
|
// smaller values will compute faster. A white pixel is
|
||||||
|
// defined as one whose pixel value is >= threshold.
|
||||||
|
//
|
||||||
|
// This can be used, in conjunction with threshold, to
|
||||||
|
// grow a mask image outwards by a certain number of
|
||||||
|
// pixels.
|
||||||
|
//
|
||||||
|
// The mask image may be the same image as this one, in
|
||||||
|
// which case it is destructively modified by this
|
||||||
|
// process.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void PNMImage::
|
||||||
|
fill_distance_outside(const PNMImage &mask, double threshold, int radius) {
|
||||||
|
nassertv(radius <= PNM_MAXMAXVAL);
|
||||||
|
PNMImage dist(mask.get_x_size(), mask.get_y_size(), 1, radius);
|
||||||
|
dist.fill_val(radius);
|
||||||
|
|
||||||
|
xelval threshold_val = (xelval)(mask.get_maxval() * threshold + 0.5);
|
||||||
|
|
||||||
|
for (int yi = 0; yi < mask.get_y_size(); ++yi) {
|
||||||
|
for (int xi = 0; xi < mask.get_x_size(); ++xi) {
|
||||||
|
if (mask.get_gray_val(xi, yi) >= threshold_val) {
|
||||||
|
dist.do_fill_distance(xi, yi, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
take_from(dist);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: PNMImage::rescale
|
// Function: PNMImage::rescale
|
||||||
// Access: Published
|
// Access: Published
|
||||||
@ -1714,6 +1902,29 @@ setup_rc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: PNMImage::do_fill_distance
|
||||||
|
// Access: Private
|
||||||
|
// Description: Recursively fills in the minimum distance measured
|
||||||
|
// from a certain set of points into the gray channel.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void PNMImage::
|
||||||
|
do_fill_distance(int xi, int yi, int d) {
|
||||||
|
if (xi < 0 || xi >= get_x_size() ||
|
||||||
|
yi < 0 || yi >= get_y_size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (get_gray_val(xi, yi) <= d) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set_gray_val(xi, yi, d);
|
||||||
|
|
||||||
|
do_fill_distance(xi + 1, yi, d + 1);
|
||||||
|
do_fill_distance(xi - 1, yi, d + 1);
|
||||||
|
do_fill_distance(xi, yi + 1, d + 1);
|
||||||
|
do_fill_distance(xi, yi - 1, d + 1);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: PNMImage::get_average_xel
|
// Function: PNMImage::get_average_xel
|
||||||
// Access: Published
|
// Access: Published
|
||||||
|
@ -218,6 +218,8 @@ PUBLISHED:
|
|||||||
double pixel_scale = 1.0);
|
double pixel_scale = 1.0);
|
||||||
void threshold(const PNMImage &select_image, int channel, double threshold,
|
void threshold(const PNMImage &select_image, int channel, double threshold,
|
||||||
const PNMImage <, const PNMImage &ge);
|
const PNMImage <, const PNMImage &ge);
|
||||||
|
BLOCKING void fill_distance_inside(const PNMImage &mask, double threshold, int radius, bool shrink_from_border);
|
||||||
|
BLOCKING void fill_distance_outside(const PNMImage &mask, double threshold, int radius);
|
||||||
|
|
||||||
void rescale(double min_val, double max_val);
|
void rescale(double min_val, double max_val);
|
||||||
|
|
||||||
@ -259,6 +261,8 @@ PUBLISHED:
|
|||||||
LColord get_average_xel_a() const;
|
LColord get_average_xel_a() const;
|
||||||
double get_average_gray() const;
|
double get_average_gray() const;
|
||||||
|
|
||||||
|
void do_fill_distance(int xi, int yi, int d);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Know what you are doing if you access the underlying data arrays
|
// Know what you are doing if you access the underlying data arrays
|
||||||
// directly.
|
// directly.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user