This topic is a continuation of the SQL Example: Process Images with 3x3 Filters topic and the SQL Example: Process Images using Dual 3x3 Filters topic. Please read those two topics in order before continuing, as this topic picks up directly where that last topic finished.
The discussion in this topic is the third in a series of three topics, beginning with the SQL Example: Process Images with 3x3 Filters topic and continuing the SQL Example: Process Images using Dual 3x3 Filters topic.
So far, our example query has operated only on single-channel images, displayed as grayscale images like the st_peters_grayscale image below.
We would like to extend the example query so it applies convolution matrix processing to three-channel, RGB images like the st_peters image below.
A single-channel image has tiles that for each pixel contain a single number, usually a integer such as a UINT8 or a float such as a FLOAT64. A three-channel, RGB image has tiles that for each pixel contain a vector consisting of three numbers, such as a UINT8x3 or a FLOAT64x3. In the case of processing a single-channel image with a filter matrix the computation happens on the single number for each pixel. We can apply an analogous procedure to RGB images by applying that single-channel computation to each of the three numbers in turn within the RGB pixel.
Our compartmentalization of processing within the processtile function will make it easier to write a multi-channel version of the query. We will also introduce a new processRGB function to modularize some of the extra work that needs to be done to split out channels for processing and then reassembly of those channels. We first will create a single filter example, using a 5x5 Unsharp matrix, and then we will create a two-filter, Prewitt version.
We can learn how to write SQL that takes a three-channel, RGB image like the one above and creates a single-channel image. To learn how to do that, we turn to our old friend and SQL tutor, the Edit Query button.
With the focus on the st_peters image we launch the Transform pane. We choose Tile as the target field and then we double-click the Copy template to launch it.
In the Copy template we choose the channel option for Use. We leave the Channel value set to the default channel 0 since we do not care which channel is extracted. We simply want to see how it is done.
For the Result, we choose New Table and enter st_peters Tiles Channel Image for the name of the new drawing and st_peters Tiles Channel for the name of the table. We can use whatever names we want (shorter names would probably be wiser...) but for now we will use those names since they help us remember what those components are supposed to be.
The default table name created automatically as we enter a name for the New image ends in ...Table. This topic uses a table name that ends in ...Tiles, which is how some users prefer to name tables for images. Others prefer to have all of their tables, both for drawings and images, end in ...Table. We can adjust the name automatically created for the table however we like.
We change the Channel type from the default float64 to uint8. RGB images uses unsigned integers, uint8 data type, for their channel values, so that is what we want the destination channel value to be.
We press the Edit Query button to see what SQL Manifold uses to pull one channel out of a three-channel RGB image. The resulting query, with the initial comment lines removed to save space:
-- prepare begin
CREATE TABLE [st_peters Tiles Channel] (
[X] INT32,
[Y] INT32,
[mfd_id] INT64,
[Tile] TILE,
INDEX [mfd_id_x] BTREE ([mfd_id]),
INDEX [X_Y_Tile_x] RTREE ([X], [Y], [Tile] TILESIZE (128, 128) TILETYPE UINT8),
PROPERTY 'FieldCoordSystem.Tile' ComponentFieldCoordSystem([st_peters Tiles], 'Tile'),
PROPERTY 'FieldTileSize.Tile' '[ 128, 128 ]',
PROPERTY 'FieldTileType.Tile' 'uint8'
);
CREATE IMAGE [st_peters Tiles Channel Image] (
PROPERTY 'Table' '[st_peters Tiles Channel]',
PROPERTY 'FieldX' 'X',
PROPERTY 'FieldY' 'Y',
PROPERTY 'FieldTile' 'Tile',
PROPERTY 'Rect' CAST(ComponentFieldBounds([st_peters Tiles], 'Tile', FALSE) AS NVARCHAR)
);
-- prepare end
DELETE FROM [st_peters Tiles Channel];
INSERT INTO [st_peters Tiles Channel] (
[X], [Y],
[Tile]
) SELECT
[X], [Y],
CASTV(TileChannel([Tile], 0) AS UINT8)
FROM [st_peters Tiles];
TABLE CALL TileUpdateFieldPyramids([st_peters Tiles Channel], 'Tile');
ALTER IMAGE [st_peters Tiles Channel Image] (
ADD PROPERTY 'Rect' Coalesce(
CAST(ComponentFieldBounds([st_peters Tiles Channel], 'Tile', TRUE) AS NVARCHAR),
ComponentProperty([st_peters Tiles Channel Image], 'Rect'))
);
Setting aside the infrastructure of creating a destination table and image and then populating it, all of the action happens using a single invocation of the TileChannel function:
CASTV(TileChannel([Tile], 0) AS UINT8)
From the function's description in the SQL Functions topic, we know TileChannel takes two arguments, a tile with multiple channels and a number for which channel is desired (counting starting with zero), and then it returns a single-channel tile containing the extracted channel.
Now that we know how to pull a single channel out of a three-channel RGB image, we can proceed to adapting our single-channel query to process in turn each of the single channels we will extract. We then will put them back together into a three-channel RGB tile. To learn how to do that, we read the Example: Import BIL and Combine 3 Bands topic, which shows how to combine three, single-channel tiles into a single three-channel tile.
In that example, if [Tile1], [Tile2], and [Tile3] are each single-channel tiles, we can combine them using the TileChannelConcat function:
TileChannelsConcat([Tile1],
TileChannelsConcat([Tile2], [Tile3]))
The above expression returns a three-channel Tile that is formed by concatenating three single-channel tiles.
We now have all of the pieces we need to adapt the single-channel example queries into three-channel RGB queries.
Consider the single-channel 5x5 Unsharp filter query we saw at the end of the SQL Example: Process Images with 3x3 Filters topic, adapted to use the st_peters_grayscale image:
-- prepare begin
CREATE TABLE [Custom] (
[X] INT32,
[Y] INT32,
[mfd_id] INT64,
[Tile] TILE,
INDEX [mfd_id_x] BTREE ([mfd_id]),
INDEX [X_Y_Tile_x] RTREE ([X], [Y], [Tile] TILESIZE (128, 128) TILETYPE FLOAT64),
PROPERTY 'FieldCoordSystem.Tile' ComponentFieldCoordSystem([st_peters_grayscale Tiles], 'Tile'),
PROPERTY 'FieldTileSize.Tile' '[ 128, 128 ]',
PROPERTY 'FieldTileType.Tile' 'float64'
);
CREATE IMAGE [Custom Image] (
PROPERTY 'Table' '[Custom]',
PROPERTY 'FieldX' 'X',
PROPERTY 'FieldY' 'Y',
PROPERTY 'FieldTile' 'Tile',
PROPERTY 'Rect' CAST(ComponentFieldBounds([st_peters_grayscale Tiles], 'Tile', FALSE) AS NVARCHAR)
);
-- prepare end
VALUE @image TABLE = CALL ComponentFieldImage([st_peters_grayscale Tiles], 'Tile');
-- Vertical Edge Detect Single Sobel
-- VALUE @filterDef TILE = StringJsonTile('[ 1, 0, -1, 2, 0, -2, 1, 0, -1 ]', 3, 3, 1, true);
-- Unsharp 5x5
VALUE @filterDef TILE = StringJsonTile('[
-0.00391, -0.01563, -0.02344, -0.01563, -0.00391,
-0.01563, -0.06250, -0.09375, -0.06250, -0.01563,
-0.02344, -0.09375, 1.85938, -0.09375, -0.02344,
-0.01563, -0.06250, -0.09375, -0.06250, -0.01563,
-0.00391, -0.01563, -0.02344, -0.01563, -0.00391
]', 5, 5, 1, true);
VALUE @radius UINT8 = 2;
FUNCTION processtile(@t TILE, @r UINT8, @f TILE) TILE AS (
TileFilter(@t, @r, @f)
) END;
DELETE FROM [Custom];
INSERT INTO [Custom] (
[X], [Y],
[Tile]
) SELECT
[X], [Y],
CASTV ((TileRemoveBorder(processtile(
TileCutBorder(@image, VectorMakeX2([X], [Y]), @radius), @radius, @filterDef
), @radius)) AS FLOAT64)
FROM [st_peters_grayscale Tiles] THREADS SystemCpuCount();
TABLE CALL TileUpdateFieldPyramids([Custom], 'Tile');
ALTER IMAGE [Custom Image] (
ADD PROPERTY 'Rect' Coalesce(
CAST(ComponentFieldBounds([Custom], 'Tile', TRUE) AS NVARCHAR),
ComponentProperty([Custom Image], 'Rect'))
);
It seems that to adapt the query to RGB, all we need do would be to apply the TileFilter function to each of the three channels within the RGB image. To do that, we will use the TileChannel function to extract each of the three channels from the three-channel RGB tile. We will then process the single-channel tiles. Finally, we will put the results tiles back together into a three-channel tile using TileChannelsConcat.
To keep our code more modular, we will introduce a new function, a processRGB function, to host all the salad dressing involved in using TileChannel three times and then concatenating the results with TileChannelsConcat:
FUNCTION processRGB(@t TILE, @r UINT8, @f TILE) TILE AS (
TileChannelsConcat(
processtile(TileChannel(@t,0), @r, @f),
TileChannelsConcat(
processtile(TileChannel(@t,1), @r, @f),
processtile(TileChannel(@t,2), @r, @f)))
) END;
The function breaks out each channel, applies processtile, and then concatenates that result into a three-channel tile.
We can now rewrite our single-channel query for use with RGB images, changing the types used, changing the name of the source image to st_peters, and changing the name of the output image to Custom_RGB
-- prepare begin
CREATE TABLE [Custom_RGB] (
[X] INT32,
[Y] INT32,
[mfd_id] INT64,
[Tile] TILE,
INDEX [mfd_id_x] BTREE ([mfd_id]),
INDEX [X_Y_Tile_x] RTREE ([X], [Y], [Tile] TILESIZE (128, 128) TILETYPE FLOAT64x3),
PROPERTY 'FieldCoordSystem.Tile' ComponentFieldCoordSystem([st_peters Tiles], 'Tile'),
PROPERTY 'FieldTileSize.Tile' '[ 128, 128 ]',
PROPERTY 'FieldTileType.Tile' 'float64x3'
);
CREATE IMAGE [Custom_RGB Image] (
PROPERTY 'Table' '[Custom_RGB]',
PROPERTY 'FieldX' 'X',
PROPERTY 'FieldY' 'Y',
PROPERTY 'FieldTile' 'Tile',
PROPERTY 'Rect' CAST(ComponentFieldBounds([st_peters Tiles], 'Tile', FALSE) AS NVARCHAR)
);
-- prepare end
VALUE @image TABLE = CALL ComponentFieldImage([st_peters Tiles], 'Tile');
-- Vertical Edge Detect Single Sobel
-- VALUE @filterDef TILE = StringJsonTile('[ 1, 0, -1, 2, 0, -2, 1, 0, -1 ]', 3, 3, 1, true);
-- Unsharp 5x5
VALUE @filterDef TILE = StringJsonTile('[
-0.00391, -0.01563, -0.02344, -0.01563, -0.00391,
-0.01563, -0.06250, -0.09375, -0.06250, -0.01563,
-0.02344, -0.09375, 1.85938, -0.09375, -0.02344,
-0.01563, -0.06250, -0.09375, -0.06250, -0.01563,
-0.00391, -0.01563, -0.02344, -0.01563, -0.00391
]', 5, 5, 1, true);
VALUE @radius UINT8 = 2;
FUNCTION processtile(@t TILE, @r UINT8, @f TILE) TILE AS (
TileFilter(@t, @r, @f)
) END;
FUNCTION processRGB(@t TILE, @r UINT8, @f TILE) TILE AS (
TileChannelsConcat(
processtile(TileChannel(@t,0), @r, @f),
TileChannelsConcat(
processtile(TileChannel(@t,1), @r, @f),
processtile(TileChannel(@t,2), @r, @f)))
) END;
DELETE FROM [Custom_RGB];
INSERT INTO [Custom_RGB] (
[X], [Y],
[Tile]
) SELECT
[X], [Y],
CASTV ((TileRemoveBorder(processRGB(
TileCutBorder(@image, VectorMakeX2([X], [Y]), @radius), @radius, @filterDef
), @radius)) AS FLOAT64)
FROM [st_peters Tiles] THREADS SystemCpuCount();
TABLE CALL TileUpdateFieldPyramids([Custom_RGB], 'Tile');
ALTER IMAGE [Custom_RGB Image] (
ADD PROPERTY 'Rect' Coalesce(
CAST(ComponentFieldBounds([Custom_RGB], 'Tile', TRUE) AS NVARCHAR),
ComponentProperty([Custom_RGB Image], 'Rect'))
);
As can be seen above, the modifications (indicated in blue color) other than changing the names of files are remarkably few, thanks to the modularization of code we achieve by using functions. From top to bottom, we make the following changes:
Change the tile type of the results table and image to a three channel float: FLOAT64x3 and float64x3.
Add a new processRGB function., which calls processtile in between doing channel razzle-dazzle.
In the big CASTV expression, use processRGB instead of the former processtile.
That's all. We can run the query to see what effect it has. The first image below shows the results of the 5x5 Unsharp computation. The second image shows the original, zoomed to the same scale, for comparison. Clearly, the 5x5 Unsharp result is sharper.
5x5 Unsharp result, above.
Original image, above.
However, if we look closer, depending on Style settings we can see unexpected pixels of color in the Unsharp image result.
Manifold is reasonably good at guessing range settings to use for images. The Style settings for the image above use channel range for the various channels from 0 to approximately 227.5308 for R, G, and B channels.
However, if we change the range settings to 0 to 255 for all three channels we get unexpected pixels of color:
The artifacts are caused by applying the same filter to three separated channels, with no guarantee that the results will fall within the range from 0 to 255 expected in an RGB image using unsigned, eight bit integers for the R, G, and B pixel values. There are many sophisticated ways of normalizing filter results to fall within a 0 to 255 range, but the usual method is to simply trim the output to a range of 0 to 255. Any values below 0 are set to 0, and any values above 255 are set to 255. Primitive, but effective.
In the first image above, the trimming was done from 0 to approximately 227.5308. But that won't work for software that cannot do such range normalization on the fly for display. We can force clipping to be done by modifying our query. Given our modularlization of processing within functions, we can achieve the desired clipping by a simple re-write of the processtile function. Instead of the original:
FUNCTION processtile(@t TILE, @r UINT8, @f TILE) TILE AS (
TileFilter(@t, @r, @f)
) END;
We simply write:
FUNCTION processtile(@t TILE, @r UINT8, @f TILE) TILE AS (
TileMin(TileMax(TileFilter(@t, @r, @f), 0), 255)
) END;
From our reading of the ever-helpful SQL Functions topic, we know that TileMax in the function above compares the output to 0 and takes the maximum. Zero is always greater than any negative number, so any tile values lower than 0 are replaced by 0. TileMin compares the output to 255 and takes the minimum. 255 is always less than anything greater than 255, so any tile values larger than 255 are replaced by 255. Since we apply the same trimming to all the tiles we process, regardless of channel, we can do the trimming within the processtile function.
Making the above change, our final version of the RGB Unsharp query is:
-- prepare begin
CREATE TABLE [Custom_RGB] (
[X] INT32,
[Y] INT32,
[mfd_id] INT64,
[Tile] TILE,
INDEX [mfd_id_x] BTREE ([mfd_id]),
INDEX [X_Y_Tile_x] RTREE ([X], [Y], [Tile] TILESIZE (128, 128) TILETYPE FLOAT64x3),
PROPERTY 'FieldCoordSystem.Tile' ComponentFieldCoordSystem([st_peters Tiles], 'Tile'),
PROPERTY 'FieldTileSize.Tile' '[ 128, 128 ]',
PROPERTY 'FieldTileType.Tile' 'float64x3'
);
CREATE IMAGE [Custom_RGB Image] (
PROPERTY 'Table' '[Custom_RGB]',
PROPERTY 'FieldX' 'X',
PROPERTY 'FieldY' 'Y',
PROPERTY 'FieldTile' 'Tile',
PROPERTY 'Rect' CAST(ComponentFieldBounds([st_peters Tiles], 'Tile', FALSE) AS NVARCHAR)
);
-- prepare end
VALUE @image TABLE = CALL ComponentFieldImage([st_peters Tiles], 'Tile');
-- Vertical Edge Detect Single Sobel
-- VALUE @filterDef TILE = StringJsonTile('[ 1, 0, -1, 2, 0, -2, 1, 0, -1 ]', 3, 3, 1, true);
-- Unsharp 5x5
VALUE @filterDef TILE = StringJsonTile('[
-0.00391, -0.01563, -0.02344, -0.01563, -0.00391,
-0.01563, -0.06250, -0.09375, -0.06250, -0.01563,
-0.02344, -0.09375, 1.85938, -0.09375, -0.02344,
-0.01563, -0.06250, -0.09375, -0.06250, -0.01563,
-0.00391, -0.01563, -0.02344, -0.01563, -0.00391
]', 5, 5, 1, true);
VALUE @radius UINT8 = 2;
FUNCTION processtile(@t TILE, @r UINT8, @f TILE) TILE AS (
TileMin(TileMax(TileFilter(@t, @r, @f), 0), 255)
) END;
FUNCTION processRGB(@t TILE, @r UINT8, @f TILE) TILE AS (
TileChannelsConcat(
processtile(TileChannel(@t,0), @r, @f),
TileChannelsConcat(
processtile(TileChannel(@t,1), @r, @f),
processtile(TileChannel(@t,2), @r, @f)))
) END;
DELETE FROM [Custom_RGB];
INSERT INTO [Custom_RGB] (
[X], [Y],
[Tile]
) SELECT
[X], [Y],
CASTV ((TileRemoveBorder(processRGB(
TileCutBorder(@image, VectorMakeX2([X], [Y]), @radius), @radius, @filterDef
), @radius)) AS FLOAT64)
FROM [st_peters Tiles] THREADS SystemCpuCount();
TABLE CALL TileUpdateFieldPyramids([Custom_RGB], 'Tile');
ALTER IMAGE [Custom_RGB Image] (
ADD PROPERTY 'Rect' Coalesce(
CAST(ComponentFieldBounds([Custom_RGB], 'Tile', TRUE) AS NVARCHAR),
ComponentProperty([Custom_RGB Image], 'Rect'))
);
Running the query, we see the result is a razor-sharp image with no color artifacts:
Zooming into the dome, we see none of the unexpected colors seen before:
The image above uses the same 0 to 255 range in the Style dialog that produced unexpected colors before we modified the query. Now that we do clipping to a 0 to 255 range within the processtile function, there are no unexpected colors.
The example query now handles RGB images without generating color artifacts. It uses a single 5x5 matrix filter. We can easily adapt it to handle two 3x3 filters for a Prewitt edge detection filter. We make changes very similar to those discussed in the SQL Example: Process Images using Dual 3x3 Filters topic:
-- $manifold$
--
VALUE @source_image TABLE = [st_peters];
VALUE @source_image_rect NVARCHAR = '[ 0, 0, 1214, 862 ]';
-- Prewitt Edge Detect Filter 1
VALUE @filter1 TILE = StringJsonTile('[ -1, 0, 1, -1, 0, 1, -1, 0, 1 ]', 3, 3, 1, true);
-- Prewitt Edge Detect Filter 2
VALUE @filter2 TILE = StringJsonTile('[ -1, -1, -1, 0, 0, 0, 1, 1, 1 ]', 3, 3, 1, true);
VALUE @radius UINT8 = 1;
CREATE TABLE [Custom] (
[X] INT32,
[Y] INT32,
[Tile] TILE,
[mfd_id] INT64,
INDEX [mfd_id_x] BTREE ([mfd_id]),
INDEX [X_Y_Tile_x] RTREE ([X], [Y], [Tile] TILESIZE (128,128) TILETYPE FLOAT64x3),
PROPERTY 'FieldTileSize.Tile' '[ 128, 128 ]',
PROPERTY 'FieldTileType.Tile' 'float64x3'
);
CREATE IMAGE [Custom Image] (
PROPERTY 'Table' '[Custom]',
PROPERTY 'FieldTile' 'Tile',
PROPERTY 'FieldX' 'X',
PROPERTY 'FieldY' 'Y',
PROPERTY 'Rect' @source_image_rect
);
FUNCTION processtile2f(@t TILE, @r UINT8, @f1 TILE, @f2 TILE) TILE AS (
TileMin((TileMax(((TileFilter(@t, @r, @f1)^2 + TileFilter(@t, @r, @f2)^2)^0.5), 0)), 255)
) END;
FUNCTION processRGB2f(@t TILE, @r UINT8, @f1 TILE, @f2 TILE) TILE AS (
TileChannelsConcat(
processtile2f(TileChannel(@t,0), @r, @f1, @f2),
TileChannelsConcat(
processtile2f(TileChannel(@t,1), @r, @f1, @f2),
processtile2f(TileChannel(@t,2), @r, @f1, @f2)))
) END;
PRAGMA ('progress.percentnext' = '100');
INSERT INTO [Custom] (
[X], [Y],
[Tile]
) SELECT
[X], [Y],
CASTV ((TileRemoveBorder(processRGB2f(
TileCutBorder(@source_image, VectorMakeX2([X], [Y]), @radius), @radius, @filter1, @filter2
), @radius)) AS FLOAT64)
FROM @source_image
THREADS SystemCpuCount();
ALTER TABLE [Custom] (
ADD PROPERTY 'FieldCoordSystem.Tile' ComponentCoordSystem(@source_image)
);
TABLE CALL TileUpdatePyramids([Custom Image]);
The changes we make to the query enable us to use two filters:
Use @filter1 and @filter2 parameters to specify the two filters used.
Adapt the processtile function used with two filters to trim results using TileMin and TileMax. We have renamed the function processtile2f to indicate it now uses two filters.
Adapt the processRGB function to use two filters. We have renamed the function processRGB2f to indicate it now uses two filters.
Adapt the big CASTV expression to pass two filters to the processRGB2f function it now uses.
Having made the above changes we can run the query to see what happens:
The tinted edge lines seen above are the usual, expected, otherworldly appearance of Prewitt edge detection filters applied to RGB images. They are not pixel noise artifacts like those which appear if we do not trim the output tile channels to a range of 0 to 255
By changing the name of the source image,, we can apply the filter to different images.
Above is the original image.
Above is the result, using a range of 0 to 255 in the Style pane, of the dual-filter RGB Prewitt query, adjusted for different input name and output name, illustrating the remarkably clean edges extracted by the calculation.
Another example, using an RGB image of a butterfly, above.
As processed by the RGB Prewitt filter, using default range for channel values in Style of 0 to approximately 66.5.
Using a channel range of approximately 0 to 200, which provides a slightly brighter display than 0 to 255 range. Open a larger version of the above image.
The following examples have the original image first, with the RGB Prewitt filtered image adjacent or below, using 0 to 255 ranges.
A military intelligence application.
Preparing an image for vectorization.
Object detection and roads vectorization.
Creating vector drawings of facilities.
Manifold Viewer - How Matrix Filters Work - The easy, simple way to learn how filters work! Watch this action-packed video using Manifold Viewer that illustrates how matrix filters, also known as convolution filters, work. In addition to explaining filters, the video provides a real-life look at simple Manifold techniques for moving objects around in drawings using the Shift transform, and fast and easy use of Selection and tables to quickly put desired values into attributes. Sound technical? Sure, but in a very easy and simple way.
Manifold Viewer - Create Custom GPU Accelerated Filters in Seconds - A technical video using the free Viewer showing how to create your own, fully custom, fully GPU-parallel, convolution matrix filters, including Emboss, Sobel, Prewitt, and Kirsch edge detection and many more, for use in Viewer or Release 9. Modify the spatial SQL examples in the downloadable example project to specify a custom matrix and in seconds your custom filter can do image processing at GPU-parallel speeds. Viewer is read-only, but you can copy and paste the query text for custom filters to and from Notepad or any other text editor. Download the Custom_Filter_Examples.mxb sample project from the Examples page on the Manifold website to try out the video in Viewer or Release 9.
Manifold Viewer - Speed Demo with 1280 GPU Cores - 2 Minutes vs 5 Days - Watch the free Manifold Viewer run CPU parallel and GPU parallel with 8 CPU cores and 1280 GPU cores to absolutely crush a job, doing in 2 minutes what would take non-GPU-parallel software 5 days. The video shows Viewer instantly pop open a 4 GB project that contains a huge, multi-gigabyte terrain elevation surface for Crater Lake, Oregon. With a point and click - no parallel code required - we compute the mean curvature at each pixel of the surface using a 7x7 matrix in under two minutes. We combine that with the original surface for enhanced hill shaded effects to better see details. Using an 11x11 matrix takes just over two minutes, a huge computation that takes days in non-GPU-parallel GIS packages.
SQL Example: Process Images with 3x3 Filters - Shows a step-by-step example of developing an SQL query that takes a query written by the Edit Query button and then modifies that query into a general purpose query that can apply any 3x3 filter. This makes it easy to use matrix filters we find on the web for custom image processing. We extend the query by using parameters and adding a function, and then show how it can be adapted to use a 5x5 filter.
SQL Example: Process Images using Dual 3x3 Filters - A continuation of the above topic, extending the example query to utilize two filters for processing, as commonly done with Sobel and Prewitt two filter processing.
SQL Example: Create NDVI Displays - How to create a query that creates an NDVI display from a four-band NAIP image, with tips and tricks on how to copy and paste existing information to get the result we want.
SQL Example: Create Topographic Position Index TPI Displays - In this example, we use a few short lines of SQL to create a Topographic Position Index (TPI) display. TPI characterizes the undulations of a terrain elevation surface. TPI value above zero show locations that are higher than then average of immediately surrounding terrain, and thus tend to show ridges. TPI values below zero show locations that are lower than the average of immediately surrounding terrain, and thus tend to show valleys. TPI values that are zero show areas of constant slope.
Example: Enhance Terrain with Curvatures - We enhance a terrain showing Crater Lake, Oregon, by using mean curvature calculation to bring out details. The example uses a 4 GB project containing a large terrain elevation surface. Using a point-and-click dialog with no SQL, we apply automatic CPU parallelism and GPU parallelism to absolutely crush a task in two and a half minutes that would take non-parallel software days.
Example: Rearrange Channels using an Expression - We use a simple expression in the Transform pane to rearrange the order of channels within the data.