Installing Google Chrome in Open Suse

I couldn’t install Google Chrome browser by single-click. I found it a little bit difficult to install that browser on Open Suse, that is why I am writing this post.

I hope my post will be useful for most of the people . Smile

First of all LSB (Linux Standard Base Core) package should be installed.

The goal of the LSB is to develop and promote a set of open standards that will increase compatibility among Linux distributions and enable software applications to run on any compliant system even in binary form. In addition, the LSB will help coordinate efforts to recruit software vendors to port and write products for Linux Operating System.

Connect as a root user:

mariam@linux-jg40:~/Downloads> su
Password:

Install that package

linux-jg40:/home/mariam/Downloads# yast2 -i lsb

Download Google Chrome installation package for Open Suse from here.

Choose appropriate radio button: 32bit or 64bit.
Accept the license terms.

After that install Google Chrome.

linux-jg40:/home/mariam/Downloads# yast2 -i google-chrome-stable_current_i386.rpm

Note:

i386 indicates that this package is for 32bit OS
x86_64 —-is for 64bit

If after that Chrome is installed but doesn’t start up then run the following:

linux-jg40:/home/mariam/Downloads# zypper in libpng12-0

You can show the shortcut on the desktop by the following way:

Right click on the Desktop Folder-> choose Create New-> Link to Application.

Go to the Application tab and fill the following textboxes.

Note: In Command field there is written /opt/google/chrome/google-chrome.

Properties for Program.desktop

Click OK.

To change icon for the shortcut : Go to the General tab and click squared box, choose Other icons and clickbrowse, choose icon and click OK.

Happy browsing!

 

Another confusion in IE

It is well-known fact that there are a lot of staff that are implemented illogically in IE. I found another one, cost of which was three successive days of rewriting + debugging and a month of background warring + thinking for me…

I have implemented dynamic loading of .js content in my web application. It had problem in IE for a long time. Once it came time to fix it. I rewrite all the staff that was related to it. Originally it was in a single file. I rewrite it in three files with three different classes (App, Manager, Loader) to make it as simple and as clear as possible with a lot of comments. Loader class is responsible for file downloading and this was the most suspected place. After exploring during three successive days I found the problem. Originally the key method was written like this: (I use ExtJs):

       /* Loads .js file from specified URL */
	Loader.prototype.loadScript = function(src, callBack){
	    var script;
	    !this.head && (this.head = document.getElementsByTagName("head")[0]);
	    script = document.createElement('script');
	    script.type = 'text/javascript';
	    if(Ext.isIE){
	        //listener for IE
	        script.setAttribute('onreadystatechange', function() {
	            if (script.readyState == 'complete'){
	                callBack();
		    }
		});
            }else{
	        //listener for other browsers than IE.
	        script.onload = callBack;
	    }
	    script.setAttribute('src', src);
	    this.head.appendChild(script);
	};

Problem was in bolded line. There are two cases:

  • When the file is not in the cache then onreadystatechange is fired two times: firstly, script.readyState == ‘loading’ and secondly, script.readyState == ‘loaded’. script.readyState never equals to “complete”!!!.
  • When the file is in the cache then onreadystatechange is fired only once and script.readyState == ‘complete’.
  • So, I rewrite if condition like this: (script.readyState == ‘complete’ || script.readyState == ‘loaded’). It is very easy solution but it was quite time-consuming to get to it for me.

    The first thing is to discover that script.onload doesn’t work in IE and you have to use script.onreadystatechange instead and the socond thing is to debug and pray for discovering how onreadystatechange works 😀

Function-based index

There are several types of indices: B-tree, Bitmap, Function-based, IOT.

I will discuss what function-based index is and in what cases is it used.

Let’s start by creating a demo example.

–Create a test table

create table tesTable(
col1 number,
col2 VARCHAR2(50)
);

–Insert some values

insert into testable
values(1,‘Giorgi’);

commit;

insert into testable
values(2,‘Mariami’);

commit;

–Gather table statistics

begin
dbms_stats.gather_table_stats(null,‘TESTABLE’);
end;

–Check if the statistics is OK

select column_name,num_distinct,hidden_column
from dba_tab_cols
where table_name=‘TESTABLE’;

–Output

COLUMN_NAME NUM_DISTINCT HIDDEN_COLUMN
COL1 2 NO
COL2 2 NO

 

Let’s see the explain plan for the following sql statement:

explain plan for select *
from testable
where UPPER(col2)=‘MARIAMI’;

select * from table(dbms_xplan.display);

——————————————————————————
| Id  | Operation   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
——————————————————————————
|   0 | SELECT STATEMENT  |          |     1 |    40 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TESTABLE |     1 |    40 |     2   (0)| 00:00:01 |
——————————————————————————

If we have a big amount of data in this table, for example 1000000 rows what will happen?

Oracle will convert each row value into UPPER case and then compare it to our string. And this will happen 1000000  times Surprised smile

To make faster our query, there exists function-based index.

–Create function-based index

create index testable_func_indx on testable(upper(col2));

–Gather table statistics

begin
dbms_stats.gather_table_stats
(null,‘TESTABLE’);
end;

–Check if the statistics is OK

select column_name,num_distinct,hidden_column
from dba_tab_cols
where table_name=‘TESTABLE’;

–Output

COLUMN_NAME NUM_DISTINCT HIDDEN_COLUMN
COL1 2 NO
COL2 2 NO
SYS_NC00003$ 2 YES

 

As you can see there appeared hidden column by system generated name. Oracle behind the scenes creates a hidden virtual column on the parent table in order to capture the data characteristics of the function so that the CBO can make an accurate determination of the selectivity associated with using the function.

–See explain plan

explain plan for select *
from testable
where upper(col2)=‘MARIAMI’;

select * from table(dbms_xplan.display);

————————————————————————————————–
| Id  | Operation                   | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————————————–
|   0 | SELECT STATEMENT            |                    |     1 |    40 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TESTABLE   |     1 |    40 |     1   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN | TESTABLE_FUNC_INDX |     1 |       |     1   (0)| 00:00:01 |
————————————————————————————————–

You see there is INDEX RANGE SCAN. It will be faster then FULL TABLE SCAN if there is a big amount of data.

If we want to use user-defined function instead of Oracle built-in function, such as UPPER, we should do the following:

create or replace function my_fun(x varchar2) RETURN VARCHAR2
deterministic
is
begin
return upper
(x);
end;

You must indicate that the function is deterministic,which means that for same input values the output will be the same. So you must not use random function or something like that .

explain plan for select *
from testable
where upper(col2)=‘MARIAMI’;

select * from table(dbms_xplan.display);

————————————————————————————————–
| Id  | Operation                   | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————————————–
|   0 | SELECT STATEMENT            |                    |     1 |    40 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TESTABLE   |     1 |    40 |     1   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN | TESTABLE_FUNC_INDX |     1 |       |     1   (0)| 00:00:01 |
————————————————————————————————–

Install SQL Server 2005 Express Edition & Enabling Remote Client Connection

Run the installation. You can download it from here.

Microsoft SQL Server 2005 Setup End User License Agreement

 

Microsoft SQL Server 2005 Setup Installing Prerequisites

Microsoft SQL Server 2005 Setup Installing Prerequisites 2

Microsoft SQL Server 2005 Setup Welcome Server Installation Wizard

Microsoft SQL Server 2005 Setup System Configuration Check

Microsoft SQL Server 2005 Setup Microsoft SQL Server Installation

Microsoft SQL Server 2005 Setup Registration Information

Microsoft SQL Server 2005 Setup Feature Selection

Microsoft SQL Server 2005 Setup Feature Selection 2

Microsoft SQL Server 2005 Setup Instance Name

Microsoft SQL Server 2005 Setup Existing Components

Click Next>

Microsoft SQL Server 2005 Setup Service Account

Microsoft SQL Server 2005 Setup Authentication Mode

Microsoft SQL Server 2005 Setup Collation Settings

Microsoft SQL Server 2005 Setup Configuration Options

It is optional, if you want to send error reports to Microsoft then check them. I will not disturb Microsoft and leave these boxes unchecked SmileSmile

Microsoft SQL Server 2005 Setup Error and Usage Report Settings

Microsoft SQL Server 2005 Setup Ready to Install

Microsoft SQL Server 2005 Setup Progress

Microsoft SQL Server 2005 Setup Completing MS SQL Server Setup

 

Enabling Remote Client Connection

Click Start->All Programs->Microsoft SQL Server 2005->Configuration Tools->SQL Server Surface Area Configuration…Click Surface Area Configuration for Services and Connections

Microsoft SQL Server 2005 Surface Area Configuration

 

Microsoft SQL Server 2005 Connection Settings Change Alert

Click OK, and reload Database Engine service:

Microsoft SQL Server 2005 Surface Area Configuration_Service_Stop

Microsoft SQL Server 2005 Surface Area Configuration_Service_Start

Click OK.

Insert multiple rows with a single INSERT statement

If you want to insert multiple rows in one table with a single INSERT statement do the following:

INSERT ALL INTO testTable(col1,col2,col3) VALUES(1,2,3)
           INTO testTable(col1,col2,col3) VALUES(4,5,6)
           INTO testTable(col1,col2,col3) VALUES(7,8,9)
           INTO testTable(col1,col2,col3) VALUES(10,11,12)
SELECT * FROM DUAL;
COMMIT;

If you want to insert multiple rows in multiple tables with a single insert statement do the following:

INSERT ALL INTO testTable_1(col1,col2,col3) VALUES(1,2,3)
           INTO testTable_2(col1,col2,col3) VALUES(4,5,6)
           INTO testTable_3(col1,col2,col3) VALUES(7,8,9)
           INTO testTable_4(col1,col2,col3) VALUES(10,11,12)
SELECT * FROM DUAL;
COMMIT;

You also are able to use when to insert specific values into tables, so adding some condition:

INSERT FIRST
   WHEN a < 50 THEN
       INTO testTable_1 VALUES(a,b,c)
   WHEN a > 50 AND a < 500 THEN
       INTO testTable_2 VALUES(a,b,c)   
   WHEN a > 500 AND a < 1000 THEN
       INTO testTable_3 VALUES(a,b,c)
   WHEN a > 1000  THEN
       INTO testTable_4 VALUES(a, b, c)
SELECT col1 AS a, col2 AS b, col3 AS c
FROM testTable_5
COMMIT;

FIRST means that if one of the condition is satisfied Oracle will stop checking other conditions. For example, if after selecting first row from the table  testTable_5 the condition
a > 50 AND a < 500 is satisfied, Oracle will insert just into testTable_2 and go again to the SELECT statement for selecting another row, till the end of table.

INSERT statement has another option ALL which will check each WHEN clause doesn’t matter if any condition is already satisfied or not.

Create Database Link in Another Schema

Creating database link has several restrictions. One of them is that you can’t create DB link in another schema, for example the following script is not correct:

create database link hr.remote_db_link
connect to k identified by k
using 'test'


If you run this then database link will be created with the name “hr.remote_db_link”  in your schema not in HR schema.

To solve this, there can be done the following:

–Create procedure from our user in HR which will execute database creation script

 create procedure hr."db_create_link_proc"
 is
 begin
      execute immediate  'create database link remote_db_link
                          connect to k identified by k
                          using ''test'' ';
 end;

–Execute that procedure from our schema

 begin
      hr."db_create_link_proc";
 end;

–Then you can drop this procedure from our schema

drop procedure hr."db_create_link_proc"

That is all.

Block Media Recovery

You can use Block Media Recovery to recover just corrupted blocks not entire datafile. It has several advantages:

1. It decreases Mean Time To Recover(MTTR). As I mentioned above, you need to recover just corrupted blocks and not all the blocks in datafile.

2. Affected datafile stays online. If you do not use Block Media Recovery you must take datafile offline, which means that affected datafile will be unavailable. During BMR just corrupted blocks are not available.

When you are recovering blocks you must know exact address of the block. Each block is recovered individually.

Also database must be in ARCHIVELOG mode and backup of the database must exist.

I have a table called TEST which is saved in TSTTBS tablespace. I have backuped database, then I’ve opened TSTTBS.DBF datafile by UltraEdit and spoiled block which
belongs to the TEST table. You do not need this steps Smile . I did it for to simulate block corruption.

SQL> select * from a;
select * from a
*
ERROR at line 1:
ORA-01578:ORACLE data block corrupted (file # 5, block # 11)
ORA-01110:data file 5:'C:\ORACLE\PRODUCT\10.2.0\ORADATA\TEST\TSTTBS.DBF'

Error shows that block number 12 is corrupted in datafile 5.

Connect target database by RMAN, and run the following:

RMAN> blockrecover datafile 5 block 11;

It should not take time like 00:00:00 or output shouldn’t be something unusual for example for the firs time I had the following output:

Starting blockrecover at 29-MAR-11
using channel ORA_DISK_1

starting media recovery
media recovery complete, elapsed time: 00:00:00

Finished blockrecover at 29-MAR-11

It was because I have had taken a bad backup. It may also happen when RMAN couldn’t find backup files or you have indicated wrong datafile number or block number.

The followings are restrictions and usage notes of BLOCKRECOVER:

  1. The target database must be mounted or open. You do not have to take a datafile offline if you are performing block media recovery on it.
  2. You can only perform complete media recovery of individual blocks. Point-in-time recovery of individual data blocks is not supported.
  3. You can only perform block media recovery on corrupt blocks.
  4. Blocks marked media corrupt are not accessible until recovery completes.
  5. You cannot perform block media recovery when using a backup control file.
  6. You cannot use proxy backups to perform block media recovery. If the only backups that you have are proxy backups, then you can restore them to a nondefault location on disk, which causes RMAN to view the restored files as datafile copies. You can then use the datafile copies for block media recovery.
  7. You must have a full backup of the file containing the corrupt blocks: block media recovery cannot use incremental backups.
  8. If RMAN fails to access a specific archived redo log file needed for block media recovery, it performs restore failover, trying all other backups listed in the RMAN repository that are suitable for use in this operation, and only fails if no suitable backup is available. See
  9. The datafile header block (block 1) cannot be recovered.
  10. You cannot perform block media recovery in NOARCHIVELOG mode.

Let’s check if corrupted block is recovered.

SQL> select * from a;
select * from a
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 5, block # 12)
ORA-01110:data file 5:'C:\ORACLE\PRODUCT\10.2.0\ORADATA\TEST\TSTTBS.DBF'

In our example after recovering block  #11 Oracle raised another error indicating that #12 block is corrupted(When multiple blocks are corrupted error is raised one by one). Let’s repeat above steps:

RMAN> blockrecover datafile 5 block 12;
SQL> select * from a;

VC
------------------
Testing Corruption

Configuring Flashback Database

Connect to your database as sysdba.

1.  Ensure that your database is in ARCHIVELOG mode.

SQL> select log_mode from v$database;

LOG_MODE
————————
NOARCHIVELOG

1.1 My database is not in ARCHIVELOG mode let’s enable it.

SQL> shutdown immediate;

SQL> startup mount;

SQL> alter database archivelog;

SQL> alter database open;

SQL> select log_mode from v$database;

LOG_MODE
——————-
ARCHIVELOG

2. Setting up flash recovery area.

SQL> show parameter db_recovery_file_dest;

NAME                                        TYPE             VALUE
————————————   ———–     ————-
db_recovery_file_dest           string
db_recovery_file_dest_size  big integer 0

First of all, db_recovery_file_dest_size parameter must be set.

SQL> alter system set db_recovery_file_dest_size=2G;

Then db_recovery_file_dest parameter.

SQL> alter system set db_recovery_file_dest=’D:\TEST\FLASHRECOVERY’;

3. Setting up retention period.

Because of flash recovery area is used in circular fashion, after some period of time(indicates db_flashback_retention_target,default is 1day) old data is overwritten. This parameter instructs Oracle to save flashback files for a certain minutes before overwriting.

SQL> alter system set db_flashback_retention_target=720;

In our case flashback data will be retained for 12 hours before overwriting.

4. Enable flashback logging.

SQL> shutdown immediate;

SQL> startup mount;

SQL> alter database flashback on;

At this time RVWR process will be started and flashback buffer will be allocated in SGA.

SQL> alter database open;

5. Check if flashbacking is enabled.

SQL> select flashback_on from v$database;

FLASHBACK_ON
———————–
YES

SQL Profile has higher priority than Hint!

Hints on SQL statement do not work???
You may have a syntax error, double check it. But if the syntax is OK, then this problem may be caused by the SQL profile, which doesn’t let hints to work.

Here, in this post I will explain the situation when there is applied SQL profile for some SQL statement and any attempt to use hints on that statement fails.

To solve this problem you should drop that SQL profile or disable it.

Note: This behavior has advantage. It assures that the SQL plan of the SQL statement will not be changed for future runs.

Let’s start…

–Create test table

create table test (n1 number
, n2 number );

–Insert some values

begin
for i in 1 .. 100000
loop
insert into test values(i,i+1);
commit;
end loop;
end;

–Create index

create index test_idx1 on test(n1);

–Analyze table

analyze table test estimate statistics;

–Run the following SQL statement

select /*+ no_index(test test_idx1) */ *
from test where n1=2;

–Calculate explain plan

explain plan for select /*+ no_index(test test_idx1) */ *
from test where n1=2;

select * from table(dbms_xplan.display);

–Output

————————————————————————-
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————–
|   0 | SELECT STATEMENT  |      |     1 |     8 |    49   (5)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST |     1 |     8 |    49   (5)| 00:00:01 |
————————————————————————–

–Create SQL tuning task

declare
my_task_name varchar2(30);
my_sqltext   clob;
begin
my_sqltext := ‘select /*+ no_index(test test_idx1) */ * from test where n1=2’;

my_task_name := dbms_sqltune.create_tuning_task(
sql_text    => my_sqltext,
scope       => ‘comprehensive’,
time_limit  => 60,
task_name   => ‘my_sql_tuning_task’);
end;

–Execute SQL tuning task

begin
dbms_sqltune.execute_tuning_task( task_name => ‘my_sql_tuning_task’);
end;

–Generate report

select dbms_sqltune.report_tuning_task( ‘my_sql_tuning_task’)
from dual;

–Output

Recommendation (estimated benefit: 95.87%)
——————————————
– Consider accepting the recommended SQL profile.
execute dbms_sqltune.accept_sql_profile(task_name =>
‘my_sql_tuning_task’, replace => TRUE);

—————————————————————————————–
| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————————–
|   0 | SELECT STATEMENT            |           |     1 |     8 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST      |     1 |     8 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | TEST_IDX1 |     1 |       |     1   (0)| 00:00:01 |
—————————————————————————————–

–Accept SQL profile

begin
dbms_sqltune.accept_sql_profile(task_name =>’my_sql_tuning_task’
,replace => true
,name => ‘my_profile_name’);
end;

–Calculate explain plan again

explain plan for select /*+ no_index(test test_idx1) */ *
from test where n1=2;

select * from table(dbms_xplan.display);

–Output

—————————————————————————————–
| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————————–
|   0 | SELECT STATEMENT            |           |     1 |     8 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST      |     1 |     8 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | TEST_IDX1 |     1 |       |     1   (0)| 00:00:01 |
—————————————————————————————–

Note: We have indicated not to use index(/*+ NO_INDEX(TEST TEST_IDX1) */ ), but as explain plan shows, Oracle still uses this index.

If we drop sql profile hint will work. Let’s check it:

begin
dbms_sqltune.drop_sql_profile(name => ‘my_profile_name’);
end;

–Calculate explain plan

explain plan for select /*+ no_index(test test_idx1) */ *
from test where n1=2;

select * from table(dbms_xplan.display);

–Output

————————————————————————–
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————–
|   0 | SELECT STATEMENT  |      |     1 |     8 |    49   (5)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST |     1 |     8 |    49   (5)| 00:00:01 |
————————————————————————–

So SQL profile has higher priority than hints!!!<- Important

Digital Image Processing using Matlab

Here, in this post I will write a code which will find the area in the picture where the sentence “Digital Image Processing” is written and outlines it.

So the result should be the picture where the sentence “Digital Image Processing” is highlighted.

 

%Reading image
I = imread(‘img1.jpg’);

%Show the original image
figure; imshow(I); title(‘Original Image’);

% Add some text on the right side of the picture, if you want to place this code in one
%line, remove dots.

text(size(I,2),size(I,1)+15,‘Edited by Mariam Kupatadze’,…
‘FontSize’,…
7,…
‘HorizontalAlignment’,…
‘right’);

Digital_Image_Processing_Original_Image

%Detect edges using sobel method
Isobel = edge(I,‘sobel’);

%Show image
figure; imshow(Isobel); title(‘Edge Detected Image’);

text(size(I,2),size(I,1)+15,‘Edited by Mariam Kupatadze’,…
‘FontSize’,…
7,…
‘HorizontalAlignment’,…
‘right’);

Digital_Image_Processing_Edge_Detected_Image

%Remove objects which are connected to the border
Inobord = imclearborder(Isobel);

%Show Image
figure; imshow(Inobord); title(‘Border Cleared Image 1’);

text(size(I,2),size(I,1)+15,‘Edited by Mariam Kupatadze’,…
‘FontSize’,…
7,…
‘HorizontalAlignment’,…
‘right’);

Digital_Image_Processing_Bordered_Cleared_Image_1

%Creates a disk-shaped structuring element by radius=15
StructEl1 = strel(‘disk’,15);

%Creates a linear structuring elements
StructEl2 = strel(‘line’, 3, 90);
StructEl3 = strel(‘line’, 3, 0);

%Remove the linear gaps from the picture
Idil = imdilate(Inobord, [StructEl2 StructEl3]);

%Because of, there left extra objects, I will dilate it by disk-shaped structuring element for to %make them connect to the border.
Idil = imdilate(Idil, StructEl1);

%Show image
figure; imshow(Idil); title(‘Dilated Image’);

text(size(I,2),size(I,1)+15,‘Edited by Mariam Kupatadze’,…
‘FontSize’,…
7,…
‘HorizontalAlignment’,…
‘right’);

Digital_Image_Processing_Dilated_Image

%Again remove objects which are connected to the border
Inobord1 = imclearborder(Idil);

%Show image
figure; imshow(Inobord1); title(‘Border Cleared Image 2’);

text(size(I,2),size(I,1)+15,‘Edited by Mariam Kupatadze’,…
‘FontSize’,…
7,…
‘HorizontalAlignment’,…
‘right’);

Digital_Image_Processing_Border_Cleared_Image_2

%Fill existing holes in the picture
Ifilled = imfill(Inobord1, ‘holes’);

%Show image
figure; imshow(Ifilled); title(‘Holes Filled Image’);

text(size(I,2),size(I,1)+15,‘Edited by Mariam Kupatadze’,…
‘FontSize’,…
7,…
‘HorizontalAlignment’,…
‘right’);

Digital_Image_Processing_Holes_Filled_Image

%Find perimeter of “Ifilled” image, with default connectivity 4
Ioutlined = bwperim(Ifilled);%The same as bwperim(Ifilled,4);
IOut = I;

%Highlight the desired area
IOut(Ioutlined) = 255;

%Show image
figure; imshow(IOut); title(‘Sentence Highlighted Image’);

text(size(I,2),size(I,1)+15,‘Edited by Mariam Kupatadze’,…
‘FontSize’,…7,…
‘HorizontalAlignment’,…
‘right’);

Digital_Image_Processing_Sentence_Highlighted_Image